Merge commit 'd822110d3b
' into clippyup
This commit is contained in:
parent
58100c014a
commit
d05e2865a0
237 changed files with 4245 additions and 1914 deletions
|
@ -4188,6 +4188,7 @@ Released 2018-09-13
|
||||||
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
|
[`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
|
[`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
|
[`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
|
[`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_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
|
[`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_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_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_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_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_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_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
|
||||||
|
|
|
@ -197,8 +197,8 @@ disallowed-names = ["toto", "tata", "titi"]
|
||||||
cognitive-complexity-threshold = 30
|
cognitive-complexity-threshold = 30
|
||||||
```
|
```
|
||||||
|
|
||||||
See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
|
See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
|
||||||
lints can be configured and the meaning of the variables.
|
the lint descriptions contain the names and meanings of these configuration variables.
|
||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
>
|
>
|
||||||
|
@ -224,7 +224,7 @@ in the `Cargo.toml` can be used.
|
||||||
rust-version = "1.30"
|
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
|
```rust
|
||||||
#![feature(custom_inner_attributes)]
|
#![feature(custom_inner_attributes)]
|
||||||
|
|
|
@ -21,3 +21,4 @@
|
||||||
- [The Clippy Book](development/infrastructure/book.md)
|
- [The Clippy Book](development/infrastructure/book.md)
|
||||||
- [Proposals](development/proposals/README.md)
|
- [Proposals](development/proposals/README.md)
|
||||||
- [Roadmap 2021](development/proposals/roadmap-2021.md)
|
- [Roadmap 2021](development/proposals/roadmap-2021.md)
|
||||||
|
- [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md)
|
||||||
|
|
|
@ -11,8 +11,8 @@ disallowed-names = ["toto", "tata", "titi"]
|
||||||
cognitive-complexity-threshold = 30
|
cognitive-complexity-threshold = 30
|
||||||
```
|
```
|
||||||
|
|
||||||
See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
|
See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
|
||||||
lints can be configured and the meaning of the variables.
|
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`
|
To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS`
|
||||||
environment variable.
|
environment variable.
|
||||||
|
@ -72,7 +72,7 @@ minimum supported Rust version (MSRV) in the clippy configuration file.
|
||||||
msrv = "1.30.0"
|
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
|
```rust
|
||||||
#![feature(custom_inner_attributes)]
|
#![feature(custom_inner_attributes)]
|
||||||
|
|
|
@ -443,27 +443,27 @@ value is passed to the constructor in `clippy_lints/lib.rs`.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub struct ManualStrip {
|
pub struct ManualStrip {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManualStrip {
|
impl ManualStrip {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The project's MSRV can then be matched against the feature MSRV in the LintPass
|
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
|
``` rust
|
||||||
if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
|
if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
|
||||||
return;
|
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
|
the value from `clippy.toml`. This can be accounted for using the
|
||||||
`extract_msrv_attr!(LintContext)` macro and passing
|
`extract_msrv_attr!(LintContext)` macro and passing
|
||||||
`LateContext`/`EarlyContext`.
|
`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.
|
for the MSRV version itself.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#![feature(custom_inner_attributes)]
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
#[clippy::msrv = "1.44"]
|
||||||
fn msrv_1_44() {
|
fn msrv_1_44() {
|
||||||
#![clippy::msrv = "1.44"]
|
|
||||||
|
|
||||||
/* something that would trigger the lint */
|
/* something that would trigger the lint */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[clippy::msrv = "1.45"]
|
||||||
fn msrv_1_45() {
|
fn msrv_1_45() {
|
||||||
#![clippy::msrv = "1.45"]
|
|
||||||
|
|
||||||
/* something that would trigger the lint */
|
/* something that would trigger the lint */
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
986
book/src/development/proposals/syntax-tree-patterns.md
Normal file
986
book/src/development/proposals/syntax-tree-patterns.md
Normal file
|
@ -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 (`<node-name>(<args>)`)
|
||||||
|
|
||||||
|
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 (`<lit>`)
|
||||||
|
|
||||||
|
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 (`<a> <b>`)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pattern!{
|
||||||
|
// matches the array [true, false]
|
||||||
|
my_pattern: Expr =
|
||||||
|
Array( Lit(Bool(true)) Lit(Bool(false)) )
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Repetition (`<a>*`, `<a>+`, `<a>?`, `<a>{n}`, `<a>{n,m}`, `<a>{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 (`<a>#<name>`)
|
||||||
|
|
||||||
|
```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-name>(<args>)` | Node | `Lit(Bool(true))`, `If(_, _, _)` |
|
||||||
|
|`<lit>` | Literal | `'x'`, `false`, `101` |
|
||||||
|
|`<a> \| <b>` | Alternation | `Char(_) \| Bool(_)` |
|
||||||
|
|`()` | Empty | `Array( () )` |
|
||||||
|
|`<a> <b>` | Sequence | `Tuple( Lit(Bool(_)) Lit(Int(_)) Lit(_) )` |
|
||||||
|
|`<a>*` <br> `<a>+` <br> `<a>?` <br> `<a>{n}` <br> `<a>{n,m}` <br> `<a>{n,}` | Repetition <br> <br> <br> <br> <br><br> | `Array( _* )`, <br> `Block( Semi(_)+ )`, <br> `If(_, _, Block(_)?)`, <br> `Array( Lit(_){10} )`, <br> `Lit(_){5,10}`, <br> `Lit(Bool(_)){10,}` |
|
||||||
|
|`<a>#<name>` | 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<Lit>),
|
||||||
|
Array(Seq<Expr>),
|
||||||
|
Block_(Alt<BlockType>),
|
||||||
|
If(Alt<Expr>, Alt<BlockType>, Opt<Expr>),
|
||||||
|
IfLet(
|
||||||
|
Alt<BlockType>,
|
||||||
|
Opt<Expr>,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Lit {
|
||||||
|
Char(Alt<char>),
|
||||||
|
Bool(Alt<bool>),
|
||||||
|
Int(Alt<u128>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Stmt {
|
||||||
|
Expr(Alt<Expr>),
|
||||||
|
Semi(Alt<Expr>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum BlockType {
|
||||||
|
Block(Seq<Stmt>),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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<T> {
|
||||||
|
Any,
|
||||||
|
Elmt(Box<T>),
|
||||||
|
Alt(Box<Self>, Box<Self>),
|
||||||
|
Named(Box<Self>, ...)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Opt<T> {
|
||||||
|
Any, // anything, but not None
|
||||||
|
Elmt(Box<T>),
|
||||||
|
None,
|
||||||
|
Alt(Box<Self>, Box<Self>),
|
||||||
|
Named(Box<Self>, ...)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Seq<T> {
|
||||||
|
Any,
|
||||||
|
Empty,
|
||||||
|
Elmt(Box<T>),
|
||||||
|
Repeat(Box<Self>, RepeatRange),
|
||||||
|
Seq(Box<Self>, Box<Self>),
|
||||||
|
Alt(Box<Self>, Box<Self>),
|
||||||
|
Named(Box<Self>, ...)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RepeatRange {
|
||||||
|
pub start: usize,
|
||||||
|
pub end: Option<usize> // 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<Lit>` and therefore doesn't support repetition (`*`). As
|
||||||
|
another example, `Array( Lit(_)* )` is a valid pattern because the parameter of
|
||||||
|
`Array` is of type `Seq<Expr>` 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<O> {
|
||||||
|
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<ast::LitKind> 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>) -> 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.
|
|
@ -120,7 +120,7 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
|
||||||
|
|
||||||
let new_lint = if enable_msrv {
|
let new_lint = if enable_msrv {
|
||||||
format!(
|
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,
|
lint_pass = lint.pass,
|
||||||
ctor_arg = if lint.pass == "late" { "_" } else { "" },
|
ctor_arg = if lint.pass == "late" { "_" } else { "" },
|
||||||
module_name = lint.name,
|
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 {
|
result.push_str(&if enable_msrv {
|
||||||
formatdoc!(
|
formatdoc!(
|
||||||
r#"
|
r#"
|
||||||
use clippy_utils::msrvs;
|
use clippy_utils::msrvs::{{self, Msrv}};
|
||||||
{pass_import}
|
{pass_import}
|
||||||
use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
|
use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{{declare_tool_lint, impl_lint_pass}};
|
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!(
|
formatdoc!(
|
||||||
r#"
|
r#"
|
||||||
pub struct {name_camel} {{
|
pub struct {name_camel} {{
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
impl {name_camel} {{
|
impl {name_camel} {{
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {{
|
pub fn new(msrv: Msrv) -> Self {{
|
||||||
Self {{ msrv }}
|
Self {{ msrv }}
|
||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
|
@ -357,15 +356,14 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
|
||||||
let _ = writedoc!(
|
let _ = writedoc!(
|
||||||
lint_file_contents,
|
lint_file_contents,
|
||||||
r#"
|
r#"
|
||||||
use clippy_utils::{{meets_msrv, msrvs}};
|
use clippy_utils::msrvs::{{self, Msrv}};
|
||||||
use rustc_lint::{{{context_import}, LintContext}};
|
use rustc_lint::{{{context_import}, LintContext}};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
|
|
||||||
use super::{name_upper};
|
use super::{name_upper};
|
||||||
|
|
||||||
// TODO: Adjust the parameters as necessary
|
// TODO: Adjust the parameters as necessary
|
||||||
pub(super) fn check(cx: &{context_import}, msrv: Option<RustcVersion>) {{
|
pub(super) fn check(cx: &{context_import}, msrv: &Msrv) {{
|
||||||
if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
|
if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
|
||||||
return;
|
return;
|
||||||
}}
|
}}
|
||||||
todo!();
|
todo!();
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
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::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_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
@ -33,10 +32,10 @@ declare_clippy_lint! {
|
||||||
impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]);
|
impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]);
|
||||||
|
|
||||||
pub struct AlmostCompleteLetterRange {
|
pub struct AlmostCompleteLetterRange {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
impl AlmostCompleteLetterRange {
|
impl AlmostCompleteLetterRange {
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +45,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange {
|
||||||
let ctxt = e.span.ctxt();
|
let ctxt = e.span.ctxt();
|
||||||
let sugg = if let Some(start) = walk_span_to_context(start.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)
|
&& 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)), "..="))
|
Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
|
||||||
} else {
|
} else {
|
||||||
|
@ -60,7 +59,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange {
|
||||||
if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
|
if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
|
||||||
&& matches!(kind.node, RangeEnd::Excluded)
|
&& 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 {
|
} else {
|
||||||
"..."
|
"..."
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
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_ast::ast::{FloatTy, LitFloatType, LitKind};
|
||||||
use rustc_hir::{Expr, ExprKind};
|
use rustc_hir::{Expr, ExprKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
@ -63,12 +63,12 @@ const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 19] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
pub struct ApproxConstant {
|
pub struct ApproxConstant {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApproxConstant {
|
impl ApproxConstant {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ impl ApproxConstant {
|
||||||
let s = s.as_str();
|
let s = s.as_str();
|
||||||
if s.parse::<f64>().is_ok() {
|
if s.parse::<f64>().is_ok() {
|
||||||
for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
|
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(
|
span_lint_and_help(
|
||||||
cx,
|
cx,
|
||||||
APPROX_CONSTANT,
|
APPROX_CONSTANT,
|
||||||
|
|
|
@ -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::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::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::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 if_chain::if_chain;
|
||||||
use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
|
use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -14,7 +13,6 @@ use rustc_hir::{
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
|
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::source_map::Span;
|
use rustc_span::source_map::Span;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
|
@ -599,7 +597,7 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EarlyAttributes {
|
pub struct EarlyAttributes {
|
||||||
pub msrv: Option<RustcVersion>,
|
pub msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_lint_pass!(EarlyAttributes => [
|
impl_lint_pass!(EarlyAttributes => [
|
||||||
|
@ -614,7 +612,7 @@ impl EarlyLintPass for EarlyAttributes {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
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);
|
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<RustcVersion>) {
|
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES);
|
if msrv.meets(msrvs::TOOL_ATTRIBUTES);
|
||||||
// check cfg_attr
|
// check cfg_attr
|
||||||
if attr.has_name(sym::cfg_attr);
|
if attr.has_name(sym::cfg_attr);
|
||||||
if let Some(items) = attr.meta_item_list();
|
if let Some(items) = attr.meta_item_list();
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::sugg::Sugg;
|
use clippy_utils::sugg::Sugg;
|
||||||
use clippy_utils::{meets_msrv, msrvs};
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, ExprKind};
|
use rustc_hir::{Expr, ExprKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
|
|
||||||
use super::CAST_ABS_TO_UNSIGNED;
|
use super::CAST_ABS_TO_UNSIGNED;
|
||||||
|
|
||||||
|
@ -15,9 +14,9 @@ pub(super) fn check(
|
||||||
cast_expr: &Expr<'_>,
|
cast_expr: &Expr<'_>,
|
||||||
cast_from: Ty<'_>,
|
cast_from: Ty<'_>,
|
||||||
cast_to: Ty<'_>,
|
cast_to: Ty<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
if meets_msrv(msrv, msrvs::UNSIGNED_ABS)
|
if msrv.meets(msrvs::UNSIGNED_ABS)
|
||||||
&& let ty::Int(from) = cast_from.kind()
|
&& let ty::Int(from) = cast_from.kind()
|
||||||
&& let ty::Uint(to) = cast_to.kind()
|
&& let ty::Uint(to) = cast_to.kind()
|
||||||
&& let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind
|
&& let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
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::source::snippet_opt;
|
||||||
use clippy_utils::ty::is_isize_or_usize;
|
use clippy_utils::ty::is_isize_or_usize;
|
||||||
use clippy_utils::{in_constant, meets_msrv, msrvs};
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, ExprKind};
|
use rustc_hir::{Expr, ExprKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::{self, FloatTy, Ty};
|
use rustc_middle::ty::{self, FloatTy, Ty};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
|
|
||||||
use super::{utils, CAST_LOSSLESS};
|
use super::{utils, CAST_LOSSLESS};
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ pub(super) fn check(
|
||||||
cast_op: &Expr<'_>,
|
cast_op: &Expr<'_>,
|
||||||
cast_from: Ty<'_>,
|
cast_from: Ty<'_>,
|
||||||
cast_to: Ty<'_>,
|
cast_to: Ty<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
if !should_lint(cx, expr, cast_from, cast_to, msrv) {
|
if !should_lint(cx, expr, cast_from, cast_to, msrv) {
|
||||||
return;
|
return;
|
||||||
|
@ -57,13 +57,7 @@ pub(super) fn check(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_lint(
|
fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool {
|
||||||
cx: &LateContext<'_>,
|
|
||||||
expr: &Expr<'_>,
|
|
||||||
cast_from: Ty<'_>,
|
|
||||||
cast_to: Ty<'_>,
|
|
||||||
msrv: Option<RustcVersion>,
|
|
||||||
) -> bool {
|
|
||||||
// Do not suggest using From in consts/statics until it is valid to do so (see #2267).
|
// Do not suggest using From in consts/statics until it is valid to do so (see #2267).
|
||||||
if in_constant(cx, expr.hir_id) {
|
if in_constant(cx, expr.hir_id) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -89,7 +83,7 @@ fn should_lint(
|
||||||
};
|
};
|
||||||
!is_isize_or_usize(cast_from) && from_nbits < to_nbits
|
!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))
|
matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
|
||||||
},
|
},
|
||||||
|
|
|
@ -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 to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
|
||||||
|
|
||||||
let cast_from_ptr_size = def.repr().int.map_or(true, |ty| {
|
let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),));
|
||||||
matches!(
|
|
||||||
ty,
|
|
||||||
IntegerType::Pointer(_),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
|
let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
|
||||||
(false, false) if from_nbits > to_nbits => "",
|
(false, false) if from_nbits > to_nbits => "",
|
||||||
(true, false) if from_nbits > to_nbits => "",
|
(true, false) if from_nbits > to_nbits => "",
|
||||||
|
|
|
@ -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 if_chain::if_chain;
|
||||||
use rustc_ast::Mutability;
|
use rustc_ast::Mutability;
|
||||||
use rustc_hir::{Expr, ExprKind, Node};
|
use rustc_hir::{Expr, ExprKind, Node};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut};
|
use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
|
|
||||||
use super::CAST_SLICE_DIFFERENT_SIZES;
|
use super::CAST_SLICE_DIFFERENT_SIZES;
|
||||||
|
|
||||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option<RustcVersion>) {
|
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
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
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 if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{def_id::DefId, Expr, ExprKind};
|
use rustc_hir::{def_id::DefId, Expr, ExprKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
|
|
||||||
use super::CAST_SLICE_FROM_RAW_PARTS;
|
use super::CAST_SLICE_FROM_RAW_PARTS;
|
||||||
|
|
||||||
|
@ -25,15 +25,9 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn check(
|
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) {
|
||||||
cx: &LateContext<'_>,
|
|
||||||
expr: &Expr<'_>,
|
|
||||||
cast_expr: &Expr<'_>,
|
|
||||||
cast_to: Ty<'_>,
|
|
||||||
msrv: Option<RustcVersion>,
|
|
||||||
) {
|
|
||||||
if_chain! {
|
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::RawPtr(ptrty) = cast_to.kind();
|
||||||
if let ty::Slice(_) = ptrty.ty.kind();
|
if let ty::Slice(_) = ptrty.ty.kind();
|
||||||
if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind;
|
if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind;
|
||||||
|
|
|
@ -21,11 +21,11 @@ mod ptr_as_ptr;
|
||||||
mod unnecessary_cast;
|
mod unnecessary_cast;
|
||||||
mod utils;
|
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_hir::{Expr, ExprKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -648,12 +648,12 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Casts {
|
pub struct Casts {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Casts {
|
impl Casts {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -686,7 +686,7 @@ impl_lint_pass!(Casts => [
|
||||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
if !in_external_macro(cx.sess(), expr.span) {
|
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() {
|
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) {
|
if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
|
||||||
return;
|
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);
|
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_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::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_possible_wrap::check(cx, expr, cast_from, cast_to);
|
||||||
cast_precision_loss::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_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_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);
|
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
|
||||||
}
|
}
|
||||||
|
|
||||||
as_underscore::check(cx, expr, cast_to_hir);
|
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);
|
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_ref_to_mut::check(cx, expr);
|
||||||
cast_ptr_alignment::check(cx, expr);
|
cast_ptr_alignment::check(cx, expr);
|
||||||
char_lit_as_u8::check(cx, expr);
|
char_lit_as_u8::check(cx, expr);
|
||||||
ptr_as_ptr::check(cx, expr, self.msrv);
|
ptr_as_ptr::check(cx, expr, &self.msrv);
|
||||||
cast_slice_different_sizes::check(cx, expr, self.msrv);
|
cast_slice_different_sizes::check(cx, expr, &self.msrv);
|
||||||
}
|
}
|
||||||
|
|
||||||
extract_msrv_attr!(LateContext);
|
extract_msrv_attr!(LateContext);
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::sugg::Sugg;
|
use clippy_utils::sugg::Sugg;
|
||||||
use clippy_utils::{meets_msrv, msrvs};
|
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
|
use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::{self, TypeAndMut};
|
use rustc_middle::ty::{self, TypeAndMut};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
|
|
||||||
use super::PTR_AS_PTR;
|
use super::PTR_AS_PTR;
|
||||||
|
|
||||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVersion>) {
|
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
|
||||||
if !meets_msrv(msrv, msrvs::POINTER_CAST) {
|
if !msrv.meets(msrvs::POINTER_CAST) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::get_parent_expr;
|
|
||||||
use clippy_utils::numeric_literal::NumericLiteral;
|
use clippy_utils::numeric_literal::NumericLiteral;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
|
use clippy_utils::{get_parent_expr, path_to_local};
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_ast::{LitFloatType, LitIntType, LitKind};
|
use rustc_ast::{LitFloatType, LitIntType, LitKind};
|
||||||
use rustc_errors::Applicability;
|
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 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(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
UNNECESSARY_CAST,
|
UNNECESSARY_CAST,
|
||||||
expr.span,
|
expr.span,
|
||||||
&format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
|
&format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
|
||||||
"try",
|
"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,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
//! lint on manually implemented checked conversions that could be transformed into `try_from`
|
//! lint on manually implemented checked conversions that could be transformed into `try_from`
|
||||||
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
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 if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
|
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -37,12 +37,12 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CheckedConversions {
|
pub struct CheckedConversions {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CheckedConversions {
|
impl CheckedConversions {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
|
||||||
// Prevent triggering on `if c { if let a = b { .. } }`.
|
// Prevent triggering on `if c { if let a = b { .. } }`.
|
||||||
if !matches!(check_inner.kind, ast::ExprKind::Let(..));
|
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 {
|
then {
|
||||||
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
|
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
|
||||||
let lhs = Sugg::ast(cx, check, "..");
|
let mut app = Applicability::MachineApplicable;
|
||||||
let rhs = Sugg::ast(cx, check_inner, "..");
|
let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app);
|
||||||
|
let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app);
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
expr.span,
|
expr.span,
|
||||||
"collapse nested if block",
|
"collapse nested if block",
|
||||||
|
@ -173,7 +175,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
|
||||||
lhs.and(&rhs),
|
lhs.and(&rhs),
|
||||||
snippet_block(cx, content.span, "..", Some(expr.span)),
|
snippet_block(cx, content.span, "..", Some(expr.span)),
|
||||||
),
|
),
|
||||||
Applicability::MachineApplicable, // snippet
|
app, // snippet
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,6 +177,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
|
crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
|
||||||
crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
|
crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
|
||||||
crate::functions::DOUBLE_MUST_USE_INFO,
|
crate::functions::DOUBLE_MUST_USE_INFO,
|
||||||
|
crate::functions::MISNAMED_GETTERS_INFO,
|
||||||
crate::functions::MUST_USE_CANDIDATE_INFO,
|
crate::functions::MUST_USE_CANDIDATE_INFO,
|
||||||
crate::functions::MUST_USE_UNIT_INFO,
|
crate::functions::MUST_USE_UNIT_INFO,
|
||||||
crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_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::TYPE_COMPLEXITY_INFO,
|
||||||
crate::types::VEC_BOX_INFO,
|
crate::types::VEC_BOX_INFO,
|
||||||
crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO,
|
crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO,
|
||||||
|
crate::undocumented_unsafe_blocks::UNNECESSARY_SAFETY_COMMENT_INFO,
|
||||||
crate::unicode::INVISIBLE_CHARACTERS_INFO,
|
crate::unicode::INVISIBLE_CHARACTERS_INFO,
|
||||||
crate::unicode::NON_ASCII_LITERAL_INFO,
|
crate::unicode::NON_ASCII_LITERAL_INFO,
|
||||||
crate::unicode::UNICODE_NOT_NFC_INFO,
|
crate::unicode::UNICODE_NOT_NFC_INFO,
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
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::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::source::{snippet_with_applicability, snippet_with_context};
|
||||||
use clippy_utils::sugg::has_enclosing_paren;
|
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::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
|
||||||
use clippy_utils::{
|
use clippy_utils::{
|
||||||
fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
|
fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage,
|
||||||
walk_to_expr_usage,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
|
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
|
||||||
use rustc_data_structures::fx::FxIndexMap;
|
use rustc_data_structures::fx::FxIndexMap;
|
||||||
use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch};
|
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,
|
self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
|
||||||
ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
|
ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
|
||||||
};
|
};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::{symbol::sym, Span, Symbol};
|
use rustc_span::{symbol::sym, Span, Symbol};
|
||||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||||
|
@ -181,12 +181,12 @@ pub struct Dereferencing<'tcx> {
|
||||||
possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||||
|
|
||||||
// `IntoIterator` for arrays requires Rust 1.53.
|
// `IntoIterator` for arrays requires Rust 1.53.
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Dereferencing<'tcx> {
|
impl<'tcx> Dereferencing<'tcx> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self {
|
Self {
|
||||||
msrv,
|
msrv,
|
||||||
..Dereferencing::default()
|
..Dereferencing::default()
|
||||||
|
@ -286,26 +286,27 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
||||||
match (self.state.take(), kind) {
|
match (self.state.take(), kind) {
|
||||||
(None, kind) => {
|
(None, kind) => {
|
||||||
let expr_ty = typeck.expr_ty(expr);
|
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 {
|
match kind {
|
||||||
RefOp::Deref => {
|
RefOp::Deref => {
|
||||||
|
let sub_ty = typeck.expr_ty(sub_expr);
|
||||||
if let Position::FieldAccess {
|
if let Position::FieldAccess {
|
||||||
name,
|
name,
|
||||||
of_union: false,
|
of_union: false,
|
||||||
} = position
|
} = position
|
||||||
&& !ty_contains_field(typeck.expr_ty(sub_expr), name)
|
&& !ty_contains_field(sub_ty, name)
|
||||||
{
|
{
|
||||||
self.state = Some((
|
self.state = Some((
|
||||||
State::ExplicitDerefField { name },
|
State::ExplicitDerefField { name },
|
||||||
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() {
|
} else if position.is_deref_stable() && sub_ty.is_ref() {
|
||||||
self.state = Some((
|
self.state = Some((
|
||||||
State::ExplicitDeref { mutability: None },
|
State::ExplicitDeref { mutability: None },
|
||||||
StateData { span: expr.span, hir_id: expr.hir_id, position },
|
StateData { span: expr.span, hir_id: expr.hir_id, position },
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
RefOp::Method(target_mut)
|
RefOp::Method(target_mut)
|
||||||
if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
|
if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
|
||||||
&& position.lint_explicit_deref() =>
|
&& position.lint_explicit_deref() =>
|
||||||
|
@ -320,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
||||||
StateData {
|
StateData {
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
hir_id: expr.hir_id,
|
hir_id: expr.hir_id,
|
||||||
position
|
position,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
|
@ -394,7 +395,11 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
||||||
msg,
|
msg,
|
||||||
snip_expr,
|
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()
|
} else if position.is_deref_stable()
|
||||||
// Auto-deref doesn't combine with other adjustments
|
// Auto-deref doesn't combine with other adjustments
|
||||||
|
@ -406,7 +411,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
||||||
StateData {
|
StateData {
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
hir_id: expr.hir_id,
|
hir_id: expr.hir_id,
|
||||||
position
|
position,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -698,7 +703,7 @@ fn walk_parents<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) -> (Position, &'tcx [Adjustment<'tcx>]) {
|
) -> (Position, &'tcx [Adjustment<'tcx>]) {
|
||||||
let mut adjustments = [].as_slice();
|
let mut adjustments = [].as_slice();
|
||||||
let mut precedence = 0i8;
|
let mut precedence = 0i8;
|
||||||
|
@ -862,7 +867,11 @@ fn walk_parents<'tcx>(
|
||||||
} && impl_ty.is_ref()
|
} && impl_ty.is_ref()
|
||||||
&& let infcx = cx.tcx.infer_ctxt().build()
|
&& let infcx = cx.tcx.infer_ctxt().build()
|
||||||
&& infcx
|
&& 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()
|
.must_apply_modulo_regions()
|
||||||
{
|
{
|
||||||
return Some(Position::MethodReceiverRefImpl)
|
return Some(Position::MethodReceiverRefImpl)
|
||||||
|
@ -1078,7 +1087,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
|
||||||
param_ty: ParamTy,
|
param_ty: ParamTy,
|
||||||
mut expr: &Expr<'tcx>,
|
mut expr: &Expr<'tcx>,
|
||||||
precedence: i8,
|
precedence: i8,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) -> Position {
|
) -> Position {
|
||||||
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
|
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
|
||||||
let sized_trait_def_id = cx.tcx.lang_items().sized_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 ty::Param(param_ty) = trait_predicate.self_ty().kind()
|
||||||
&& let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
|
&& let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
|
||||||
&& ty.is_array()
|
&& ty.is_array()
|
||||||
&& !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
|
&& !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::traits::Reveal;
|
use rustc_middle::traits::Reveal;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef,
|
self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate,
|
||||||
Ty, TyCtxt,
|
TraitRef, Ty, TyCtxt,
|
||||||
};
|
};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::source_map::Span;
|
use rustc_span::source_map::Span;
|
||||||
|
|
|
@ -253,7 +253,7 @@ declare_clippy_lint! {
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.66.0"]
|
#[clippy::version = "1.66.0"]
|
||||||
pub UNNECESSARY_SAFETY_DOC,
|
pub UNNECESSARY_SAFETY_DOC,
|
||||||
style,
|
restriction,
|
||||||
"`pub fn` or `pub trait` with `# Safety` docs"
|
"`pub fn` or `pub trait` with `# Safety` docs"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
||||||
use rustc_middle::ty::binding::BindingMode;
|
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_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::symbol::sym;
|
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(mut snippet) = snippet_opt(cx, callee.span) {
|
||||||
if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait()
|
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()
|
&& 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::<Vec<_>>())
|
&& implements_trait(
|
||||||
|
cx,
|
||||||
|
callee_ty.peel_refs(),
|
||||||
|
fn_mut_id,
|
||||||
|
&args.iter().copied().map(Into::into).collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
&& path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr))
|
&& 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.
|
// 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);
|
if check_sig(cx, closure_ty, call_ty);
|
||||||
then {
|
then {
|
||||||
span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
|
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(
|
diag.span_suggestion(
|
||||||
expr.span,
|
expr.span,
|
||||||
"replace the closure with the method itself",
|
"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)
|
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 assoc_item = cx.tcx.associated_item(method_def_id);
|
||||||
let def_id = assoc_item.container_id(cx.tcx);
|
let def_id = assoc_item.container_id(cx.tcx);
|
||||||
match assoc_item.container {
|
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);
|
let ty = cx.tcx.type_of(def_id);
|
||||||
match ty.kind() {
|
match ty.kind() {
|
||||||
ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()),
|
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(),
|
_ => ty.to_string(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,21 +7,34 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// `exit()` terminates the program and doesn't provide a
|
/// Detects calls to the `exit()` function which terminates the program.
|
||||||
/// stack trace.
|
|
||||||
///
|
///
|
||||||
/// ### Why is this bad?
|
/// ### 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.
|
/// the main function.
|
||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
/// ```ignore
|
/// ```
|
||||||
/// std::process::exit(0)
|
/// 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"]
|
#[clippy::version = "1.41.0"]
|
||||||
pub EXIT,
|
pub EXIT,
|
||||||
restriction,
|
restriction,
|
||||||
"`std::process::exit` is called, terminating the program"
|
"detects `std::process::exit` calls"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint_pass!(Exit => [EXIT]);
|
declare_lint_pass!(Exit => [EXIT]);
|
||||||
|
|
|
@ -1,19 +1,22 @@
|
||||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
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::{
|
use clippy_utils::macros::{
|
||||||
is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage,
|
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::source::snippet_opt;
|
||||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
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 if_chain::if_chain;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::{
|
||||||
|
Applicability,
|
||||||
|
SuggestionStyle::{CompletelyHidden, ShowCode},
|
||||||
|
};
|
||||||
use rustc_hir::{Expr, ExprKind, HirId, QPath};
|
use rustc_hir::{Expr, ExprKind, HirId, QPath};
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
use rustc_span::edition::Edition::Edition2021;
|
use rustc_span::edition::Edition::Edition2021;
|
||||||
|
@ -103,19 +106,25 @@ declare_clippy_lint! {
|
||||||
/// format!("{var:.prec$}");
|
/// format!("{var:.prec$}");
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ### Known Problems
|
/// If allow-mixed-uninlined-format-args is set to false in clippy.toml,
|
||||||
///
|
/// the following code will also trigger the lint:
|
||||||
/// There may be a false positive if the format string is expanded from certain proc macros:
|
/// ```rust
|
||||||
///
|
/// # let var = 42;
|
||||||
/// ```ignore
|
/// format!("{} {}", var, 1+2);
|
||||||
/// println!(indoc!("{}"), var);
|
|
||||||
/// ```
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// # let var = 42;
|
||||||
|
/// format!("{var} {}", 1+2);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### Known Problems
|
||||||
///
|
///
|
||||||
/// If a format string contains a numbered argument that cannot be inlined
|
/// If a format string contains a numbered argument that cannot be inlined
|
||||||
/// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
|
/// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
|
||||||
#[clippy::version = "1.65.0"]
|
#[clippy::version = "1.65.0"]
|
||||||
pub UNINLINED_FORMAT_ARGS,
|
pub UNINLINED_FORMAT_ARGS,
|
||||||
pedantic,
|
style,
|
||||||
"using non-inlined variables in `format!` calls"
|
"using non-inlined variables in `format!` calls"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,13 +167,17 @@ impl_lint_pass!(FormatArgs => [
|
||||||
]);
|
]);
|
||||||
|
|
||||||
pub struct FormatArgs {
|
pub struct FormatArgs {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
|
ignore_mixed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatArgs {
|
impl FormatArgs {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self {
|
||||||
Self { msrv }
|
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_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);
|
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);
|
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() {
|
if args.format_string.span.from_expansion() {
|
||||||
return;
|
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,
|
// we cannot remove any other arguments in the format string,
|
||||||
// because the index numbers might be wrong after inlining.
|
// because the index numbers might be wrong after inlining.
|
||||||
// Example of an un-inlinable format: print!("{}{1}", foo, 2)
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
|
// multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
|
||||||
if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) {
|
// in those cases, make the code suggestion hidden
|
||||||
return;
|
let multiline_fix = fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span));
|
||||||
}
|
|
||||||
|
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
|
@ -297,12 +315,22 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si
|
||||||
call_site,
|
call_site,
|
||||||
"variables can be used directly in the `format!` string",
|
"variables can be used directly in the `format!` string",
|
||||||
|diag| {
|
|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)
|
if matches!(param.kind, Implicit | Starred | Named(_) | Numbered)
|
||||||
&& let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
|
&& let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
|
||||||
&& let [segment] = path.segments
|
&& let [segment] = path.segments
|
||||||
|
@ -317,8 +345,10 @@ fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut
|
||||||
fixes.push((arg_span, String::new()));
|
fixes.push((arg_span, String::new()));
|
||||||
true // successful inlining, continue checking
|
true // successful inlining, continue checking
|
||||||
} else {
|
} else {
|
||||||
// if we can't inline a numbered argument, we can't continue
|
// Do not continue inlining (return false) in case
|
||||||
param.kind != Numbered
|
// * 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(_)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::macros::span_is_local;
|
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::source::snippet_opt;
|
||||||
use clippy_utils::{meets_msrv, msrvs, path_def_id};
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::{walk_path, Visitor};
|
use rustc_hir::intravisit::{walk_path, Visitor};
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
|
@ -10,7 +11,6 @@ use rustc_hir::{
|
||||||
};
|
};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::hir::nested_filter::OnlyBodies;
|
use rustc_middle::hir::nested_filter::OnlyBodies;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::symbol::{kw, sym};
|
use rustc_span::symbol::{kw, sym};
|
||||||
use rustc_span::{Span, Symbol};
|
use rustc_span::{Span, Symbol};
|
||||||
|
@ -49,12 +49,12 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FromOverInto {
|
pub struct FromOverInto {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromOverInto {
|
impl FromOverInto {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
FromOverInto { msrv }
|
FromOverInto { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for FromOverInto {
|
impl<'tcx> LateLintPass<'tcx> for FromOverInto {
|
||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
125
clippy_lints/src/functions/misnamed_getters.rs
Normal file
125
clippy_lints/src/functions/misnamed_getters.rs
Normal file
|
@ -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) <self_data>.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 &<expr>, &mut <expr> and <expr>
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod misnamed_getters;
|
||||||
mod must_use;
|
mod must_use;
|
||||||
mod not_unsafe_ptr_arg_deref;
|
mod not_unsafe_ptr_arg_deref;
|
||||||
mod result;
|
mod result;
|
||||||
|
@ -260,6 +261,48 @@ declare_clippy_lint! {
|
||||||
"function returning `Result` with large `Err` type"
|
"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)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Functions {
|
pub struct Functions {
|
||||||
too_many_arguments_threshold: u64,
|
too_many_arguments_threshold: u64,
|
||||||
|
@ -286,6 +329,7 @@ impl_lint_pass!(Functions => [
|
||||||
MUST_USE_CANDIDATE,
|
MUST_USE_CANDIDATE,
|
||||||
RESULT_UNIT_ERR,
|
RESULT_UNIT_ERR,
|
||||||
RESULT_LARGE_ERR,
|
RESULT_LARGE_ERR,
|
||||||
|
MISNAMED_GETTERS,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for Functions {
|
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_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);
|
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);
|
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<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||||
|
|
|
@ -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;
|
if let hir::ItemKind::Enum(ref def, _) = item.kind;
|
||||||
then {
|
then {
|
||||||
let variants_size = AdtVariantInfo::new(cx, *adt, subst);
|
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(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
RESULT_LARGE_ERR,
|
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",
|
"the `Err`-variant returned from this function is very large",
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.span_label(
|
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),
|
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 {
|
if variant.size >= large_err_threshold {
|
||||||
let variant_def = &def.variants[variant.ind];
|
let variant_def = &def.variants[variant.ind];
|
||||||
diag.span_label(
|
diag.span_label(
|
||||||
|
|
|
@ -91,7 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|
||||||
infcx
|
infcx
|
||||||
.err_ctxt()
|
.err_ctxt()
|
||||||
.maybe_note_obligation_cause_for_async_await(db, &obligation);
|
.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!(
|
db.note(&format!(
|
||||||
"`{}` doesn't implement `{}`",
|
"`{}` doesn't implement `{}`",
|
||||||
trait_pred.self_ty(),
|
trait_pred.self_ty(),
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
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::source::snippet_with_macro_callsite;
|
||||||
use clippy_utils::{
|
use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
|
||||||
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::LangItem::{OptionNone, OptionSome};
|
||||||
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
|
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -47,12 +45,12 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IfThenSomeElseNone {
|
pub struct IfThenSomeElseNone {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IfThenSomeElseNone {
|
impl IfThenSomeElseNone {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +59,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||||
} else {
|
} 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) {
|
let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) {
|
||||||
"then_some"
|
"then_some"
|
||||||
} else {
|
} else {
|
||||||
method_body.insert_str(0, "|| ");
|
method_body.insert_str(0, "|| ");
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use clippy_utils::consts::{constant, Constant};
|
use clippy_utils::consts::{constant, Constant};
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::higher::IfLet;
|
use clippy_utils::higher::IfLet;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::ty::is_copy;
|
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 if_chain::if_chain;
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -11,7 +12,6 @@ use rustc_hir::intravisit::{self, Visitor};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::{symbol::Ident, Span};
|
use rustc_span::{symbol::Ident, Span};
|
||||||
|
|
||||||
|
@ -47,18 +47,17 @@ declare_clippy_lint! {
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.59.0"]
|
#[clippy::version = "1.59.0"]
|
||||||
pub INDEX_REFUTABLE_SLICE,
|
pub INDEX_REFUTABLE_SLICE,
|
||||||
nursery,
|
pedantic,
|
||||||
"avoid indexing on slices which could be destructed"
|
"avoid indexing on slices which could be destructed"
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct IndexRefutableSlice {
|
pub struct IndexRefutableSlice {
|
||||||
max_suggested_slice: u64,
|
max_suggested_slice: u64,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexRefutableSlice {
|
impl IndexRefutableSlice {
|
||||||
pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option<RustcVersion>) -> Self {
|
pub fn new(max_suggested_slice_pattern_length: u64, msrv: Msrv) -> Self {
|
||||||
Self {
|
Self {
|
||||||
max_suggested_slice: max_suggested_slice_pattern_length,
|
max_suggested_slice: max_suggested_slice_pattern_length,
|
||||||
msrv,
|
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 !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 let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
|
||||||
if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
|
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);
|
let found_slices = find_slice_values(cx, let_pat);
|
||||||
if !found_slices.is_empty();
|
if !found_slices.is_empty();
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
use clippy_utils::{
|
use clippy_utils::diagnostics::{self, span_lint_and_sugg};
|
||||||
diagnostics::{self, span_lint_and_sugg},
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
meets_msrv, msrvs, source,
|
use clippy_utils::source;
|
||||||
sugg::Sugg,
|
use clippy_utils::sugg::Sugg;
|
||||||
ty,
|
use clippy_utils::ty;
|
||||||
};
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::{source_map::Spanned, sym};
|
use rustc_span::{source_map::Spanned, sym};
|
||||||
|
|
||||||
|
@ -68,12 +66,12 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InstantSubtraction {
|
pub struct InstantSubtraction {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstantSubtraction {
|
impl InstantSubtraction {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +99,7 @@ impl LateLintPass<'_> for InstantSubtraction {
|
||||||
} else {
|
} else {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if !expr.span.from_expansion();
|
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_an_instant(cx, lhs);
|
||||||
if is_a_duration(cx, rhs);
|
if is_a_duration(cx, rhs);
|
||||||
|
|
|
@ -52,10 +52,9 @@ extern crate declare_clippy_lint;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clippy_utils::parse_msrv;
|
use clippy_utils::msrvs::Msrv;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_lint::{Lint, LintId};
|
use rustc_lint::{Lint, LintId};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
|
|
||||||
#[cfg(feature = "internal")]
|
#[cfg(feature = "internal")]
|
||||||
|
@ -322,48 +321,10 @@ pub use crate::utils::conf::{lookup_conf_file, Conf};
|
||||||
/// Used in `./src/driver.rs`.
|
/// Used in `./src/driver.rs`.
|
||||||
pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
|
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.
|
// 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| {
|
store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() }));
|
||||||
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<RustcVersion> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[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_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
|
||||||
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
|
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 avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
|
||||||
let allow_expect_in_tests = conf.allow_expect_in_tests;
|
let allow_expect_in_tests = conf.allow_expect_in_tests;
|
||||||
let allow_unwrap_in_tests = conf.allow_unwrap_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 |_| {
|
store.register_late_pass(move |_| {
|
||||||
Box::new(methods::Methods::new(
|
Box::new(methods::Methods::new(
|
||||||
avoid_breaking_exported_api,
|
avoid_breaking_exported_api,
|
||||||
msrv,
|
msrv(),
|
||||||
allow_expect_in_tests,
|
allow_expect_in_tests,
|
||||||
allow_unwrap_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;
|
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_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_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_non_exhaustive::ManualNonExhaustiveEnum::new(msrv())));
|
||||||
store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::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_static_lifetimes::RedundantStaticLifetimes::new(msrv())));
|
||||||
store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::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(checked_conversions::CheckedConversions::new(msrv())));
|
||||||
store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::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(ranges::Ranges::new(msrv())));
|
||||||
store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::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(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(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(needless_question_mark::NeedlessQuestionMark));
|
||||||
store.register_late_pass(move |_| Box::new(casts::Casts::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_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(size_of_in_element_count::SizeOfInElementCount));
|
||||||
store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod));
|
store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod));
|
||||||
let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
|
let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
|
||||||
store.register_late_pass(move |_| {
|
store.register_late_pass(move |_| {
|
||||||
Box::new(index_refutable_slice::IndexRefutableSlice::new(
|
Box::new(index_refutable_slice::IndexRefutableSlice::new(
|
||||||
max_suggested_slice_pattern_length,
|
max_suggested_slice_pattern_length,
|
||||||
msrv,
|
msrv(),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
store.register_late_pass(|_| Box::<shadow::Shadow>::default());
|
store.register_late_pass(|_| Box::<shadow::Shadow>::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(borrow_deref_ref::BorrowDerefRef));
|
||||||
store.register_late_pass(|_| Box::new(no_effect::NoEffect));
|
store.register_late_pass(|_| Box::new(no_effect::NoEffect));
|
||||||
store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment));
|
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;
|
let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
|
||||||
store.register_late_pass(move |_| {
|
store.register_late_pass(move |_| {
|
||||||
Box::new(cognitive_complexity::CognitiveComplexity::new(
|
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(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
|
||||||
store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
|
store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
|
||||||
store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
|
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(option_if_let_else::OptionIfLetElse));
|
||||||
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
|
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
|
||||||
store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
|
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::<vec_init_then_push::VecInitThenPush>::default());
|
store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::default());
|
||||||
store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing));
|
store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing));
|
||||||
store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10));
|
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_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison));
|
||||||
store.register_early_pass(move || Box::new(module_style::ModStyle));
|
store.register_early_pass(move || Box::new(module_style::ModStyle));
|
||||||
store.register_late_pass(|_| Box::new(unused_async::UnusedAsync));
|
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(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_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
|
||||||
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
|
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(needless_late_init::NeedlessLateInit));
|
||||||
store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
|
store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
|
||||||
store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields));
|
store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields));
|
||||||
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
|
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::new(default_union_representation::DefaultUnionRepresentation));
|
||||||
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
|
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
|
||||||
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
|
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_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
|
||||||
store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
|
store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
|
||||||
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
|
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(swap_ptr_to_ref::SwapPtrToRef));
|
||||||
store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch));
|
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(read_zero_byte_vec::ReadZeroByteVec));
|
||||||
store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
|
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_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_retain::ManualRetain::new(msrv())));
|
||||||
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
|
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(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(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
|
||||||
store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
|
store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::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(|_| 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(manual_string_new::ManualStringNew));
|
||||||
store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
|
store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
|
||||||
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
|
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(missing_trait_methods::MissingTraitMethods));
|
||||||
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
|
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(|_| 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`
|
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::trait_ref_of_method;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
|
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
|
||||||
use rustc_hir::intravisit::{
|
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,
|
walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
|
||||||
};
|
};
|
||||||
use rustc_hir::lang_items;
|
use rustc_hir::lang_items;
|
||||||
|
@ -481,7 +481,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
||||||
sub_visitor.visit_fn_decl(decl);
|
sub_visitor.visit_fn_decl(decl);
|
||||||
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
|
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
|
||||||
},
|
},
|
||||||
TyKind::TraitObject(bounds, ref lt, _) => {
|
TyKind::TraitObject(bounds, lt, _) => {
|
||||||
if !lt.is_elided() {
|
if !lt.is_elided() {
|
||||||
self.unelided_trait_object_lifetime = true;
|
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 {
|
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);
|
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);
|
||||||
// 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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
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::source::snippet_with_applicability;
|
||||||
use clippy_utils::{get_parent_expr, meets_msrv, msrvs};
|
|
||||||
use rustc_ast::ast::LitKind;
|
use rustc_ast::ast::LitKind;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
|
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
|
|
||||||
|
@ -34,12 +34,12 @@ declare_clippy_lint! {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ManualBits {
|
pub struct ManualBits {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManualBits {
|
impl ManualBits {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for ManualBits {
|
impl<'tcx> LateLintPass<'tcx> for ManualBits {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 itertools::Itertools;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
use rustc_errors::Diagnostic;
|
use rustc_errors::Diagnostic;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind,
|
def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind,
|
||||||
};
|
};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::{symbol::sym, Span};
|
use rustc_span::{symbol::sym, Span};
|
||||||
use std::ops::Deref;
|
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! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Identifies good opportunities for a clamp function from std or core, and suggests using it.
|
/// 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]);
|
impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]);
|
||||||
|
|
||||||
pub struct ManualClamp {
|
pub struct ManualClamp {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManualClamp {
|
impl ManualClamp {
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,7 +111,7 @@ struct InputMinMax<'tcx> {
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for ManualClamp {
|
impl<'tcx> LateLintPass<'tcx> for ManualClamp {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if !expr.span.from_expansion() {
|
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>) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
for suggestion in is_two_if_pattern(cx, block) {
|
for suggestion in is_two_if_pattern(cx, block) {
|
||||||
|
|
|
@ -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_ast::LitKind::{Byte, Char};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd};
|
use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::{def_id::DefId, sym};
|
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! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Suggests to use dedicated built-in methods,
|
/// Suggests to use dedicated built-in methods,
|
||||||
|
@ -45,12 +42,12 @@ declare_clippy_lint! {
|
||||||
impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]);
|
impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]);
|
||||||
|
|
||||||
pub struct ManualIsAsciiCheck {
|
pub struct ManualIsAsciiCheck {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManualIsAsciiCheck {
|
impl ManualIsAsciiCheck {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,11 +67,11 @@ enum CharRange {
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
|
impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
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;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::higher::IfLetOrMatch;
|
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::ty::is_type_diagnostic_item;
|
||||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||||
use clippy_utils::{meets_msrv, msrvs, peel_blocks};
|
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
|
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
@ -50,13 +50,13 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ManualLetElse {
|
pub struct ManualLetElse {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
matches_behaviour: MatchLintBehaviour,
|
matches_behaviour: MatchLintBehaviour,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManualLetElse {
|
impl ManualLetElse {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>, matches_behaviour: MatchLintBehaviour) -> Self {
|
pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self {
|
||||||
Self {
|
Self {
|
||||||
msrv,
|
msrv,
|
||||||
matches_behaviour,
|
matches_behaviour,
|
||||||
|
@ -69,7 +69,7 @@ impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]);
|
||||||
impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
|
impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
|
||||||
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) {
|
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) {
|
||||||
let if_let_or_match = if_chain! {
|
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 !in_external_macro(cx.sess(), stmt.span);
|
||||||
if let StmtKind::Local(local) = stmt.kind;
|
if let StmtKind::Local(local) = stmt.kind;
|
||||||
if let Some(init) = local.init;
|
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
|
// * unused binding collision detection with existing ones
|
||||||
// * putting patterns with at the top level | inside ()
|
// * putting patterns with at the top level | inside ()
|
||||||
// for this to be machine applicable.
|
// 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(..)) {
|
let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
|
||||||
sn_else
|
sn_else.into_owned()
|
||||||
} else {
|
} else {
|
||||||
format!("{{ {sn_else} }}")
|
format!("{{ {sn_else} }}")
|
||||||
};
|
};
|
||||||
let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
|
let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
|
||||||
diag.span_suggestion(span, "consider writing", sugg, app);
|
diag.span_suggestion(span, "consider writing", sugg, app);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
|
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::source::snippet_opt;
|
||||||
use clippy_utils::{is_doc_hidden, meets_msrv, msrvs};
|
|
||||||
use rustc_ast::ast::{self, VisibilityKind};
|
use rustc_ast::ast::{self, VisibilityKind};
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::Applicability;
|
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_hir::{self as hir, Expr, ExprKind, QPath};
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::ty::DefIdTree;
|
use rustc_middle::ty::DefIdTree;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::def_id::{DefId, LocalDefId};
|
use rustc_span::def_id::{DefId, LocalDefId};
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
@ -63,12 +63,12 @@ declare_clippy_lint! {
|
||||||
|
|
||||||
#[expect(clippy::module_name_repetitions)]
|
#[expect(clippy::module_name_repetitions)]
|
||||||
pub struct ManualNonExhaustiveStruct {
|
pub struct ManualNonExhaustiveStruct {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManualNonExhaustiveStruct {
|
impl ManualNonExhaustiveStruct {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,14 +77,14 @@ impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
|
||||||
|
|
||||||
#[expect(clippy::module_name_repetitions)]
|
#[expect(clippy::module_name_repetitions)]
|
||||||
pub struct ManualNonExhaustiveEnum {
|
pub struct ManualNonExhaustiveEnum {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
constructed_enum_variants: FxHashSet<(DefId, DefId)>,
|
constructed_enum_variants: FxHashSet<(DefId, DefId)>,
|
||||||
potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
|
potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManualNonExhaustiveEnum {
|
impl ManualNonExhaustiveEnum {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self {
|
Self {
|
||||||
msrv,
|
msrv,
|
||||||
constructed_enum_variants: FxHashSet::default(),
|
constructed_enum_variants: FxHashSet::default(),
|
||||||
|
@ -97,7 +97,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
|
||||||
|
|
||||||
impl EarlyLintPass for ManualNonExhaustiveStruct {
|
impl EarlyLintPass for ManualNonExhaustiveStruct {
|
||||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
|
impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
|
||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use clippy_utils::consts::{constant_full_int, FullInt};
|
use clippy_utils::consts::{constant_full_int, FullInt};
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
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_errors::Applicability;
|
||||||
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
|
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -34,12 +34,12 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ManualRemEuclid {
|
pub struct ManualRemEuclid {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManualRemEuclid {
|
impl ManualRemEuclid {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,11 +48,11 @@ impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
|
impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
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;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_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::{get_parent_expr, match_def_path, paths, SpanlessEq};
|
||||||
use clippy_utils::{meets_msrv, msrvs};
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
|
@ -50,12 +50,12 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ManualRetain {
|
pub struct ManualRetain {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManualRetain {
|
impl ManualRetain {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,9 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain {
|
||||||
&& let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind
|
&& 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)
|
&& 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) {
|
&& match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) {
|
||||||
check_into_iter(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_iter(cx, parent_expr, left_expr, target_expr, &self.msrv);
|
||||||
check_to_owned(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<'_>,
|
parent_expr: &hir::Expr<'_>,
|
||||||
left_expr: &hir::Expr<'_>,
|
left_expr: &hir::Expr<'_>,
|
||||||
target_expr: &hir::Expr<'_>,
|
target_expr: &hir::Expr<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind
|
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)
|
&& 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<'_>,
|
parent_expr: &hir::Expr<'_>,
|
||||||
left_expr: &hir::Expr<'_>,
|
left_expr: &hir::Expr<'_>,
|
||||||
target_expr: &hir::Expr<'_>,
|
target_expr: &hir::Expr<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
|
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)
|
&& 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<'_>,
|
parent_expr: &hir::Expr<'_>,
|
||||||
left_expr: &hir::Expr<'_>,
|
left_expr: &hir::Expr<'_>,
|
||||||
target_expr: &hir::Expr<'_>,
|
target_expr: &hir::Expr<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
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 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)
|
&& 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)
|
&& 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))
|
.any(|&method| match_def_path(cx, collect_def_id, method))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option<RustcVersion>) -> bool {
|
fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool {
|
||||||
let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
|
let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
|
||||||
ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
|
ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
|
||||||
is_type_diagnostic_item(cx, expr_ty, *ty)
|
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))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use clippy_utils::consts::{constant, Constant};
|
use clippy_utils::consts::{constant, Constant};
|
||||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::usage::mutated_variables;
|
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 if_chain::if_chain;
|
||||||
use rustc_ast::ast::LitKind;
|
use rustc_ast::ast::LitKind;
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
|
@ -11,7 +12,6 @@ use rustc_hir::BinOpKind;
|
||||||
use rustc_hir::{BorrowKind, Expr, ExprKind};
|
use rustc_hir::{BorrowKind, Expr, ExprKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::source_map::Spanned;
|
use rustc_span::source_map::Spanned;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
@ -48,12 +48,12 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ManualStrip {
|
pub struct ManualStrip {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManualStrip {
|
impl ManualStrip {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ enum StripKind {
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,13 +23,13 @@ mod single_match;
|
||||||
mod try_err;
|
mod try_err;
|
||||||
mod wild_in_or_pats;
|
mod wild_in_or_pats;
|
||||||
|
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::{snippet_opt, walk_span_to_context};
|
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_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
|
||||||
use rustc_lexer::{tokenize, TokenKind};
|
use rustc_lexer::{tokenize, TokenKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::{Span, SpanData, SyntaxContext};
|
use rustc_span::{Span, SpanData, SyntaxContext};
|
||||||
|
|
||||||
|
@ -930,13 +930,13 @@ declare_clippy_lint! {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Matches {
|
pub struct Matches {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
infallible_destructuring_match_linted: bool,
|
infallible_destructuring_match_linted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Matches {
|
impl Matches {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self {
|
Self {
|
||||||
msrv,
|
msrv,
|
||||||
..Matches::default()
|
..Matches::default()
|
||||||
|
@ -1000,9 +1000,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||||
|
|
||||||
if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
|
if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
|
||||||
if source == MatchSource::Normal {
|
if source == MatchSource::Normal {
|
||||||
if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO)
|
if !(self.msrv.meets(msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) {
|
||||||
&& match_like_matches::check_match(cx, expr, ex, arms))
|
|
||||||
{
|
|
||||||
match_same_arms::check(cx, 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);
|
collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
|
||||||
if !from_expansion {
|
if !from_expansion {
|
||||||
if let Some(else_expr) = if_let.if_else {
|
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(
|
match_like_matches::check_if_let(
|
||||||
cx,
|
cx,
|
||||||
expr,
|
expr,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
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 if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::LangItem::ResultErr;
|
use rustc_hir::LangItem::ResultErr;
|
||||||
|
@ -107,7 +107,7 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
|
||||||
fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let ty::Adt(def, subst) = ty.kind();
|
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);
|
let ready_ty = subst.type_at(0);
|
||||||
|
|
||||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
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<Ty<'tcx>> {
|
fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let ty::Adt(def, subst) = ty.kind();
|
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);
|
let ready_ty = subst.type_at(0);
|
||||||
|
|
||||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
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::source::{snippet, snippet_with_applicability};
|
||||||
use clippy_utils::ty::is_non_aggregate_primitive_type;
|
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 if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::LangItem::OptionNone;
|
use rustc_hir::LangItem::OptionNone;
|
||||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
|
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::source_map::Span;
|
use rustc_span::source_map::Span;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
@ -227,12 +227,12 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MemReplace {
|
pub struct MemReplace {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemReplace {
|
impl MemReplace {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
||||||
then {
|
then {
|
||||||
check_replace_option_with_none(cx, src, dest, expr.span);
|
check_replace_option_with_none(cx, src, dest, expr.span);
|
||||||
check_replace_with_uninit(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);
|
check_replace_with_default(cx, src, dest, expr.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
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::ty::{get_iterator_item_ty, is_copy};
|
||||||
use clippy_utils::{is_trait_method, meets_msrv, msrvs};
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::Expr;
|
use rustc_hir::Expr;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
use super::CLONED_INSTEAD_OF_COPIED;
|
use super::CLONED_INSTEAD_OF_COPIED;
|
||||||
|
|
||||||
pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<RustcVersion>) {
|
pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: &Msrv) {
|
||||||
let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
|
let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
|
||||||
let inner_ty = match recv_ty.kind() {
|
let inner_ty = match recv_ty.kind() {
|
||||||
// `Option<T>` -> `T`
|
// `Option<T>` -> `T`
|
||||||
ty::Adt(adt, subst)
|
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)
|
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) {
|
match get_iterator_item_ty(cx, recv_ty) {
|
||||||
// <T as Iterator>::Item
|
// <T as Iterator>::Item
|
||||||
Some(ty) => ty,
|
Some(ty) => ty,
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
use super::ERR_EXPECT;
|
use super::ERR_EXPECT;
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::ty::has_debug_impl;
|
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_errors::Applicability;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
pub(super) fn check(
|
pub(super) fn check(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
_expr: &rustc_hir::Expr<'_>,
|
_expr: &rustc_hir::Expr<'_>,
|
||||||
recv: &rustc_hir::Expr<'_>,
|
recv: &rustc_hir::Expr<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
|
||||||
expect_span: Span,
|
expect_span: Span,
|
||||||
err_span: Span,
|
err_span: Span,
|
||||||
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
|
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
|
// 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)
|
// 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<T, E>` type
|
// Grabs the `Result<T, E>` type
|
||||||
let result_type = cx.typeck_results().expr_ty(recv);
|
let result_type = cx.typeck_results().expr_ty(recv);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
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::source::snippet;
|
||||||
use clippy_utils::{is_trait_method, meets_msrv, msrvs};
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
|
|
||||||
use super::FILTER_MAP_NEXT;
|
use super::FILTER_MAP_NEXT;
|
||||||
|
@ -14,10 +14,10 @@ pub(super) fn check<'tcx>(
|
||||||
expr: &'tcx hir::Expr<'_>,
|
expr: &'tcx hir::Expr<'_>,
|
||||||
recv: &'tcx hir::Expr<'_>,
|
recv: &'tcx hir::Expr<'_>,
|
||||||
arg: &'tcx hir::Expr<'_>,
|
arg: &'tcx hir::Expr<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
if is_trait_method(cx, expr, sym::Iterator) {
|
if is_trait_method(cx, expr, sym::Iterator) {
|
||||||
if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) {
|
if !msrv.meets(msrvs::ITERATOR_FIND_MAP) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
//! Lint for `c.is_digit(10)`
|
//! Lint for `c.is_digit(10)`
|
||||||
|
|
||||||
use super::IS_DIGIT_ASCII_RADIX;
|
use super::IS_DIGIT_ASCII_RADIX;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::{
|
use clippy_utils::{
|
||||||
consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
|
consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, source::snippet_with_applicability,
|
||||||
source::snippet_with_applicability,
|
|
||||||
};
|
};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::Expr;
|
use rustc_hir::Expr;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
|
|
||||||
pub(super) fn check<'tcx>(
|
pub(super) fn check<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
expr: &'tcx Expr<'_>,
|
expr: &'tcx Expr<'_>,
|
||||||
self_arg: &'tcx Expr<'_>,
|
self_arg: &'tcx Expr<'_>,
|
||||||
radix: &'tcx Expr<'_>,
|
radix: &'tcx Expr<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) {
|
if !msrv.meets(msrvs::IS_ASCII_DIGIT) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
|
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 if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
@ -9,19 +10,12 @@ use rustc_lint::LateContext;
|
||||||
use rustc_middle::mir::Mutability;
|
use rustc_middle::mir::Mutability;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_middle::ty::adjustment::Adjust;
|
use rustc_middle::ty::adjustment::Adjust;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
use super::MAP_CLONE;
|
use super::MAP_CLONE;
|
||||||
|
|
||||||
pub(super) fn check(
|
pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) {
|
||||||
cx: &LateContext<'_>,
|
|
||||||
e: &hir::Expr<'_>,
|
|
||||||
recv: &hir::Expr<'_>,
|
|
||||||
arg: &hir::Expr<'_>,
|
|
||||||
msrv: Option<RustcVersion>,
|
|
||||||
) {
|
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
|
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
|
||||||
if cx.tcx.impl_of_method(method_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<RustcVersion>) {
|
fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) {
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
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")
|
("you are using an explicit closure for copying elements", "copied")
|
||||||
} else {
|
} else {
|
||||||
("you are using an explicit closure for cloning elements", "cloned")
|
("you are using an explicit closure for cloning elements", "cloned")
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
use clippy_utils::usage::mutated_variables;
|
use clippy_utils::usage::mutated_variables;
|
||||||
use clippy_utils::{meets_msrv, msrvs};
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
|
||||||
use super::MAP_UNWRAP_OR;
|
use super::MAP_UNWRAP_OR;
|
||||||
|
@ -19,13 +18,13 @@ pub(super) fn check<'tcx>(
|
||||||
recv: &'tcx hir::Expr<'_>,
|
recv: &'tcx hir::Expr<'_>,
|
||||||
map_arg: &'tcx hir::Expr<'_>,
|
map_arg: &'tcx hir::Expr<'_>,
|
||||||
unwrap_arg: &'tcx hir::Expr<'_>,
|
unwrap_arg: &'tcx hir::Expr<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// lint if the caller of `map()` is an `Option`
|
// 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_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_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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,8 +104,9 @@ mod zst_offset;
|
||||||
use bind_instead_of_map::BindInsteadOfMap;
|
use bind_instead_of_map::BindInsteadOfMap;
|
||||||
use clippy_utils::consts::{constant, Constant};
|
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_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::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 if_chain::if_chain;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
|
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_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_middle::ty::{self, TraitRef, Ty};
|
use rustc_middle::ty::{self, TraitRef, Ty};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
|
@ -3163,7 +3163,7 @@ declare_clippy_lint! {
|
||||||
|
|
||||||
pub struct Methods {
|
pub struct Methods {
|
||||||
avoid_breaking_exported_api: bool,
|
avoid_breaking_exported_api: bool,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
allow_expect_in_tests: bool,
|
allow_expect_in_tests: bool,
|
||||||
allow_unwrap_in_tests: bool,
|
allow_unwrap_in_tests: bool,
|
||||||
}
|
}
|
||||||
|
@ -3172,7 +3172,7 @@ impl Methods {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
avoid_breaking_exported_api: bool,
|
avoid_breaking_exported_api: bool,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
allow_expect_in_tests: bool,
|
allow_expect_in_tests: bool,
|
||||||
allow_unwrap_in_tests: bool,
|
allow_unwrap_in_tests: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -3325,7 +3325,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||||
single_char_add_str::check(cx, expr, receiver, args);
|
single_char_add_str::check(cx, expr, receiver, args);
|
||||||
into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
|
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);
|
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 => {
|
hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
|
||||||
let mut info = BinaryExprInfo {
|
let mut info = BinaryExprInfo {
|
||||||
|
@ -3501,7 +3501,7 @@ impl Methods {
|
||||||
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
||||||
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
||||||
("assume_init", []) => uninit_assumed_init::check(cx, expr, 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) => {
|
("collect", []) if is_trait_method(cx, expr, sym::Iterator) => {
|
||||||
needless_collect::check(cx, span, expr, recv, call_span);
|
needless_collect::check(cx, span, expr, recv, call_span);
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
|
@ -3512,7 +3512,7 @@ impl Methods {
|
||||||
map_collect_result_unit::check(cx, expr, m_recv, m_arg);
|
map_collect_result_unit::check(cx, expr, m_recv, m_arg);
|
||||||
},
|
},
|
||||||
Some(("take", take_self_arg, [take_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);
|
manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3539,7 +3539,7 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("expect", [_]) => match method_call(recv) {
|
("expect", [_]) => match method_call(recv) {
|
||||||
Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, 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_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),
|
("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);
|
unit_hash::check(cx, expr, recv, arg);
|
||||||
},
|
},
|
||||||
("is_file", []) => filetype_is_file::check(cx, expr, recv),
|
("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_none", []) => check_is_some_is_none(cx, expr, recv, false),
|
||||||
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
|
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
|
||||||
("iter" | "iter_mut" | "into_iter", []) => {
|
("iter" | "iter_mut" | "into_iter", []) => {
|
||||||
|
@ -3601,7 +3601,7 @@ impl Methods {
|
||||||
},
|
},
|
||||||
(name @ ("map" | "map_err"), [m_arg]) => {
|
(name @ ("map" | "map_err"), [m_arg]) => {
|
||||||
if name == "map" {
|
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) {
|
if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) {
|
||||||
iter_kv_map::check(cx, map_name, expr, recv2, m_arg);
|
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) {
|
if let Some((name, recv2, args, span2,_)) = method_call(recv) {
|
||||||
match (name, args) {
|
match (name, args) {
|
||||||
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, 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),
|
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
|
||||||
("filter", [f_arg]) => {
|
("filter", [f_arg]) => {
|
||||||
filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
|
filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
|
||||||
},
|
},
|
||||||
|
@ -3632,7 +3632,7 @@ impl Methods {
|
||||||
match (name2, args2) {
|
match (name2, args2) {
|
||||||
("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
|
("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
|
||||||
("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
|
("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),
|
("iter", []) => iter_next_slice::check(cx, expr, recv2),
|
||||||
("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
|
("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
|
||||||
("skip_while", [_]) => skip_while_next::check(cx, expr),
|
("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);
|
vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
|
||||||
},
|
},
|
||||||
("seek", [arg]) => {
|
("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);
|
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);
|
seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3699,7 +3699,7 @@ impl Methods {
|
||||||
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
|
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
|
||||||
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
|
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
|
||||||
suspicious_splitn::check(cx, name, expr, recv, count);
|
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, _]) => {
|
("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
|
||||||
|
@ -3717,7 +3717,7 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("take", []) => needless_option_take::check(cx, expr, recv),
|
("take", []) => needless_option_take::check(cx, expr, recv),
|
||||||
("then", [arg]) => {
|
("then", [arg]) => {
|
||||||
if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
|
if !self.msrv.meets(msrvs::BOOL_THEN_SOME) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
|
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) {
|
("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) => {},
|
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {},
|
||||||
_ => {
|
_ => {
|
||||||
unwrap_or_else_default::check(cx, expr, recv, u_arg);
|
unwrap_or_else_default::check(cx, expr, recv, u_arg);
|
||||||
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
|
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
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 if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
|
|
||||||
use super::OPTION_AS_REF_DEREF;
|
use super::OPTION_AS_REF_DEREF;
|
||||||
|
@ -19,9 +19,9 @@ pub(super) fn check(
|
||||||
as_ref_recv: &hir::Expr<'_>,
|
as_ref_recv: &hir::Expr<'_>,
|
||||||
map_arg: &hir::Expr<'_>,
|
map_arg: &hir::Expr<'_>,
|
||||||
is_mut: bool,
|
is_mut: bool,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) {
|
if !msrv.meets(msrvs::OPTION_AS_DEREF) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use clippy_utils::consts::{constant, Constant};
|
use clippy_utils::consts::{constant, Constant};
|
||||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
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::source::snippet_with_context;
|
||||||
use clippy_utils::usage::local_used_after_expr;
|
use clippy_utils::usage::local_used_after_expr;
|
||||||
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
|
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 core::ops::ControlFlow;
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -12,7 +13,6 @@ use rustc_hir::{
|
||||||
};
|
};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_span::{sym, Span, Symbol, SyntaxContext};
|
use rustc_span::{sym, Span, Symbol, SyntaxContext};
|
||||||
|
|
||||||
use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
|
use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
|
||||||
|
@ -24,7 +24,7 @@ pub(super) fn check(
|
||||||
self_arg: &Expr<'_>,
|
self_arg: &Expr<'_>,
|
||||||
pat_arg: &Expr<'_>,
|
pat_arg: &Expr<'_>,
|
||||||
count: u128,
|
count: u128,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
|
if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
|
||||||
return;
|
return;
|
||||||
|
@ -34,7 +34,7 @@ pub(super) fn check(
|
||||||
IterUsageKind::Nth(n) => count > n + 1,
|
IterUsageKind::Nth(n) => count > n + 1,
|
||||||
IterUsageKind::NextTuple => count > 2,
|
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)) {
|
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),
|
Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg),
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
use super::implicit_clone::is_clone_like;
|
use super::implicit_clone::is_clone_like;
|
||||||
use super::unnecessary_iter_cloned::{self, is_into_iter};
|
use super::unnecessary_iter_cloned::{self, is_into_iter};
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet_opt;
|
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::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
|
||||||
use clippy_utils::visitors::find_all_ret_expressions;
|
use clippy_utils::visitors::find_all_ret_expressions;
|
||||||
use clippy_utils::{
|
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
|
||||||
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_errors::Applicability;
|
||||||
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
|
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
|
||||||
use rustc_hir_typeck::{FnCtxt, Inherited};
|
use rustc_hir_typeck::{FnCtxt, Inherited};
|
||||||
|
@ -16,14 +14,9 @@ use rustc_lint::LateContext;
|
||||||
use rustc_middle::mir::Mutability;
|
use rustc_middle::mir::Mutability;
|
||||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
|
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
|
||||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
|
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
|
||||||
use rustc_middle::ty::EarlyBinder;
|
use rustc_middle::ty::{self, Clause, EarlyBinder, 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_span::{sym, Symbol};
|
||||||
use rustc_trait_selection::traits::{
|
use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
|
||||||
query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause,
|
|
||||||
};
|
|
||||||
use std::cmp::max;
|
|
||||||
|
|
||||||
use super::UNNECESSARY_TO_OWNED;
|
use super::UNNECESSARY_TO_OWNED;
|
||||||
|
|
||||||
|
@ -33,7 +26,7 @@ pub fn check<'tcx>(
|
||||||
method_name: Symbol,
|
method_name: Symbol,
|
||||||
receiver: &'tcx Expr<'_>,
|
receiver: &'tcx Expr<'_>,
|
||||||
args: &'tcx [Expr<'_>],
|
args: &'tcx [Expr<'_>],
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
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<'_>,
|
expr: &Expr<'_>,
|
||||||
method_name: Symbol,
|
method_name: Symbol,
|
||||||
receiver: &Expr<'_>,
|
receiver: &Expr<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let Some(parent) = get_parent_expr(cx, expr);
|
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) {
|
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
|
||||||
return 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"
|
"copied"
|
||||||
} else {
|
} else {
|
||||||
"cloned"
|
"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 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;
|
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);
|
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
|
// We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
|
||||||
// `Target = T`.
|
// `Target = T`.
|
||||||
if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
|
if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) {
|
||||||
let n_refs = max(n_refs, usize::from(!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);
|
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||||
then {
|
then {
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
|
@ -345,13 +349,13 @@ fn get_input_traits_and_projections<'tcx>(
|
||||||
if trait_predicate.trait_ref.self_ty() == input {
|
if trait_predicate.trait_ref.self_ty() == input {
|
||||||
trait_predicates.push(trait_predicate);
|
trait_predicates.push(trait_predicate);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
PredicateKind::Clause(Clause::Projection(projection_predicate)) => {
|
PredicateKind::Clause(Clause::Projection(projection_predicate)) => {
|
||||||
if projection_predicate.projection_ty.self_ty() == input {
|
if projection_predicate.projection_ty.self_ty() == input {
|
||||||
projection_predicates.push(projection_predicate);
|
projection_predicates.push(projection_predicate);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
_ => {}
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(trait_predicates, projection_predicates)
|
(trait_predicates, projection_predicates)
|
||||||
|
@ -403,8 +407,10 @@ 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)
|
let mut trait_predicates = cx.tcx.param_env(callee_def_id)
|
||||||
.caller_bounds().iter().filter(|predicate| {
|
.caller_bounds().iter().filter(|predicate| {
|
||||||
if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder()
|
if let PredicateKind::Clause(Clause::Trait(trait_predicate))
|
||||||
&& trait_predicate.trait_ref.self_ty() == *param_ty {
|
= predicate.kind().skip_binder()
|
||||||
|
&& trait_predicate.trait_ref.self_ty() == *param_ty
|
||||||
|
{
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
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"
|
/// Returns true if the named method can be used to convert the receiver to its "owned"
|
||||||
/// representation.
|
/// representation.
|
||||||
fn is_to_owned_like<'a>(
|
fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool {
|
||||||
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_clone_like(cx, method_name.as_str(), method_def_id)
|
||||||
|| is_cow_into_owned(cx, method_name, 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)
|
|| is_to_string_on_string_like(cx, call_expr, method_name, method_def_id)
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use clippy_utils::diagnostics::span_lint;
|
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::qualify_min_const_fn::is_min_const_fn;
|
||||||
use clippy_utils::ty::has_drop;
|
use clippy_utils::ty::has_drop;
|
||||||
use clippy_utils::{
|
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
|
||||||
fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, meets_msrv, msrvs, trait_ref_of_method,
|
|
||||||
};
|
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||||
use rustc_hir::intravisit::FnKind;
|
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_hir_analysis::hir_ty_to_ty;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
@ -75,12 +73,12 @@ declare_clippy_lint! {
|
||||||
impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
|
impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
|
||||||
|
|
||||||
pub struct MissingConstForFn {
|
pub struct MissingConstForFn {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MissingConstForFn {
|
impl MissingConstForFn {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||||
span: Span,
|
span: Span,
|
||||||
hir_id: HirId,
|
hir_id: HirId,
|
||||||
) {
|
) {
|
||||||
if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) {
|
if !self.msrv.meets(msrvs::CONST_IF_MATCH) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||||
|
|
||||||
let mir = cx.tcx.optimized_mir(def_id);
|
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()) {
|
if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
|
||||||
cx.tcx.sess.span_err(span, err.as_ref());
|
cx.tcx.sess.span_err(span, err.as_ref());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||||
use clippy_utils::ptr::get_spans;
|
use clippy_utils::ptr::get_spans;
|
||||||
use clippy_utils::source::{snippet, snippet_opt};
|
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 clippy_utils::{get_trait_def_id, is_self, paths};
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_ast::ast::Attribute;
|
use rustc_ast::ast::Attribute;
|
||||||
|
@ -124,7 +126,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||||
.filter_map(|obligation| {
|
.filter_map(|obligation| {
|
||||||
// Note that we do not want to deal with qualified predicates here.
|
// Note that we do not want to deal with qualified predicates here.
|
||||||
match obligation.predicate.kind().no_bound_vars() {
|
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,
|
_ => None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,7 +6,8 @@ use clippy_utils::ty::has_drop;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource};
|
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 rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use std::ops::Deref;
|
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<'_>) {
|
fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let StmtKind::Semi(expr) = stmt.kind;
|
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 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 {
|
then {
|
||||||
if let ExprKind::Index(..) = &expr.kind {
|
if let ExprKind::Index(..) = &expr.kind {
|
||||||
let snippet = if let (Some(arr), Some(func)) =
|
let snippet = if let (Some(arr), Some(func)) =
|
||||||
|
|
|
@ -490,7 +490,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
|
||||||
ty_name: name.ident.name,
|
ty_name: name.ident.name,
|
||||||
method_renames,
|
method_renames,
|
||||||
ref_prefix: RefPrefix {
|
ref_prefix: RefPrefix {
|
||||||
lt: lt.clone(),
|
lt: *lt,
|
||||||
mutability,
|
mutability,
|
||||||
},
|
},
|
||||||
deref_ty,
|
deref_ty,
|
||||||
|
@ -693,9 +693,10 @@ fn matches_preds<'tcx>(
|
||||||
cx.tcx,
|
cx.tcx,
|
||||||
ObligationCause::dummy(),
|
ObligationCause::dummy(),
|
||||||
cx.param_env,
|
cx.param_env,
|
||||||
cx.tcx.mk_predicate(Binder::dummy(
|
cx.tcx
|
||||||
PredicateKind::Clause(Clause::Projection(p.with_self_ty(cx.tcx, ty))),
|
.mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Projection(
|
||||||
)),
|
p.with_self_ty(cx.tcx, ty),
|
||||||
|
)))),
|
||||||
)),
|
)),
|
||||||
ExistentialPredicate::AutoTrait(p) => infcx
|
ExistentialPredicate::AutoTrait(p) => infcx
|
||||||
.type_implements_trait(p, [ty], cx.param_env)
|
.type_implements_trait(p, [ty], cx.param_env)
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use clippy_utils::consts::{constant, Constant};
|
use clippy_utils::consts::{constant, Constant};
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
|
||||||
use clippy_utils::higher;
|
use clippy_utils::higher;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
|
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
|
||||||
use clippy_utils::sugg::Sugg;
|
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 if_chain::if_chain;
|
||||||
use rustc_ast::ast::RangeLimits;
|
use rustc_ast::ast::RangeLimits;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
|
use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::source_map::{Span, Spanned};
|
use rustc_span::source_map::{Span, Spanned};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
@ -161,12 +161,12 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Ranges {
|
pub struct Ranges {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ranges {
|
impl Ranges {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ impl_lint_pass!(Ranges => [
|
||||||
impl<'tcx> LateLintPass<'tcx> for Ranges {
|
impl<'tcx> LateLintPass<'tcx> for Ranges {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
if let ExprKind::Binary(ref op, l, r) = expr.kind {
|
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);
|
check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,8 +81,8 @@ impl EarlyLintPass for RedundantClosureCall {
|
||||||
"try not to call a closure in the expression where it is declared",
|
"try not to call a closure in the expression where it is declared",
|
||||||
|diag| {
|
|diag| {
|
||||||
if fn_decl.inputs.is_empty() {
|
if fn_decl.inputs.is_empty() {
|
||||||
let app = Applicability::MachineApplicable;
|
let mut app = Applicability::MachineApplicable;
|
||||||
let mut hint = Sugg::ast(cx, body, "..");
|
let mut hint = Sugg::ast(cx, body, "..", closure.span.ctxt(), &mut app);
|
||||||
|
|
||||||
if asyncness.is_async() {
|
if asyncness.is_async() {
|
||||||
// `async x` is a syntax error, so it becomes `async { x }`
|
// `async x` is a syntax error, so it becomes `async { x }`
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
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_ast::ast::{Expr, ExprKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -37,12 +36,12 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RedundantFieldNames {
|
pub struct RedundantFieldNames {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RedundantFieldNames {
|
impl RedundantFieldNames {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +50,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
|
||||||
|
|
||||||
impl EarlyLintPass for RedundantFieldNames {
|
impl EarlyLintPass for RedundantFieldNames {
|
||||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::{meets_msrv, msrvs};
|
|
||||||
use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
|
use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -34,12 +33,12 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RedundantStaticLifetimes {
|
pub struct RedundantStaticLifetimes {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RedundantStaticLifetimes {
|
impl RedundantStaticLifetimes {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +95,7 @@ impl RedundantStaticLifetimes {
|
||||||
|
|
||||||
impl EarlyLintPass for RedundantStaticLifetimes {
|
impl EarlyLintPass for RedundantStaticLifetimes {
|
||||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_middle::ty::subst::GenericArgKind;
|
use rustc_middle::ty::subst::GenericArgKind;
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::source_map::Span;
|
use rustc_span::source_map::Span;
|
||||||
|
use rustc_span::{BytePos, Pos};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
@ -209,13 +210,14 @@ fn check_final_expr<'tcx>(
|
||||||
if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
|
if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
|
||||||
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
|
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
|
||||||
if !borrows {
|
if !borrows {
|
||||||
emit_return_lint(
|
// check if expr return nothing
|
||||||
cx,
|
let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
|
||||||
peeled_drop_expr.span,
|
extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
|
||||||
semi_spans,
|
} else {
|
||||||
inner.as_ref().map(|i| i.span),
|
peeled_drop_expr.span
|
||||||
replacement,
|
};
|
||||||
);
|
|
||||||
|
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()
|
.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
|
||||||
|
}
|
||||||
|
|
|
@ -16,10 +16,10 @@ mod utils;
|
||||||
mod wrong_transmute;
|
mod wrong_transmute;
|
||||||
|
|
||||||
use clippy_utils::in_constant;
|
use clippy_utils::in_constant;
|
||||||
|
use clippy_utils::msrvs::Msrv;
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_hir::{Expr, ExprKind, QPath};
|
use rustc_hir::{Expr, ExprKind, QPath};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
|
||||||
|
@ -410,7 +410,7 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Transmute {
|
pub struct Transmute {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
impl_lint_pass!(Transmute => [
|
impl_lint_pass!(Transmute => [
|
||||||
CROSSPOINTER_TRANSMUTE,
|
CROSSPOINTER_TRANSMUTE,
|
||||||
|
@ -431,7 +431,7 @@ impl_lint_pass!(Transmute => [
|
||||||
]);
|
]);
|
||||||
impl Transmute {
|
impl Transmute {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -461,7 +461,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
|
||||||
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
|
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
|
||||||
| crosspointer_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)
|
| 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_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_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)
|
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use super::TRANSMUTE_PTR_TO_REF;
|
use super::TRANSMUTE_PTR_TO_REF;
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::{meets_msrv, msrvs, sugg};
|
use clippy_utils::sugg;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind};
|
use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::{self, Ty, TypeVisitable};
|
use rustc_middle::ty::{self, Ty, TypeVisitable};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
|
|
||||||
/// Checks for `transmute_ptr_to_ref` lint.
|
/// Checks for `transmute_ptr_to_ref` lint.
|
||||||
/// Returns `true` if it's triggered, otherwise returns `false`.
|
/// Returns `true` if it's triggered, otherwise returns `false`.
|
||||||
|
@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
arg: &'tcx Expr<'_>,
|
arg: &'tcx Expr<'_>,
|
||||||
path: &'tcx Path<'_>,
|
path: &'tcx Path<'_>,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match (&from_ty.kind(), &to_ty.kind()) {
|
match (&from_ty.kind(), &to_ty.kind()) {
|
||||||
(ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => {
|
(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 sugg = if let Some(ty) = get_explicit_type(path) {
|
||||||
let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app);
|
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())
|
format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par())
|
||||||
} else if from_ptr_ty.has_erased_regions() {
|
} else if from_ptr_ty.has_erased_regions() {
|
||||||
sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string()
|
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 {
|
} else if from_ptr_ty.ty == *to_ref_ty {
|
||||||
if from_ptr_ty.has_erased_regions() {
|
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())
|
format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par())
|
||||||
} else {
|
} else {
|
||||||
sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}")))
|
sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}")))
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
use clippy_utils::source::walk_span_to_context;
|
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 clippy_utils::{get_parent_node, is_lint_allowed};
|
||||||
|
use hir::HirId;
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
|
use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
|
||||||
|
@ -59,11 +63,39 @@ declare_clippy_lint! {
|
||||||
restriction,
|
restriction,
|
||||||
"creating an unsafe block without explaining why it is safe"
|
"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 {
|
impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
|
||||||
fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
|
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
|
||||||
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
||||||
&& !in_external_macro(cx.tcx.sess, block.span)
|
&& !in_external_macro(cx.tcx.sess, block.span)
|
||||||
&& !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
|
&& !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
|
||||||
|
@ -87,15 +119,69 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
|
||||||
"consider adding a safety comment on the preceding line",
|
"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 (
|
||||||
|
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<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||||
if let hir::ItemKind::Impl(imple) = item.kind
|
if in_external_macro(cx.tcx.sess, item.span) {
|
||||||
&& imple.unsafety == hir::Unsafety::Unsafe
|
return;
|
||||||
&& !in_external_macro(cx.tcx.sess, item.span)
|
}
|
||||||
&& !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
|
|
||||||
|
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)
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
&& !is_unsafe_from_proc_macro(cx, item.span)
|
||||||
&& !item_has_safety_comment(cx, item)
|
|
||||||
{
|
{
|
||||||
let source_map = cx.tcx.sess.source_map();
|
let source_map = cx.tcx.sess.source_map();
|
||||||
let span = if source_map.is_multiline(item.span) {
|
let span = if source_map.is_multiline(item.span) {
|
||||||
|
@ -113,7 +199,93 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
|
||||||
"consider adding a safety comment on the preceding line",
|
"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<Span> {
|
||||||
|
// 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 {
|
fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool {
|
||||||
|
@ -170,47 +342,53 @@ 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
|
// won't work. This is to avoid dealing with where such a comment should be place relative to
|
||||||
// attributes and doc comments.
|
// 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.
|
/// Checks if the lines immediately preceding the item contain a safety comment.
|
||||||
#[allow(clippy::collapsible_match)]
|
#[allow(clippy::collapsible_match)]
|
||||||
fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool {
|
fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment {
|
||||||
if span_from_macro_expansion_has_safety_comment(cx, item.span) {
|
match span_from_macro_expansion_has_safety_comment(cx, item.span) {
|
||||||
return true;
|
HasSafetyComment::Maybe => (),
|
||||||
|
has_safety_comment => return has_safety_comment,
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.span.ctxt() == SyntaxContext::root() {
|
if item.span.ctxt() != SyntaxContext::root() {
|
||||||
|
return HasSafetyComment::No;
|
||||||
|
}
|
||||||
if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
|
if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
|
||||||
let comment_start = match parent_node {
|
let comment_start = match parent_node {
|
||||||
Node::Crate(parent_mod) => {
|
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) => {
|
Node::Item(parent_item) => {
|
||||||
if let ItemKind::Mod(parent_mod) = &parent_item.kind {
|
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 {
|
} else {
|
||||||
// Doesn't support impls in this position. Pretend a comment was found.
|
// Doesn't support impls in this position. Pretend a comment was found.
|
||||||
return true;
|
return HasSafetyComment::Maybe;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Node::Stmt(stmt) => {
|
Node::Stmt(stmt) => {
|
||||||
if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) {
|
if let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) {
|
||||||
match stmt_parent {
|
walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo)
|
||||||
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 {
|
} else {
|
||||||
// Problem getting the parent node. Pretend a comment was found.
|
// 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.
|
// Doesn't support impls in this position. Pretend a comment was found.
|
||||||
return true;
|
return HasSafetyComment::Maybe;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -221,34 +399,77 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool {
|
||||||
&& Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
|
&& Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
|
||||||
&& let Some(src) = unsafe_line.sf.src.as_deref()
|
&& let Some(src) = unsafe_line.sf.src.as_deref()
|
||||||
{
|
{
|
||||||
unsafe_line.sf.lines(|lines| {
|
return unsafe_line.sf.lines(|lines| {
|
||||||
comment_start_line.line < unsafe_line.line && text_has_safety_comment(
|
if comment_start_line.line >= unsafe_line.line {
|
||||||
|
HasSafetyComment::No
|
||||||
|
} else {
|
||||||
|
match text_has_safety_comment(
|
||||||
src,
|
src,
|
||||||
&lines[comment_start_line.line + 1..=unsafe_line.line],
|
&lines[comment_start_line.line + 1..=unsafe_line.line],
|
||||||
unsafe_line.sf.start_pos.to_usize(),
|
unsafe_line.sf.start_pos.to_usize(),
|
||||||
)
|
) {
|
||||||
})
|
Some(b) => HasSafetyComment::Yes(b),
|
||||||
} else {
|
None => HasSafetyComment::No,
|
||||||
// Problem getting source text. Pretend a comment was found.
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// No parent node. Pretend a comment was found.
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
HasSafetyComment::Maybe
|
||||||
|
}
|
||||||
|
|
||||||
fn comment_start_before_impl_in_mod(
|
/// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HasSafetyComment::Maybe
|
||||||
|
}
|
||||||
|
|
||||||
|
fn comment_start_before_item_in_mod(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
parent_mod: &hir::Mod<'_>,
|
parent_mod: &hir::Mod<'_>,
|
||||||
parent_mod_span: Span,
|
parent_mod_span: Span,
|
||||||
imple: &hir::Item<'_>,
|
item: &hir::Item<'_>,
|
||||||
) -> Option<BytePos> {
|
) -> Option<BytePos> {
|
||||||
parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| {
|
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 {
|
if idx == 0 {
|
||||||
// mod A { /* comment */ unsafe impl T {} ... }
|
// mod A { /* comment */ unsafe impl T {} ... }
|
||||||
// ^------------------------------------------^ returns the start of this span
|
// ^------------------------------------------^ 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 source_map = cx.sess().source_map();
|
||||||
let ctxt = span.ctxt();
|
let ctxt = span.ctxt();
|
||||||
if ctxt == SyntaxContext::root() {
|
if ctxt == SyntaxContext::root() {
|
||||||
false
|
HasSafetyComment::Maybe
|
||||||
} else {
|
} else {
|
||||||
// From a macro expansion. Get the text from the start of the macro declaration to start of the
|
// From a macro expansion. Get the text from the start of the macro declaration to start of the
|
||||||
// unsafe block.
|
// 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()
|
&& let Some(src) = unsafe_line.sf.src.as_deref()
|
||||||
{
|
{
|
||||||
unsafe_line.sf.lines(|lines| {
|
unsafe_line.sf.lines(|lines| {
|
||||||
macro_line.line < unsafe_line.line && text_has_safety_comment(
|
if macro_line.line < unsafe_line.line {
|
||||||
|
match text_has_safety_comment(
|
||||||
src,
|
src,
|
||||||
&lines[macro_line.line + 1..=unsafe_line.line],
|
&lines[macro_line.line + 1..=unsafe_line.line],
|
||||||
unsafe_line.sf.start_pos.to_usize(),
|
unsafe_line.sf.start_pos.to_usize(),
|
||||||
)
|
) {
|
||||||
|
Some(b) => HasSafetyComment::Yes(b),
|
||||||
|
None => HasSafetyComment::No,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HasSafetyComment::No
|
||||||
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Problem getting source text. Pretend a comment was found.
|
// 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,
|
src,
|
||||||
&lines[body_line.line + 1..=unsafe_line.line],
|
&lines[body_line.line + 1..=unsafe_line.line],
|
||||||
unsafe_line.sf.start_pos.to_usize(),
|
unsafe_line.sf.start_pos.to_usize(),
|
||||||
)
|
).is_some()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Problem getting source text. Pretend a comment was found.
|
// 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.
|
/// 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<BytePos> {
|
||||||
let mut lines = line_starts
|
let mut lines = line_starts
|
||||||
.array_windows::<2>()
|
.array_windows::<2>()
|
||||||
.rev()
|
.rev()
|
||||||
.map_while(|[start, end]| {
|
.map_while(|[start, end]| {
|
||||||
let start = start.to_usize() - offset;
|
let start = start.to_usize() - offset;
|
||||||
let end = end.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());
|
.filter(|(_, text)| !text.is_empty());
|
||||||
|
|
||||||
let Some((line_start, line)) = lines.next() else {
|
let Some((line_start, line)) = lines.next() else {
|
||||||
return false;
|
return None;
|
||||||
};
|
};
|
||||||
// Check for a sequence of line comments.
|
// Check for a sequence of line comments.
|
||||||
if line.starts_with("//") {
|
if line.starts_with("//") {
|
||||||
let mut line = line;
|
let (mut line, mut line_start) = (line, line_start);
|
||||||
loop {
|
loop {
|
||||||
if line.to_ascii_uppercase().contains("SAFETY:") {
|
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() {
|
match lines.next() {
|
||||||
Some((_, x)) if x.starts_with("//") => line = x,
|
Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s),
|
||||||
_ => return false,
|
_ => 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);
|
let (mut line_start, mut line) = (line_start, line);
|
||||||
loop {
|
loop {
|
||||||
if line.starts_with("/*") {
|
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);
|
let mut tokens = tokenize(src);
|
||||||
return src[..tokens.next().unwrap().len as usize]
|
return (src[..tokens.next().unwrap().len as usize]
|
||||||
.to_ascii_uppercase()
|
.to_ascii_uppercase()
|
||||||
.contains("SAFETY:")
|
.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() {
|
match lines.next() {
|
||||||
Some(x) => (line_start, line) = x,
|
Some(x) => (line_start, line) = x,
|
||||||
None => return false,
|
None => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
|
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::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::mut_visit::*;
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
|
use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::DUMMY_SP;
|
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)`"
|
"unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct UnnestedOrPatterns {
|
pub struct UnnestedOrPatterns {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnnestedOrPatterns {
|
impl UnnestedOrPatterns {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self { msrv }
|
Self { msrv }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,13 +60,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]);
|
||||||
|
|
||||||
impl EarlyLintPass for UnnestedOrPatterns {
|
impl EarlyLintPass for UnnestedOrPatterns {
|
||||||
fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
|
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);
|
lint_unnested_or_patterns(cx, &a.pat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
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 {
|
if let ast::ExprKind::Let(pat, _, _) = &e.kind {
|
||||||
lint_unnested_or_patterns(cx, pat);
|
lint_unnested_or_patterns(cx, pat);
|
||||||
}
|
}
|
||||||
|
@ -75,13 +74,13 @@ impl EarlyLintPass for UnnestedOrPatterns {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
|
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);
|
lint_unnested_or_patterns(cx, &p.pat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
|
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);
|
lint_unnested_or_patterns(cx, &l.pat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::source::snippet;
|
||||||
use rustc_ast::ast::{Expr, ExprKind, MethodCall};
|
use rustc_ast::ast::{Expr, ExprKind, MethodCall};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||||
|
@ -29,22 +30,16 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]);
|
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
|
if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind
|
||||||
&& let method_name = name_ident.ident.name.as_str()
|
&& let method_name = name_ident.ident.name.as_str()
|
||||||
&& (method_name == "ceil" || method_name == "round" || method_name == "floor")
|
&& (method_name == "ceil" || method_name == "round" || method_name == "floor")
|
||||||
&& let ExprKind::Lit(token_lit) = &receiver.kind
|
&& let ExprKind::Lit(token_lit) = &receiver.kind
|
||||||
&& token_lit.is_semantic_float() {
|
&& token_lit.is_semantic_float()
|
||||||
let mut f_str = token_lit.symbol.to_string();
|
&& let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::<f64>() {
|
||||||
let f = f_str.trim_end_matches('_').parse::<f64>().unwrap();
|
(f.fract() == 0.0).then(||
|
||||||
if let Some(suffix) = token_lit.suffix {
|
(method_name, snippet(cx, receiver.span, "..").to_string())
|
||||||
f_str.push_str(suffix.as_str());
|
)
|
||||||
}
|
|
||||||
if f.fract() == 0.0 {
|
|
||||||
Some((method_name, f_str))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -52,7 +47,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
|
||||||
|
|
||||||
impl EarlyLintPass for UnusedRounding {
|
impl EarlyLintPass for UnusedRounding {
|
||||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
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(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
UNUSED_ROUNDING,
|
UNUSED_ROUNDING,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
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::ty::same_type_and_consts;
|
||||||
use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs};
|
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -14,7 +15,6 @@ use rustc_hir::{
|
||||||
};
|
};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
use rustc_hir_analysis::hir_ty_to_ty;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
@ -57,13 +57,13 @@ declare_clippy_lint! {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct UseSelf {
|
pub struct UseSelf {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
stack: Vec<StackItem>,
|
stack: Vec<StackItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UseSelf {
|
impl UseSelf {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
Self {
|
Self {
|
||||||
msrv,
|
msrv,
|
||||||
..Self::default()
|
..Self::default()
|
||||||
|
@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
||||||
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
|
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if !hir_ty.span.from_expansion();
|
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 {
|
if let Some(&StackItem::Check {
|
||||||
impl_id,
|
impl_id,
|
||||||
in_body,
|
in_body,
|
||||||
|
@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if !expr.span.from_expansion();
|
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 let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
|
||||||
if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
|
if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
|
||||||
then {} else { return; }
|
then {} else { return; }
|
||||||
|
@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
||||||
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
|
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if !pat.span.from_expansion();
|
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();
|
if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
|
||||||
// get the path from the pattern
|
// get the path from the pattern
|
||||||
if let PatKind::Path(QPath::Resolved(_, path))
|
if let PatKind::Path(QPath::Resolved(_, path))
|
||||||
|
|
|
@ -402,6 +402,10 @@ define_Conf! {
|
||||||
/// A list of paths to types that should be treated like `Arc`, i.e. ignored but
|
/// A list of paths to types that should be treated like `Arc`, i.e. ignored but
|
||||||
/// for the generic parameters for determining interior mutability
|
/// for the generic parameters for determining interior mutability
|
||||||
(ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])),
|
(ignore_interior_mutability: Vec<String> = 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.
|
/// Search for the configuration file.
|
||||||
|
|
|
@ -41,7 +41,7 @@ impl LateLintPass<'_> for MsrvAttrImpl {
|
||||||
.type_of(f.did)
|
.type_of(f.did)
|
||||||
.walk()
|
.walk()
|
||||||
.filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
|
.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));
|
if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
|
||||||
then {
|
then {
|
||||||
|
|
|
@ -125,19 +125,19 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
|
pub fn get_unique_attr<'a>(
|
||||||
let mut unique_attr = None;
|
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) {
|
for attr in get_attr(sess, attrs, name) {
|
||||||
match attr.style {
|
if let Some(duplicate) = unique_attr {
|
||||||
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"))
|
sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
|
||||||
.span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
|
.span_note(duplicate.span, "first definition found here")
|
||||||
.emit();
|
.emit();
|
||||||
},
|
} else {
|
||||||
ast::AttrStyle::Outer => {
|
unique_attr = Some(attr);
|
||||||
sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute"));
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unique_attr
|
unique_attr
|
||||||
|
|
|
@ -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)]
|
#[expect(clippy::too_many_lines)]
|
||||||
fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
|
fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
|
||||||
struct V<'cx, 'tcx> {
|
struct V<'cx, 'tcx> {
|
||||||
|
@ -113,13 +123,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
||||||
},
|
},
|
||||||
args,
|
args,
|
||||||
) => match self.cx.qpath_res(path, hir_id) {
|
) => match self.cx.qpath_res(path, hir_id) {
|
||||||
Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => {
|
res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => {
|
||||||
if self
|
if res_has_significant_drop(res, self.cx, e) {
|
||||||
.cx
|
|
||||||
.typeck_results()
|
|
||||||
.expr_ty(e)
|
|
||||||
.has_significant_drop(self.cx.tcx, self.cx.param_env)
|
|
||||||
{
|
|
||||||
self.eagerness = ForceNoChange;
|
self.eagerness = ForceNoChange;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -147,6 +152,12 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
||||||
self.eagerness |= NoChange;
|
self.eagerness |= NoChange;
|
||||||
return;
|
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, ..) => {
|
ExprKind::MethodCall(name, ..) => {
|
||||||
self.eagerness |= self
|
self.eagerness |= self
|
||||||
.cx
|
.cx
|
||||||
|
@ -206,7 +217,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
||||||
| ExprKind::Match(..)
|
| ExprKind::Match(..)
|
||||||
| ExprKind::Closure { .. }
|
| ExprKind::Closure { .. }
|
||||||
| ExprKind::Field(..)
|
| ExprKind::Field(..)
|
||||||
| ExprKind::Path(_)
|
|
||||||
| ExprKind::AddrOf(..)
|
| ExprKind::AddrOf(..)
|
||||||
| ExprKind::Struct(..)
|
| ExprKind::Struct(..)
|
||||||
| ExprKind::Repeat(..)
|
| ExprKind::Repeat(..)
|
||||||
|
|
|
@ -105,8 +105,6 @@ use rustc_middle::ty::{
|
||||||
layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
|
layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{FloatTy, IntTy, UintTy};
|
use rustc_middle::ty::{FloatTy, IntTy, UintTy};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
use rustc_session::Session;
|
|
||||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||||
use rustc_span::source_map::SourceMap;
|
use rustc_span::source_map::SourceMap;
|
||||||
use rustc_span::sym;
|
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::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 crate::visitors::for_each_expr;
|
||||||
|
|
||||||
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
|
|
||||||
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<RustcVersion>, lint_msrv: RustcVersion) -> bool {
|
|
||||||
msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! extract_msrv_attr {
|
macro_rules! extract_msrv_attr {
|
||||||
($context:ident) => {
|
($context:ident) => {
|
||||||
fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
|
fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
|
||||||
let sess = rustc_lint::LintContext::sess(cx);
|
let sess = rustc_lint::LintContext::sess(cx);
|
||||||
match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
|
self.msrv.enter_lint_attrs(sess, attrs);
|
||||||
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");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use rustc_ast::Attribute;
|
||||||
use rustc_semver::RustcVersion;
|
use rustc_semver::RustcVersion;
|
||||||
|
use rustc_session::Session;
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
use crate::attrs::get_unique_attr;
|
||||||
|
|
||||||
macro_rules! msrv_aliases {
|
macro_rules! msrv_aliases {
|
||||||
($($major:literal,$minor:literal,$patch:literal {
|
($($major:literal,$minor:literal,$patch:literal {
|
||||||
|
@ -40,3 +47,97 @@ msrv_aliases! {
|
||||||
1,16,0 { STR_REPEAT }
|
1,16,0 { STR_REPEAT }
|
||||||
1,55,0 { SEEK_REWIND }
|
1,55,0 { SEEK_REWIND }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
|
||||||
|
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<RustcVersion>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Msrv {
|
||||||
|
fn new(initial: Option<RustcVersion>) -> Self {
|
||||||
|
Self {
|
||||||
|
stack: Vec::from_iter(initial),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_inner(conf_msrv: &Option<String>, 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<String>, sess: &Session) -> &'static Self {
|
||||||
|
static PARSED: OnceLock<Msrv> = OnceLock::new();
|
||||||
|
|
||||||
|
PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current(&self) -> Option<RustcVersion> {
|
||||||
|
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<RustcVersion> {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -60,6 +60,8 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
|
||||||
#[cfg(feature = "internal")]
|
#[cfg(feature = "internal")]
|
||||||
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
|
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
|
||||||
pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
|
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 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_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 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"];
|
pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
|
||||||
#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
|
#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
|
||||||
pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
|
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: [&str; 3] = ["core", "intrinsics", "copy"];
|
||||||
pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
|
pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
|
||||||
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
|
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_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_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
|
||||||
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "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_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
|
||||||
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
|
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
|
||||||
pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
|
pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
|
// 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.
|
// 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 as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_middle::mir::{
|
use rustc_middle::mir::{
|
||||||
|
@ -18,20 +19,22 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
||||||
|
|
||||||
pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option<RustcVersion>) -> 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 def_id = body.source.def_id();
|
||||||
let mut current = def_id;
|
let mut current = def_id;
|
||||||
loop {
|
loop {
|
||||||
let predicates = tcx.predicates_of(current);
|
let predicates = tcx.predicates_of(current);
|
||||||
for (predicate, _) in predicates.predicates {
|
for (predicate, _) in predicates.predicates {
|
||||||
match predicate.kind().skip_binder() {
|
match predicate.kind().skip_binder() {
|
||||||
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_))
|
ty::PredicateKind::Clause(
|
||||||
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_))
|
ty::Clause::RegionOutlives(_)
|
||||||
|
| ty::Clause::TypeOutlives(_)
|
||||||
|
| ty::Clause::Projection(_)
|
||||||
|
| ty::Clause::Trait(..),
|
||||||
|
)
|
||||||
| ty::PredicateKind::WellFormed(_)
|
| ty::PredicateKind::WellFormed(_)
|
||||||
| ty::PredicateKind::Clause(ty::Clause::Projection(_))
|
|
||||||
| ty::PredicateKind::ConstEvaluatable(..)
|
| ty::PredicateKind::ConstEvaluatable(..)
|
||||||
| ty::PredicateKind::ConstEquate(..)
|
| ty::PredicateKind::ConstEquate(..)
|
||||||
| ty::PredicateKind::Clause(ty::Clause::Trait(..))
|
|
||||||
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
|
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
|
||||||
ty::PredicateKind::ObjectSafe(_) => panic!("object safe 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::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"),
|
||||||
|
@ -281,7 +284,7 @@ fn check_terminator<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
body: &Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
terminator: &Terminator<'tcx>,
|
terminator: &Terminator<'tcx>,
|
||||||
msrv: Option<RustcVersion>,
|
msrv: &Msrv,
|
||||||
) -> McfResult {
|
) -> McfResult {
|
||||||
let span = terminator.source_info.span;
|
let span = terminator.source_info.span;
|
||||||
match &terminator.kind {
|
match &terminator.kind {
|
||||||
|
@ -365,7 +368,7 @@ fn check_terminator<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
|
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
|
||||||
tcx.is_const_fn(def_id)
|
tcx.is_const_fn(def_id)
|
||||||
&& tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
|
&& tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
|
||||||
if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level {
|
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<RustcVersion>) -> bo
|
||||||
|
|
||||||
let since = rustc_span::Symbol::intern(short_version);
|
let since = rustc_span::Symbol::intern(short_version);
|
||||||
|
|
||||||
crate::meets_msrv(
|
msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
|
||||||
msrv,
|
|
||||||
RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
|
|
||||||
panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
|
panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
|
||||||
}),
|
}))
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
// Unstable const fn with the feature enabled.
|
// Unstable const fn with the feature enabled.
|
||||||
msrv.is_none()
|
msrv.current().is_none()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, ExprKind};
|
use rustc_hir::{Expr, ExprKind};
|
||||||
use rustc_lint::{LateContext, LintContext};
|
use rustc_lint::{LateContext, LintContext};
|
||||||
|
use rustc_session::Session;
|
||||||
use rustc_span::hygiene;
|
use rustc_span::hygiene;
|
||||||
use rustc_span::source_map::{original_sp, SourceMap};
|
use rustc_span::source_map::{original_sp, SourceMap};
|
||||||
use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
|
use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
|
||||||
|
@ -204,11 +205,20 @@ pub fn snippet_with_applicability<'a, T: LintContext>(
|
||||||
span: Span,
|
span: Span,
|
||||||
default: &'a str,
|
default: &'a str,
|
||||||
applicability: &mut Applicability,
|
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> {
|
) -> Cow<'a, str> {
|
||||||
if *applicability != Applicability::Unspecified && span.from_expansion() {
|
if *applicability != Applicability::Unspecified && span.from_expansion() {
|
||||||
*applicability = Applicability::MaybeIncorrect;
|
*applicability = Applicability::MaybeIncorrect;
|
||||||
}
|
}
|
||||||
snippet_opt(cx, span).map_or_else(
|
snippet_opt_sess(sess, span).map_or_else(
|
||||||
|| {
|
|| {
|
||||||
if *applicability == Applicability::MachineApplicable {
|
if *applicability == Applicability::MachineApplicable {
|
||||||
*applicability = Applicability::HasPlaceholders;
|
*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.
|
/// Converts a span to a code snippet. Returns `None` if not available.
|
||||||
pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
|
||||||
cx.sess().source_map().span_to_snippet(span).ok()
|
snippet_opt_sess(cx.sess(), span)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
|
||||||
|
sess.source_map().span_to_snippet(span).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a span (from a block) to a code snippet if available, otherwise use default.
|
/// 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
|
/// Same as `snippet_block`, but adapts the applicability level by the rules of
|
||||||
/// `snippet_with_applicability`.
|
/// `snippet_with_applicability`.
|
||||||
pub fn snippet_block_with_applicability<'a, T: LintContext>(
|
pub fn snippet_block_with_applicability<'a>(
|
||||||
cx: &T,
|
cx: &impl LintContext,
|
||||||
span: Span,
|
span: Span,
|
||||||
default: &'a str,
|
default: &'a str,
|
||||||
indent_relative_to: Option<Span>,
|
indent_relative_to: Option<Span>,
|
||||||
|
@ -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.
|
/// This will also return whether or not the snippet is a macro call.
|
||||||
pub fn snippet_with_context<'a>(
|
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,
|
span: Span,
|
||||||
outer: SyntaxContext,
|
outer: SyntaxContext,
|
||||||
default: &'a str,
|
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,
|
is_macro_call,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,25 +176,28 @@ impl<'a> Sugg<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepare a suggestion from an expression.
|
/// 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;
|
use rustc_ast::ast::RangeLimits;
|
||||||
|
|
||||||
let snippet_without_expansion = |cx, span: Span, default| {
|
#[expect(clippy::match_wildcard_for_single_variants)]
|
||||||
if span.from_expansion() {
|
|
||||||
snippet_with_macro_callsite(cx, span, default)
|
|
||||||
} else {
|
|
||||||
snippet(cx, span, default)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match expr.kind {
|
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::AddrOf(..)
|
||||||
| ast::ExprKind::Box(..)
|
| ast::ExprKind::Box(..)
|
||||||
| ast::ExprKind::Closure { .. }
|
| ast::ExprKind::Closure { .. }
|
||||||
| ast::ExprKind::If(..)
|
| ast::ExprKind::If(..)
|
||||||
| ast::ExprKind::Let(..)
|
| ast::ExprKind::Let(..)
|
||||||
| ast::ExprKind::Unary(..)
|
| 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::Async(..)
|
||||||
| ast::ExprKind::Block(..)
|
| ast::ExprKind::Block(..)
|
||||||
| ast::ExprKind::Break(..)
|
| ast::ExprKind::Break(..)
|
||||||
|
@ -224,45 +227,49 @@ impl<'a> Sugg<'a> {
|
||||||
| ast::ExprKind::Array(..)
|
| ast::ExprKind::Array(..)
|
||||||
| ast::ExprKind::While(..)
|
| ast::ExprKind::While(..)
|
||||||
| ast::ExprKind::Await(..)
|
| 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(
|
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
|
||||||
AssocOp::DotDot,
|
AssocOp::DotDot,
|
||||||
lhs.as_ref()
|
lhs.as_ref().map_or("".into(), |lhs| {
|
||||||
.map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
|
snippet_with_context(cx, lhs.span, ctxt, default, app).0
|
||||||
rhs.as_ref()
|
}),
|
||||||
.map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
|
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(
|
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
|
||||||
AssocOp::DotDotEq,
|
AssocOp::DotDotEq,
|
||||||
lhs.as_ref()
|
lhs.as_ref().map_or("".into(), |lhs| {
|
||||||
.map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
|
snippet_with_context(cx, lhs.span, ctxt, default, app).0
|
||||||
rhs.as_ref()
|
}),
|
||||||
.map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
|
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(
|
ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
|
||||||
AssocOp::Assign,
|
AssocOp::Assign,
|
||||||
snippet_without_expansion(cx, lhs.span, default),
|
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||||
snippet_without_expansion(cx, rhs.span, default),
|
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||||
),
|
),
|
||||||
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
|
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
|
||||||
astbinop2assignop(op),
|
astbinop2assignop(op),
|
||||||
snippet_without_expansion(cx, lhs.span, default),
|
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||||
snippet_without_expansion(cx, rhs.span, default),
|
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||||
),
|
),
|
||||||
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
|
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
|
||||||
AssocOp::from_ast_binop(op.node),
|
AssocOp::from_ast_binop(op.node),
|
||||||
snippet_without_expansion(cx, lhs.span, default),
|
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||||
snippet_without_expansion(cx, rhs.span, default),
|
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||||
),
|
),
|
||||||
ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
|
ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
|
||||||
AssocOp::As,
|
AssocOp::As,
|
||||||
snippet_without_expansion(cx, lhs.span, default),
|
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||||
snippet_without_expansion(cx, ty.span, default),
|
snippet_with_context(cx, ty.span, ctxt, default, app).0,
|
||||||
),
|
),
|
||||||
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
|
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
|
||||||
AssocOp::Colon,
|
AssocOp::Colon,
|
||||||
snippet_without_expansion(cx, lhs.span, default),
|
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||||
snippet_without_expansion(cx, ty.span, default),
|
snippet_with_context(cx, ty.span, ctxt, default, app).0,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,10 @@ use rustc_hir as hir;
|
||||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
|
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_lint::LateContext;
|
||||||
use rustc_middle::mir::interpret::{ConstValue, Scalar};
|
use rustc_middle::mir::interpret::{ConstValue, Scalar};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
|
@ -189,7 +192,13 @@ pub fn implements_trait<'tcx>(
|
||||||
trait_id: DefId,
|
trait_id: DefId,
|
||||||
ty_params: &[GenericArg<'tcx>],
|
ty_params: &[GenericArg<'tcx>],
|
||||||
) -> bool {
|
) -> 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.
|
/// 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,
|
kind: TypeVariableOriginKind::MiscVariable,
|
||||||
span: DUMMY_SP,
|
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
|
infcx
|
||||||
.type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
|
.type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
|
||||||
.must_apply_modulo_regions()
|
.must_apply_modulo_regions()
|
||||||
|
@ -712,7 +725,9 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
|
||||||
}
|
}
|
||||||
inputs = Some(i);
|
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() {
|
if output.is_some() {
|
||||||
// Multiple different fn trait impls. Is this even allowed?
|
// Multiple different fn trait impls. Is this even allowed?
|
||||||
return None;
|
return None;
|
||||||
|
@ -992,14 +1007,12 @@ pub fn make_projection<'tcx>(
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
generic_count == substs.len(),
|
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\
|
note: the expected parameters are: {:#?}\n\
|
||||||
the given arguments are: `{:#?}`",
|
the given arguments are: `{substs:#?}`",
|
||||||
assoc_item.def_id,
|
assoc_item.def_id,
|
||||||
substs.len(),
|
substs.len(),
|
||||||
generic_count,
|
|
||||||
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
|
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
|
||||||
substs,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some((idx, (param, arg))) = params
|
if let Some((idx, (param, arg))) = params
|
||||||
|
@ -1017,14 +1030,11 @@ pub fn make_projection<'tcx>(
|
||||||
{
|
{
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
false,
|
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\
|
note: the expected parameters are {:#?}\n\
|
||||||
the given arguments are {:#?}",
|
the given arguments are {substs:#?}",
|
||||||
idx,
|
|
||||||
param.descr(),
|
param.descr(),
|
||||||
arg,
|
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>()
|
||||||
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
|
|
||||||
substs,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,22 +170,22 @@ where
|
||||||
cb: F,
|
cb: F,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WithStmtGuarg<'a, F> {
|
struct WithStmtGuard<'a, F> {
|
||||||
val: &'a mut RetFinder<F>,
|
val: &'a mut RetFinder<F>,
|
||||||
prev_in_stmt: bool,
|
prev_in_stmt: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> RetFinder<F> {
|
impl<F> RetFinder<F> {
|
||||||
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);
|
let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
|
||||||
WithStmtGuarg {
|
WithStmtGuard {
|
||||||
val: self,
|
val: self,
|
||||||
prev_in_stmt,
|
prev_in_stmt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
|
impl<F> std::ops::Deref for WithStmtGuard<'_, F> {
|
||||||
type Target = RetFinder<F>;
|
type Target = RetFinder<F>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
|
@ -193,13 +193,13 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
|
impl<F> std::ops::DerefMut for WithStmtGuard<'_, F> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
self.val
|
self.val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> Drop for WithStmtGuarg<'_, F> {
|
impl<F> Drop for WithStmtGuard<'_, F> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.val.in_stmt = self.prev_in_stmt;
|
self.val.in_stmt = self.prev_in_stmt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,8 +120,8 @@ impl ClippyWarning {
|
||||||
format!("$CARGO_HOME/{}", stripped.display())
|
format!("$CARGO_HOME/{}", stripped.display())
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"target/lintcheck/sources/{}-{}/{}",
|
"target/lintcheck/sources/{crate_name}-{crate_version}/{}",
|
||||||
crate_name, crate_version, span.file_name
|
span.file_name
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -322,13 +322,13 @@ impl Crate {
|
||||||
|
|
||||||
if config.max_jobs == 1 {
|
if config.max_jobs == 1 {
|
||||||
println!(
|
println!(
|
||||||
"{}/{} {}% Linting {} {}",
|
"{index}/{total_crates_to_lint} {perc}% Linting {} {}",
|
||||||
index, total_crates_to_lint, perc, &self.name, &self.version
|
&self.name, &self.version
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"{}/{} {}% Linting {} {} in target dir {:?}",
|
"{index}/{total_crates_to_lint} {perc}% Linting {} {} in target dir {thread_index:?}",
|
||||||
index, total_crates_to_lint, perc, &self.name, &self.version, thread_index
|
&self.name, &self.version
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,8 +398,7 @@ impl Crate {
|
||||||
.output()
|
.output()
|
||||||
.unwrap_or_else(|error| {
|
.unwrap_or_else(|error| {
|
||||||
panic!(
|
panic!(
|
||||||
"Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
|
"Encountered error:\n{error:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
|
||||||
error,
|
|
||||||
&cargo_clippy_path.display(),
|
&cargo_clippy_path.display(),
|
||||||
&self.path.display()
|
&self.path.display()
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2022-11-21"
|
channel = "nightly-2022-12-01"
|
||||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![feature(rustc_private)]
|
#![feature(rustc_private)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(once_cell)]
|
#![feature(once_cell)]
|
||||||
|
#![feature(lint_reasons)]
|
||||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||||
|
@ -90,6 +91,10 @@ fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option<String>) {
|
||||||
|
|
||||||
// During development track the `clippy-driver` executable so that cargo will re-run clippy whenever
|
// During development track the `clippy-driver` executable so that cargo will re-run clippy whenever
|
||||||
// it is rebuilt
|
// 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 cfg!(debug_assertions) {
|
||||||
if let Ok(current_exe) = env::current_exe()
|
if let Ok(current_exe) = env::current_exe()
|
||||||
&& let Some(current_exe) = current_exe.to_str()
|
&& let Some(current_exe) = current_exe.to_str()
|
||||||
|
|
|
@ -11,9 +11,9 @@ extern crate rustc_middle;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rustc_session;
|
extern crate rustc_session;
|
||||||
use clippy_utils::extract_msrv_attr;
|
use clippy_utils::extract_msrv_attr;
|
||||||
|
use clippy_utils::msrvs::Msrv;
|
||||||
use rustc_hir::Expr;
|
use rustc_hir::Expr;
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
pub TEST_LINT,
|
pub TEST_LINT,
|
||||||
|
@ -22,7 +22,7 @@ declare_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Pass {
|
struct Pass {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_lint_pass!(Pass => [TEST_LINT]);
|
impl_lint_pass!(Pass => [TEST_LINT]);
|
||||||
|
|
|
@ -11,9 +11,9 @@ extern crate rustc_middle;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rustc_session;
|
extern crate rustc_session;
|
||||||
use clippy_utils::extract_msrv_attr;
|
use clippy_utils::extract_msrv_attr;
|
||||||
|
use clippy_utils::msrvs::Msrv;
|
||||||
use rustc_hir::Expr;
|
use rustc_hir::Expr;
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||||
use rustc_semver::RustcVersion;
|
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
pub TEST_LINT,
|
pub TEST_LINT,
|
||||||
|
@ -22,7 +22,7 @@ declare_lint! {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Pass {
|
struct Pass {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_lint_pass!(Pass => [TEST_LINT]);
|
impl_lint_pass!(Pass => [TEST_LINT]);
|
||||||
|
|
|
@ -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
|
error: hardcoded path to a diagnostic item
|
||||||
--> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36
|
--> $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`
|
= 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
|
error: hardcoded path to a diagnostic item
|
||||||
--> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
|
--> $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`
|
= 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
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
allow-mixed-uninlined-format-args = false
|
|
@ -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<i32> = 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());
|
||||||
|
}
|
|
@ -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<i32> = 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());
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of
|
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of
|
||||||
allow-dbg-in-tests
|
allow-dbg-in-tests
|
||||||
allow-expect-in-tests
|
allow-expect-in-tests
|
||||||
|
allow-mixed-uninlined-format-args
|
||||||
allow-print-in-tests
|
allow-print-in-tests
|
||||||
allow-unwrap-in-tests
|
allow-unwrap-in-tests
|
||||||
allowed-scripts
|
allowed-scripts
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// edition:2018
|
// edition:2018
|
||||||
// aux-build:macro_rules.rs
|
// aux-build:macro_rules.rs
|
||||||
|
|
||||||
#![feature(custom_inner_attributes)]
|
|
||||||
#![feature(exclusive_range_pattern)]
|
#![feature(exclusive_range_pattern)]
|
||||||
#![feature(stmt_expr_attributes)]
|
#![feature(stmt_expr_attributes)]
|
||||||
#![warn(clippy::almost_complete_letter_range)]
|
#![warn(clippy::almost_complete_letter_range)]
|
||||||
|
@ -62,16 +61,16 @@ fn main() {
|
||||||
b!();
|
b!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[clippy::msrv = "1.25"]
|
||||||
fn _under_msrv() {
|
fn _under_msrv() {
|
||||||
#![clippy::msrv = "1.25"]
|
|
||||||
let _ = match 'a' {
|
let _ = match 'a' {
|
||||||
'a'...'z' => 1,
|
'a'...'z' => 1,
|
||||||
_ => 2,
|
_ => 2,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[clippy::msrv = "1.26"]
|
||||||
fn _meets_msrv() {
|
fn _meets_msrv() {
|
||||||
#![clippy::msrv = "1.26"]
|
|
||||||
let _ = 'a'..='z';
|
let _ = 'a'..='z';
|
||||||
let _ = match 'a' {
|
let _ = match 'a' {
|
||||||
'a'..='z' => 1,
|
'a'..='z' => 1,
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// edition:2018
|
// edition:2018
|
||||||
// aux-build:macro_rules.rs
|
// aux-build:macro_rules.rs
|
||||||
|
|
||||||
#![feature(custom_inner_attributes)]
|
|
||||||
#![feature(exclusive_range_pattern)]
|
#![feature(exclusive_range_pattern)]
|
||||||
#![feature(stmt_expr_attributes)]
|
#![feature(stmt_expr_attributes)]
|
||||||
#![warn(clippy::almost_complete_letter_range)]
|
#![warn(clippy::almost_complete_letter_range)]
|
||||||
|
@ -62,16 +61,16 @@ fn main() {
|
||||||
b!();
|
b!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[clippy::msrv = "1.25"]
|
||||||
fn _under_msrv() {
|
fn _under_msrv() {
|
||||||
#![clippy::msrv = "1.25"]
|
|
||||||
let _ = match 'a' {
|
let _ = match 'a' {
|
||||||
'a'..'z' => 1,
|
'a'..'z' => 1,
|
||||||
_ => 2,
|
_ => 2,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[clippy::msrv = "1.26"]
|
||||||
fn _meets_msrv() {
|
fn _meets_msrv() {
|
||||||
#![clippy::msrv = "1.26"]
|
|
||||||
let _ = 'a'..'z';
|
let _ = 'a'..'z';
|
||||||
let _ = match 'a' {
|
let _ = match 'a' {
|
||||||
'a'..'z' => 1,
|
'a'..'z' => 1,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error: almost complete ascii letter range
|
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';
|
LL | let _ = ('a') ..'z';
|
||||||
| ^^^^^^--^^^
|
| ^^^^^^--^^^
|
||||||
|
@ -9,7 +9,7 @@ LL | let _ = ('a') ..'z';
|
||||||
= note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
|
= note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
|
||||||
|
|
||||||
error: almost complete ascii letter range
|
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');
|
LL | let _ = 'A' .. ('Z');
|
||||||
| ^^^^--^^^^^^
|
| ^^^^--^^^^^^
|
||||||
|
@ -17,7 +17,7 @@ LL | let _ = 'A' .. ('Z');
|
||||||
| help: use an inclusive range: `..=`
|
| help: use an inclusive range: `..=`
|
||||||
|
|
||||||
error: almost complete ascii letter 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');
|
LL | let _ = (b'a')..(b'z');
|
||||||
| ^^^^^^--^^^^^^
|
| ^^^^^^--^^^^^^
|
||||||
|
@ -25,7 +25,7 @@ LL | let _ = (b'a')..(b'z');
|
||||||
| help: use an inclusive range: `..=`
|
| help: use an inclusive range: `..=`
|
||||||
|
|
||||||
error: almost complete ascii letter 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';
|
LL | let _ = b'A'..b'Z';
|
||||||
| ^^^^--^^^^
|
| ^^^^--^^^^
|
||||||
|
@ -33,7 +33,7 @@ LL | let _ = b'A'..b'Z';
|
||||||
| help: use an inclusive range: `..=`
|
| help: use an inclusive range: `..=`
|
||||||
|
|
||||||
error: almost complete ascii letter 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';
|
LL | let _ = a!()..'z';
|
||||||
| ^^^^--^^^
|
| ^^^^--^^^
|
||||||
|
@ -41,7 +41,7 @@ LL | let _ = a!()..'z';
|
||||||
| help: use an inclusive range: `..=`
|
| help: use an inclusive range: `..=`
|
||||||
|
|
||||||
error: almost complete ascii letter 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,
|
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: `..=`
|
| help: use an inclusive range: `..=`
|
||||||
|
|
||||||
error: almost complete ascii letter 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,
|
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: `..=`
|
| help: use an inclusive range: `..=`
|
||||||
|
|
||||||
error: almost complete ascii letter 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,
|
LL | 'a'..'z' if true => 1,
|
||||||
| ^^^--^^^
|
| ^^^--^^^
|
||||||
|
@ -65,7 +65,7 @@ LL | 'a'..'z' if true => 1,
|
||||||
| help: use an inclusive range: `..=`
|
| help: use an inclusive range: `..=`
|
||||||
|
|
||||||
error: almost complete ascii letter 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,
|
LL | 'A'..'Z' if true => 2,
|
||||||
| ^^^--^^^
|
| ^^^--^^^
|
||||||
|
@ -73,7 +73,7 @@ LL | 'A'..'Z' if true => 2,
|
||||||
| help: use an inclusive range: `..=`
|
| help: use an inclusive range: `..=`
|
||||||
|
|
||||||
error: almost complete ascii letter 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';
|
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)
|
= 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
|
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,
|
LL | 'a'..'z' => 1,
|
||||||
| ^^^--^^^
|
| ^^^--^^^
|
||||||
|
@ -94,7 +94,7 @@ LL | 'a'..'z' => 1,
|
||||||
| help: use an inclusive range: `...`
|
| help: use an inclusive range: `...`
|
||||||
|
|
||||||
error: almost complete ascii letter 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';
|
LL | let _ = 'a'..'z';
|
||||||
| ^^^--^^^
|
| ^^^--^^^
|
||||||
|
@ -102,7 +102,7 @@ LL | let _ = 'a'..'z';
|
||||||
| help: use an inclusive range: `..=`
|
| help: use an inclusive range: `..=`
|
||||||
|
|
||||||
error: almost complete ascii letter 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,
|
LL | 'a'..'z' => 1,
|
||||||
| ^^^--^^^
|
| ^^^--^^^
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// run-rustfix
|
// run-rustfix
|
||||||
|
|
||||||
#![feature(custom_inner_attributes)]
|
|
||||||
#![warn(clippy::cast_abs_to_unsigned)]
|
#![warn(clippy::cast_abs_to_unsigned)]
|
||||||
#![allow(clippy::uninlined_format_args, unused)]
|
#![allow(clippy::uninlined_format_args, unused)]
|
||||||
|
|
||||||
|
@ -33,16 +32,14 @@ fn main() {
|
||||||
let _ = (x as i64 - y as i64).unsigned_abs() as u32;
|
let _ = (x as i64 - y as i64).unsigned_abs() as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[clippy::msrv = "1.50"]
|
||||||
fn msrv_1_50() {
|
fn msrv_1_50() {
|
||||||
#![clippy::msrv = "1.50"]
|
|
||||||
|
|
||||||
let x: i32 = 10;
|
let x: i32 = 10;
|
||||||
assert_eq!(10u32, x.abs() as u32);
|
assert_eq!(10u32, x.abs() as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[clippy::msrv = "1.51"]
|
||||||
fn msrv_1_51() {
|
fn msrv_1_51() {
|
||||||
#![clippy::msrv = "1.51"]
|
|
||||||
|
|
||||||
let x: i32 = 10;
|
let x: i32 = 10;
|
||||||
assert_eq!(10u32, x.unsigned_abs());
|
assert_eq!(10u32, x.unsigned_abs());
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue