Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
11434f270f
|
@ -4188,6 +4188,7 @@ Released 2018-09-13
|
|||
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
|
||||
[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
|
||||
[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
|
||||
[`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters
|
||||
[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
|
||||
[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
|
||||
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
|
||||
|
@ -4450,6 +4451,7 @@ Released 2018-09-13
|
|||
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
|
||||
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
|
||||
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
|
||||
[`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment
|
||||
[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc
|
||||
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
|
||||
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
|
||||
|
|
|
@ -42,6 +42,7 @@ filetime = "0.2"
|
|||
rustc-workspace-hack = "1.0"
|
||||
|
||||
# UI test dependencies
|
||||
clap = { version = "3.1", features = ["derive"] }
|
||||
clippy_utils = { path = "clippy_utils" }
|
||||
derive-new = "0.5"
|
||||
if_chain = "1.0"
|
||||
|
|
|
@ -197,8 +197,8 @@ disallowed-names = ["toto", "tata", "titi"]
|
|||
cognitive-complexity-threshold = 30
|
||||
```
|
||||
|
||||
See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
|
||||
lints can be configured and the meaning of the variables.
|
||||
See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
|
||||
the lint descriptions contain the names and meanings of these configuration variables.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
|
@ -224,7 +224,7 @@ in the `Cargo.toml` can be used.
|
|||
rust-version = "1.30"
|
||||
```
|
||||
|
||||
The MSRV can also be specified as an inner attribute, like below.
|
||||
The MSRV can also be specified as an attribute, like below.
|
||||
|
||||
```rust
|
||||
#![feature(custom_inner_attributes)]
|
||||
|
|
|
@ -21,3 +21,4 @@
|
|||
- [The Clippy Book](development/infrastructure/book.md)
|
||||
- [Proposals](development/proposals/README.md)
|
||||
- [Roadmap 2021](development/proposals/roadmap-2021.md)
|
||||
- [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md)
|
||||
|
|
|
@ -11,8 +11,8 @@ disallowed-names = ["toto", "tata", "titi"]
|
|||
cognitive-complexity-threshold = 30
|
||||
```
|
||||
|
||||
See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
|
||||
lints can be configured and the meaning of the variables.
|
||||
See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
|
||||
the lint descriptions contain the names and meanings of these configuration variables.
|
||||
|
||||
To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS`
|
||||
environment variable.
|
||||
|
@ -72,7 +72,7 @@ minimum supported Rust version (MSRV) in the clippy configuration file.
|
|||
msrv = "1.30.0"
|
||||
```
|
||||
|
||||
The MSRV can also be specified as an inner attribute, like below.
|
||||
The MSRV can also be specified as an attribute, like below.
|
||||
|
||||
```rust
|
||||
#![feature(custom_inner_attributes)]
|
||||
|
|
|
@ -443,27 +443,27 @@ value is passed to the constructor in `clippy_lints/lib.rs`.
|
|||
|
||||
```rust
|
||||
pub struct ManualStrip {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ManualStrip {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The project's MSRV can then be matched against the feature MSRV in the LintPass
|
||||
using the `meets_msrv` utility function.
|
||||
using the `Msrv::meets` method.
|
||||
|
||||
``` rust
|
||||
if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
|
||||
if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
The project's MSRV can also be specified as an inner attribute, which overrides
|
||||
The project's MSRV can also be specified as an attribute, which overrides
|
||||
the value from `clippy.toml`. This can be accounted for using the
|
||||
`extract_msrv_attr!(LintContext)` macro and passing
|
||||
`LateContext`/`EarlyContext`.
|
||||
|
@ -483,19 +483,15 @@ have a case for the version below the MSRV and one with the same contents but
|
|||
for the MSRV version itself.
|
||||
|
||||
```rust
|
||||
#![feature(custom_inner_attributes)]
|
||||
|
||||
...
|
||||
|
||||
#[clippy::msrv = "1.44"]
|
||||
fn msrv_1_44() {
|
||||
#![clippy::msrv = "1.44"]
|
||||
|
||||
/* something that would trigger the lint */
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.45"]
|
||||
fn msrv_1_45() {
|
||||
#![clippy::msrv = "1.45"]
|
||||
|
||||
/* something that would trigger the lint */
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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 {
|
||||
format!(
|
||||
"store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv)));\n ",
|
||||
"store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv())));\n ",
|
||||
lint_pass = lint.pass,
|
||||
ctor_arg = if lint.pass == "late" { "_" } else { "" },
|
||||
module_name = lint.name,
|
||||
|
@ -238,10 +238,9 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||
result.push_str(&if enable_msrv {
|
||||
formatdoc!(
|
||||
r#"
|
||||
use clippy_utils::msrvs;
|
||||
use clippy_utils::msrvs::{{self, Msrv}};
|
||||
{pass_import}
|
||||
use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{{declare_tool_lint, impl_lint_pass}};
|
||||
|
||||
"#
|
||||
|
@ -263,12 +262,12 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||
formatdoc!(
|
||||
r#"
|
||||
pub struct {name_camel} {{
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}}
|
||||
|
||||
impl {name_camel} {{
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {{
|
||||
pub fn new(msrv: Msrv) -> Self {{
|
||||
Self {{ msrv }}
|
||||
}}
|
||||
}}
|
||||
|
@ -357,15 +356,14 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
|
|||
let _ = writedoc!(
|
||||
lint_file_contents,
|
||||
r#"
|
||||
use clippy_utils::{{meets_msrv, msrvs}};
|
||||
use clippy_utils::msrvs::{{self, Msrv}};
|
||||
use rustc_lint::{{{context_import}, LintContext}};
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
use super::{name_upper};
|
||||
|
||||
// TODO: Adjust the parameters as necessary
|
||||
pub(super) fn check(cx: &{context_import}, msrv: Option<RustcVersion>) {{
|
||||
if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
|
||||
pub(super) fn check(cx: &{context_import}, msrv: &Msrv) {{
|
||||
if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
|
||||
return;
|
||||
}}
|
||||
todo!();
|
||||
|
|
|
@ -19,7 +19,7 @@ quine-mc_cluskey = "0.2"
|
|||
regex-syntax = "0.6"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
tempfile = { version = "3.2", optional = true }
|
||||
tempfile = { version = "3.3.0", optional = true }
|
||||
toml = "0.5"
|
||||
unicode-normalization = "0.1"
|
||||
unicode-script = { version = "0.5", default-features = false }
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{trim_span, walk_span_to_context};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
|
||||
|
@ -33,10 +32,10 @@ declare_clippy_lint! {
|
|||
impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]);
|
||||
|
||||
pub struct AlmostCompleteLetterRange {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
impl AlmostCompleteLetterRange {
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +45,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange {
|
|||
let ctxt = e.span.ctxt();
|
||||
let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt)
|
||||
&& let Some(end) = walk_span_to_context(end.span, ctxt)
|
||||
&& meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE)
|
||||
&& self.msrv.meets(msrvs::RANGE_INCLUSIVE)
|
||||
{
|
||||
Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
|
||||
} else {
|
||||
|
@ -60,7 +59,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange {
|
|||
if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
|
||||
&& matches!(kind.node, RangeEnd::Excluded)
|
||||
{
|
||||
let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) {
|
||||
let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) {
|
||||
"..="
|
||||
} else {
|
||||
"..."
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -63,12 +63,12 @@ const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 19] = [
|
|||
];
|
||||
|
||||
pub struct ApproxConstant {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ApproxConstant {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ impl ApproxConstant {
|
|||
let s = s.as_str();
|
||||
if s.parse::<f64>().is_ok() {
|
||||
for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
|
||||
if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) {
|
||||
if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| self.msrv.meets(msrv)) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
APPROX_CONSTANT,
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::macros::{is_panic, macro_backtrace};
|
||||
use clippy_utils::msrvs;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
|
||||
use clippy_utils::{extract_msrv_attr, meets_msrv};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -14,7 +13,6 @@ use rustc_hir::{
|
|||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
@ -599,7 +597,7 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
|
|||
}
|
||||
|
||||
pub struct EarlyAttributes {
|
||||
pub msrv: Option<RustcVersion>,
|
||||
pub msrv: Msrv,
|
||||
}
|
||||
|
||||
impl_lint_pass!(EarlyAttributes => [
|
||||
|
@ -614,7 +612,7 @@ impl EarlyLintPass for EarlyAttributes {
|
|||
}
|
||||
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
check_deprecated_cfg_attr(cx, attr, self.msrv);
|
||||
check_deprecated_cfg_attr(cx, attr, &self.msrv);
|
||||
check_mismatched_target_os(cx, attr);
|
||||
}
|
||||
|
||||
|
@ -654,9 +652,9 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
|
|||
}
|
||||
}
|
||||
|
||||
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
|
||||
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
|
||||
if_chain! {
|
||||
if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES);
|
||||
if msrv.meets(msrvs::TOOL_ATTRIBUTES);
|
||||
// check cfg_attr
|
||||
if attr.has_name(sym::cfg_attr);
|
||||
if let Some(items) = attr.meta_item_list();
|
||||
|
|
|
@ -85,8 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
let span =
|
||||
block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
|
||||
let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
|
||||
if span.from_expansion() || expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
use super::CAST_ABS_TO_UNSIGNED;
|
||||
|
||||
|
@ -15,9 +14,9 @@ pub(super) fn check(
|
|||
cast_expr: &Expr<'_>,
|
||||
cast_from: Ty<'_>,
|
||||
cast_to: Ty<'_>,
|
||||
msrv: Option<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::Uint(to) = cast_to.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::in_constant;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::is_isize_or_usize;
|
||||
use clippy_utils::{in_constant, meets_msrv, msrvs};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, FloatTy, Ty};
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
use super::{utils, CAST_LOSSLESS};
|
||||
|
||||
|
@ -16,7 +16,7 @@ pub(super) fn check(
|
|||
cast_op: &Expr<'_>,
|
||||
cast_from: Ty<'_>,
|
||||
cast_to: Ty<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if !should_lint(cx, expr, cast_from, cast_to, msrv) {
|
||||
return;
|
||||
|
@ -57,13 +57,7 @@ pub(super) fn check(
|
|||
);
|
||||
}
|
||||
|
||||
fn should_lint(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
cast_from: Ty<'_>,
|
||||
cast_to: Ty<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
) -> bool {
|
||||
fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool {
|
||||
// Do not suggest using From in consts/statics until it is valid to do so (see #2267).
|
||||
if in_constant(cx, expr.hir_id) {
|
||||
return false;
|
||||
|
@ -89,7 +83,7 @@ fn should_lint(
|
|||
};
|
||||
!is_isize_or_usize(cast_from) && from_nbits < to_nbits
|
||||
},
|
||||
(false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true,
|
||||
(false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(msrvs::FROM_BOOL) => true,
|
||||
(_, _) => {
|
||||
matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
|
||||
},
|
||||
|
|
|
@ -118,12 +118,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
|||
};
|
||||
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
|
||||
|
||||
let cast_from_ptr_size = def.repr().int.map_or(true, |ty| {
|
||||
matches!(
|
||||
ty,
|
||||
IntegerType::Pointer(_),
|
||||
)
|
||||
});
|
||||
let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),));
|
||||
let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
|
||||
(false, false) if from_nbits > to_nbits => "",
|
||||
(true, false) if from_nbits > to_nbits => "",
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::{diagnostics::span_lint_and_then, source};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut};
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
use super::CAST_SLICE_DIFFERENT_SIZES;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option<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
|
||||
if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) {
|
||||
if !msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def_id::DefId, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
use super::CAST_SLICE_FROM_RAW_PARTS;
|
||||
|
||||
|
@ -25,15 +25,9 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
cast_expr: &Expr<'_>,
|
||||
cast_to: Ty<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) {
|
||||
if_chain! {
|
||||
if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS);
|
||||
if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS);
|
||||
if let ty::RawPtr(ptrty) = cast_to.kind();
|
||||
if let ty::Slice(_) = ptrty.ty.kind();
|
||||
if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind;
|
||||
|
|
|
@ -21,11 +21,11 @@ mod ptr_as_ptr;
|
|||
mod unnecessary_cast;
|
||||
mod utils;
|
||||
|
||||
use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
|
||||
use clippy_utils::is_hir_ty_cfg_dependant;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -648,12 +648,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct Casts {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl Casts {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -686,7 +686,7 @@ impl_lint_pass!(Casts => [
|
|||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !in_external_macro(cx.sess(), expr.span) {
|
||||
ptr_as_ptr::check(cx, expr, self.msrv);
|
||||
ptr_as_ptr::check(cx, expr, &self.msrv);
|
||||
}
|
||||
|
||||
if expr.span.from_expansion() {
|
||||
|
@ -705,7 +705,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
|
||||
return;
|
||||
}
|
||||
cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
|
||||
cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv);
|
||||
as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to);
|
||||
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
|
@ -717,16 +717,16 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
cast_possible_wrap::check(cx, expr, cast_from, cast_to);
|
||||
cast_precision_loss::check(cx, expr, cast_from, cast_to);
|
||||
cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
|
||||
cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
|
||||
cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
}
|
||||
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
|
||||
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
|
||||
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
|
||||
}
|
||||
|
||||
as_underscore::check(cx, expr, cast_to_hir);
|
||||
|
||||
if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
|
||||
if self.msrv.meets(msrvs::BORROW_AS_PTR) {
|
||||
borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
|
||||
}
|
||||
}
|
||||
|
@ -734,8 +734,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
cast_ref_to_mut::check(cx, expr);
|
||||
cast_ptr_alignment::check(cx, expr);
|
||||
char_lit_as_u8::check(cx, expr);
|
||||
ptr_as_ptr::check(cx, expr, self.msrv);
|
||||
cast_slice_different_sizes::check(cx, expr, self.msrv);
|
||||
ptr_as_ptr::check(cx, expr, &self.msrv);
|
||||
cast_slice_different_sizes::check(cx, expr, &self.msrv);
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, TypeAndMut};
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
use super::PTR_AS_PTR;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVersion>) {
|
||||
if !meets_msrv(msrv, msrvs::POINTER_CAST) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
|
||||
if !msrv.meets(msrvs::POINTER_CAST) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::numeric_literal::NumericLiteral;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{get_parent_expr, path_to_local};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::{LitFloatType, LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -75,13 +75,26 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
|
||||
if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
|
||||
if let Some(id) = path_to_local(cast_expr)
|
||||
&& let Some(span) = cx.tcx.hir().opt_span(id)
|
||||
&& span.ctxt() != cast_expr.span.ctxt()
|
||||
{
|
||||
// Binding context is different than the identifiers context.
|
||||
// Weird macro wizardry could be involved here.
|
||||
return false;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_CAST,
|
||||
expr.span,
|
||||
&format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
|
||||
"try",
|
||||
cast_str,
|
||||
if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(..))) {
|
||||
format!("{{ {cast_str} }}")
|
||||
} else {
|
||||
cast_str
|
||||
},
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return true;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
//! lint on manually implemented checked conversions that could be transformed into `try_from`
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq};
|
||||
use clippy_utils::{in_constant, is_integer_literal, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -37,12 +37,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct CheckedConversions {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl CheckedConversions {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
|
||||
if !meets_msrv(self.msrv, msrvs::TRY_FROM) {
|
||||
if !self.msrv.meets(msrvs::TRY_FROM) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -160,11 +160,13 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
|
|||
if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
|
||||
// Prevent triggering on `if c { if let a = b { .. } }`.
|
||||
if !matches!(check_inner.kind, ast::ExprKind::Let(..));
|
||||
if expr.span.ctxt() == inner.span.ctxt();
|
||||
let ctxt = expr.span.ctxt();
|
||||
if inner.span.ctxt() == ctxt;
|
||||
then {
|
||||
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
|
||||
let lhs = Sugg::ast(cx, check, "..");
|
||||
let rhs = Sugg::ast(cx, check_inner, "..");
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app);
|
||||
let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app);
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"collapse nested if block",
|
||||
|
@ -173,7 +175,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
|
|||
lhs.and(&rhs),
|
||||
snippet_block(cx, content.span, "..", Some(expr.span)),
|
||||
),
|
||||
Applicability::MachineApplicable, // snippet
|
||||
app, // snippet
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -177,6 +177,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
|
||||
crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
|
||||
crate::functions::DOUBLE_MUST_USE_INFO,
|
||||
crate::functions::MISNAMED_GETTERS_INFO,
|
||||
crate::functions::MUST_USE_CANDIDATE_INFO,
|
||||
crate::functions::MUST_USE_UNIT_INFO,
|
||||
crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
|
||||
|
@ -583,6 +584,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::types::TYPE_COMPLEXITY_INFO,
|
||||
crate::types::VEC_BOX_INFO,
|
||||
crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO,
|
||||
crate::undocumented_unsafe_blocks::UNNECESSARY_SAFETY_COMMENT_INFO,
|
||||
crate::unicode::INVISIBLE_CHARACTERS_INFO,
|
||||
crate::unicode::NON_ASCII_LITERAL_INFO,
|
||||
crate::unicode::UNICODE_NOT_NFC_INFO,
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
|
||||
use clippy_utils::{
|
||||
fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
|
||||
walk_to_expr_usage,
|
||||
fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage,
|
||||
};
|
||||
|
||||
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch};
|
||||
|
@ -28,7 +29,6 @@ use rustc_middle::ty::{
|
|||
self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
|
||||
ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
|
||||
};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{symbol::sym, Span, Symbol};
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
|
@ -181,12 +181,12 @@ pub struct Dereferencing<'tcx> {
|
|||
possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||
|
||||
// `IntoIterator` for arrays requires Rust 1.53.
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl<'tcx> Dereferencing<'tcx> {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
..Dereferencing::default()
|
||||
|
@ -286,26 +286,27 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
match (self.state.take(), kind) {
|
||||
(None, kind) => {
|
||||
let expr_ty = typeck.expr_ty(expr);
|
||||
let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv);
|
||||
let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, &self.msrv);
|
||||
match kind {
|
||||
RefOp::Deref => {
|
||||
let sub_ty = typeck.expr_ty(sub_expr);
|
||||
if let Position::FieldAccess {
|
||||
name,
|
||||
of_union: false,
|
||||
} = position
|
||||
&& !ty_contains_field(typeck.expr_ty(sub_expr), name)
|
||||
&& !ty_contains_field(sub_ty, name)
|
||||
{
|
||||
self.state = Some((
|
||||
State::ExplicitDerefField { name },
|
||||
StateData { span: expr.span, hir_id: expr.hir_id, position },
|
||||
));
|
||||
} else if position.is_deref_stable() {
|
||||
} else if position.is_deref_stable() && sub_ty.is_ref() {
|
||||
self.state = Some((
|
||||
State::ExplicitDeref { mutability: None },
|
||||
StateData { span: expr.span, hir_id: expr.hir_id, position },
|
||||
));
|
||||
}
|
||||
}
|
||||
},
|
||||
RefOp::Method(target_mut)
|
||||
if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
|
||||
&& position.lint_explicit_deref() =>
|
||||
|
@ -320,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
StateData {
|
||||
span: expr.span,
|
||||
hir_id: expr.hir_id,
|
||||
position
|
||||
position,
|
||||
},
|
||||
));
|
||||
},
|
||||
|
@ -394,7 +395,11 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
msg,
|
||||
snip_expr,
|
||||
}),
|
||||
StateData { span: expr.span, hir_id: expr.hir_id, position },
|
||||
StateData {
|
||||
span: expr.span,
|
||||
hir_id: expr.hir_id,
|
||||
position,
|
||||
},
|
||||
));
|
||||
} else if position.is_deref_stable()
|
||||
// Auto-deref doesn't combine with other adjustments
|
||||
|
@ -406,7 +411,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
StateData {
|
||||
span: expr.span,
|
||||
hir_id: expr.hir_id,
|
||||
position
|
||||
position,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -698,7 +703,7 @@ fn walk_parents<'tcx>(
|
|||
cx: &LateContext<'tcx>,
|
||||
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||
e: &'tcx Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) -> (Position, &'tcx [Adjustment<'tcx>]) {
|
||||
let mut adjustments = [].as_slice();
|
||||
let mut precedence = 0i8;
|
||||
|
@ -862,7 +867,11 @@ fn walk_parents<'tcx>(
|
|||
} && impl_ty.is_ref()
|
||||
&& let infcx = cx.tcx.infer_ctxt().build()
|
||||
&& infcx
|
||||
.type_implements_trait(trait_id, [impl_ty.into()].into_iter().chain(subs.iter().copied()), cx.param_env)
|
||||
.type_implements_trait(
|
||||
trait_id,
|
||||
[impl_ty.into()].into_iter().chain(subs.iter().copied()),
|
||||
cx.param_env,
|
||||
)
|
||||
.must_apply_modulo_regions()
|
||||
{
|
||||
return Some(Position::MethodReceiverRefImpl)
|
||||
|
@ -1078,7 +1087,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
|
|||
param_ty: ParamTy,
|
||||
mut expr: &Expr<'tcx>,
|
||||
precedence: i8,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) -> Position {
|
||||
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
|
||||
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
|
||||
|
@ -1178,7 +1187,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
|
|||
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
|
||||
&& let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
|
||||
&& ty.is_array()
|
||||
&& !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
|
||||
&& !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::traits::Reveal;
|
||||
use rustc_middle::ty::{
|
||||
self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef,
|
||||
Ty, TyCtxt,
|
||||
self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate,
|
||||
TraitRef, Ty, TyCtxt,
|
||||
};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
|
|
|
@ -253,7 +253,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub UNNECESSARY_SAFETY_DOC,
|
||||
style,
|
||||
restriction,
|
||||
"`pub fn` or `pub trait` with `# Safety` docs"
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety};
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
||||
use rustc_middle::ty::binding::BindingMode;
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitable};
|
||||
use rustc_middle::ty::{self, EarlyBinder, SubstsRef, Ty, TypeVisitable};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
|
@ -125,7 +125,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
|
||||
if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait()
|
||||
&& let args = cx.tcx.erase_late_bound_regions(substs.as_closure().sig()).inputs()
|
||||
&& implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &args.iter().copied().map(Into::into).collect::<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))
|
||||
{
|
||||
// Mutable closure is used after current expr; we cannot consume it.
|
||||
|
@ -152,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||
if check_sig(cx, closure_ty, call_ty);
|
||||
then {
|
||||
span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
|
||||
let name = get_ufcs_type_name(cx, method_def_id);
|
||||
let name = get_ufcs_type_name(cx, method_def_id, substs);
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"replace the closure with the method itself",
|
||||
|
@ -222,7 +227,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tc
|
|||
cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
|
||||
}
|
||||
|
||||
fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
|
||||
fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs: SubstsRef<'tcx>) -> String {
|
||||
let assoc_item = cx.tcx.associated_item(method_def_id);
|
||||
let def_id = assoc_item.container_id(cx.tcx);
|
||||
match assoc_item.container {
|
||||
|
@ -231,6 +236,15 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
|
|||
let ty = cx.tcx.type_of(def_id);
|
||||
match ty.kind() {
|
||||
ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()),
|
||||
ty::Array(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Never
|
||||
| ty::RawPtr(_)
|
||||
| ty::Ref(..)
|
||||
| ty::Slice(_)
|
||||
| ty::Tuple(_) => {
|
||||
format!("<{}>", EarlyBinder(ty).subst(cx.tcx, substs))
|
||||
},
|
||||
_ => ty.to_string(),
|
||||
}
|
||||
},
|
||||
|
|
|
@ -7,21 +7,34 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// `exit()` terminates the program and doesn't provide a
|
||||
/// stack trace.
|
||||
/// Detects calls to the `exit()` function which terminates the program.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Ideally a program is terminated by finishing
|
||||
/// Exit terminates the program at the location it is called. For unrecoverable
|
||||
/// errors `panics` should be used to provide a stacktrace and potentualy other
|
||||
/// information. A normal termination or one with an error code should happen in
|
||||
/// the main function.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// ```
|
||||
/// std::process::exit(0)
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```ignore
|
||||
/// // To provide a stacktrace and additional information
|
||||
/// panic!("message");
|
||||
///
|
||||
/// // or a main method with a return
|
||||
/// fn main() -> Result<(), i32> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.41.0"]
|
||||
pub EXIT,
|
||||
restriction,
|
||||
"`std::process::exit` is called, terminating the program"
|
||||
"detects `std::process::exit` calls"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Exit => [EXIT]);
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred};
|
||||
use clippy_utils::is_diag_trait_item;
|
||||
use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred};
|
||||
use clippy_utils::macros::{
|
||||
is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage,
|
||||
};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs};
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_errors::{
|
||||
Applicability,
|
||||
SuggestionStyle::{CompletelyHidden, ShowCode},
|
||||
};
|
||||
use rustc_hir::{Expr, ExprKind, HirId, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::edition::Edition::Edition2021;
|
||||
|
@ -103,19 +106,25 @@ declare_clippy_lint! {
|
|||
/// format!("{var:.prec$}");
|
||||
/// ```
|
||||
///
|
||||
/// ### Known Problems
|
||||
///
|
||||
/// There may be a false positive if the format string is expanded from certain proc macros:
|
||||
///
|
||||
/// ```ignore
|
||||
/// println!(indoc!("{}"), var);
|
||||
/// If allow-mixed-uninlined-format-args is set to false in clippy.toml,
|
||||
/// the following code will also trigger the lint:
|
||||
/// ```rust
|
||||
/// # let var = 42;
|
||||
/// format!("{} {}", var, 1+2);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let var = 42;
|
||||
/// format!("{var} {}", 1+2);
|
||||
/// ```
|
||||
///
|
||||
/// ### Known Problems
|
||||
///
|
||||
/// If a format string contains a numbered argument that cannot be inlined
|
||||
/// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub UNINLINED_FORMAT_ARGS,
|
||||
pedantic,
|
||||
style,
|
||||
"using non-inlined variables in `format!` calls"
|
||||
}
|
||||
|
||||
|
@ -158,13 +167,17 @@ impl_lint_pass!(FormatArgs => [
|
|||
]);
|
||||
|
||||
pub struct FormatArgs {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
ignore_mixed: bool,
|
||||
}
|
||||
|
||||
impl FormatArgs {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
ignore_mixed: allow_mixed_uninlined_format_args,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,8 +201,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
|||
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
|
||||
check_to_string_in_format_args(cx, name, arg.param.value);
|
||||
}
|
||||
if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) {
|
||||
check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id);
|
||||
if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
|
||||
check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -267,7 +280,13 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) {
|
||||
fn check_uninlined_args(
|
||||
cx: &LateContext<'_>,
|
||||
args: &FormatArgsExpn<'_>,
|
||||
call_site: Span,
|
||||
def_id: DefId,
|
||||
ignore_mixed: bool,
|
||||
) {
|
||||
if args.format_string.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
@ -282,14 +301,13 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si
|
|||
// we cannot remove any other arguments in the format string,
|
||||
// because the index numbers might be wrong after inlining.
|
||||
// Example of an un-inlinable format: print!("{}{1}", foo, 2)
|
||||
if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() {
|
||||
if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
|
||||
if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) {
|
||||
return;
|
||||
}
|
||||
// multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
|
||||
// in those cases, make the code suggestion hidden
|
||||
let multiline_fix = fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span));
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
@ -297,12 +315,22 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si
|
|||
call_site,
|
||||
"variables can be used directly in the `format!` string",
|
||||
|diag| {
|
||||
diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable);
|
||||
diag.multipart_suggestion_with_style(
|
||||
"change this to",
|
||||
fixes,
|
||||
Applicability::MachineApplicable,
|
||||
if multiline_fix { CompletelyHidden } else { ShowCode },
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool {
|
||||
fn check_one_arg(
|
||||
args: &FormatArgsExpn<'_>,
|
||||
param: &FormatParam<'_>,
|
||||
fixes: &mut Vec<(Span, String)>,
|
||||
ignore_mixed: bool,
|
||||
) -> bool {
|
||||
if matches!(param.kind, Implicit | Starred | Named(_) | Numbered)
|
||||
&& let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
|
||||
&& let [segment] = path.segments
|
||||
|
@ -317,8 +345,10 @@ fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut
|
|||
fixes.push((arg_span, String::new()));
|
||||
true // successful inlining, continue checking
|
||||
} else {
|
||||
// if we can't inline a numbered argument, we can't continue
|
||||
param.kind != Numbered
|
||||
// Do not continue inlining (return false) in case
|
||||
// * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
|
||||
// * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
|
||||
param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,12 +360,7 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_format_in_format_args(
|
||||
cx: &LateContext<'_>,
|
||||
call_site: Span,
|
||||
name: Symbol,
|
||||
arg: &Expr<'_>,
|
||||
) {
|
||||
fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
|
||||
let expn_data = arg.span.ctxt().outer_expn_data();
|
||||
if expn_data.call_site.from_expansion() {
|
||||
return;
|
||||
|
@ -408,7 +433,10 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
|
|||
|
||||
/// Returns true if `hir_id` is referred to by multiple format params
|
||||
fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
|
||||
args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err()
|
||||
args.params()
|
||||
.filter(|param| param.value.hir_id == hir_id)
|
||||
.at_most_one()
|
||||
.is_err()
|
||||
}
|
||||
|
||||
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
|
||||
|
@ -418,7 +446,11 @@ where
|
|||
let mut n_total = 0;
|
||||
let mut n_needed = 0;
|
||||
loop {
|
||||
if let Some(Adjustment { kind: Adjust::Deref(overloaded_deref), target }) = iter.next() {
|
||||
if let Some(Adjustment {
|
||||
kind: Adjust::Deref(overloaded_deref),
|
||||
target,
|
||||
}) = iter.next()
|
||||
{
|
||||
n_total += 1;
|
||||
if overloaded_deref.is_some() {
|
||||
n_needed = n_total;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::span_is_local;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::path_def_id;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{meets_msrv, msrvs, path_def_id};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_path, Visitor};
|
||||
use rustc_hir::{
|
||||
|
@ -10,7 +11,6 @@ use rustc_hir::{
|
|||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter::OnlyBodies;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
@ -49,12 +49,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct FromOverInto {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl FromOverInto {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
FromOverInto { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for FromOverInto {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) {
|
||||
if !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 not_unsafe_ptr_arg_deref;
|
||||
mod result;
|
||||
|
@ -260,6 +261,48 @@ declare_clippy_lint! {
|
|||
"function returning `Result` with large `Err` type"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for getter methods that return a field that doesn't correspond
|
||||
/// to the name of the method, when there is a field's whose name matches that of the method.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is most likely that such a method is a bug caused by a typo or by copy-pasting.
|
||||
///
|
||||
/// ### Example
|
||||
|
||||
/// ```rust
|
||||
/// struct A {
|
||||
/// a: String,
|
||||
/// b: String,
|
||||
/// }
|
||||
///
|
||||
/// impl A {
|
||||
/// fn a(&self) -> &str{
|
||||
/// &self.b
|
||||
/// }
|
||||
/// }
|
||||
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct A {
|
||||
/// a: String,
|
||||
/// b: String,
|
||||
/// }
|
||||
///
|
||||
/// impl A {
|
||||
/// fn a(&self) -> &str{
|
||||
/// &self.a
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub MISNAMED_GETTERS,
|
||||
suspicious,
|
||||
"getter method returning the wrong field"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Functions {
|
||||
too_many_arguments_threshold: u64,
|
||||
|
@ -286,6 +329,7 @@ impl_lint_pass!(Functions => [
|
|||
MUST_USE_CANDIDATE,
|
||||
RESULT_UNIT_ERR,
|
||||
RESULT_LARGE_ERR,
|
||||
MISNAMED_GETTERS,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Functions {
|
||||
|
@ -301,6 +345,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
|
|||
too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold);
|
||||
too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold);
|
||||
not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id);
|
||||
misnamed_getters::check_fn(cx, kind, decl, body, span, hir_id);
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
|
|
|
@ -94,7 +94,9 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty
|
|||
if let hir::ItemKind::Enum(ref def, _) = item.kind;
|
||||
then {
|
||||
let variants_size = AdtVariantInfo::new(cx, *adt, subst);
|
||||
if variants_size[0].size >= large_err_threshold {
|
||||
if let Some((first_variant, variants)) = variants_size.split_first()
|
||||
&& first_variant.size >= large_err_threshold
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
RESULT_LARGE_ERR,
|
||||
|
@ -102,11 +104,11 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty
|
|||
"the `Err`-variant returned from this function is very large",
|
||||
|diag| {
|
||||
diag.span_label(
|
||||
def.variants[variants_size[0].ind].span,
|
||||
def.variants[first_variant.ind].span,
|
||||
format!("the largest variant contains at least {} bytes", variants_size[0].size),
|
||||
);
|
||||
|
||||
for variant in &variants_size[1..] {
|
||||
for variant in variants {
|
||||
if variant.size >= large_err_threshold {
|
||||
let variant_def = &def.variants[variant.ind];
|
||||
diag.span_label(
|
||||
|
|
|
@ -91,7 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|
|||
infcx
|
||||
.err_ctxt()
|
||||
.maybe_note_obligation_cause_for_async_await(db, &obligation);
|
||||
if let PredicateKind::Clause(Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder() {
|
||||
if let PredicateKind::Clause(Clause::Trait(trait_pred)) =
|
||||
obligation.predicate.kind().skip_binder()
|
||||
{
|
||||
db.note(&format!(
|
||||
"`{}` doesn't implement `{}`",
|
||||
trait_pred.self_ty(),
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::{
|
||||
contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks,
|
||||
};
|
||||
use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -47,12 +45,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct IfThenSomeElseNone {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl IfThenSomeElseNone {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +59,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
|
||||
if !self.msrv.meets(msrvs::BOOL_THEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -94,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
} else {
|
||||
format!("{{ /* snippet */ {arg_snip} }}")
|
||||
};
|
||||
let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
|
||||
let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) {
|
||||
"then_some"
|
||||
} else {
|
||||
method_body.insert_str(0, "|| ");
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher::IfLet;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local};
|
||||
use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -11,7 +12,6 @@ use rustc_hir::intravisit::{self, Visitor};
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{symbol::Ident, Span};
|
||||
|
||||
|
@ -47,18 +47,17 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.59.0"]
|
||||
pub INDEX_REFUTABLE_SLICE,
|
||||
nursery,
|
||||
pedantic,
|
||||
"avoid indexing on slices which could be destructed"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct IndexRefutableSlice {
|
||||
max_suggested_slice: u64,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
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 {
|
||||
max_suggested_slice: max_suggested_slice_pattern_length,
|
||||
msrv,
|
||||
|
@ -74,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
|
|||
if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
|
||||
if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
|
||||
if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
|
||||
if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS);
|
||||
if self.msrv.meets(msrvs::SLICE_PATTERNS);
|
||||
|
||||
let found_slices = find_slice_values(cx, let_pat);
|
||||
if !found_slices.is_empty();
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
use clippy_utils::{
|
||||
diagnostics::{self, span_lint_and_sugg},
|
||||
meets_msrv, msrvs, source,
|
||||
sugg::Sugg,
|
||||
ty,
|
||||
};
|
||||
use clippy_utils::diagnostics::{self, span_lint_and_sugg};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{source_map::Spanned, sym};
|
||||
|
||||
|
@ -68,12 +66,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct InstantSubtraction {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl InstantSubtraction {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +99,7 @@ impl LateLintPass<'_> for InstantSubtraction {
|
|||
} else {
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion();
|
||||
if meets_msrv(self.msrv, msrvs::TRY_FROM);
|
||||
if self.msrv.meets(msrvs::TRY_FROM);
|
||||
|
||||
if is_an_instant(cx, lhs);
|
||||
if is_a_duration(cx, rhs);
|
||||
|
|
|
@ -52,10 +52,9 @@ extern crate declare_clippy_lint;
|
|||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clippy_utils::parse_msrv;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_lint::{Lint, LintId};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::Session;
|
||||
|
||||
#[cfg(feature = "internal")]
|
||||
|
@ -322,48 +321,10 @@ pub use crate::utils::conf::{lookup_conf_file, Conf};
|
|||
/// Used in `./src/driver.rs`.
|
||||
pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
|
||||
// NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
|
||||
let msrv = Msrv::read(&conf.msrv, sess);
|
||||
let msrv = move || msrv.clone();
|
||||
|
||||
let msrv = conf.msrv.as_ref().and_then(|s| {
|
||||
parse_msrv(s, None, None).or_else(|| {
|
||||
sess.err(format!(
|
||||
"error reading Clippy's configuration file. `{s}` is not a valid Rust version"
|
||||
));
|
||||
None
|
||||
})
|
||||
});
|
||||
|
||||
store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
|
||||
}
|
||||
|
||||
fn read_msrv(conf: &Conf, sess: &Session) -> Option<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
|
||||
}
|
||||
store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() }));
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -595,43 +556,44 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
|
||||
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
|
||||
|
||||
let msrv = read_msrv(conf, sess);
|
||||
let msrv = Msrv::read(&conf.msrv, sess);
|
||||
let msrv = move || msrv.clone();
|
||||
let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
|
||||
let allow_expect_in_tests = conf.allow_expect_in_tests;
|
||||
let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
|
||||
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(methods::Methods::new(
|
||||
avoid_breaking_exported_api,
|
||||
msrv,
|
||||
msrv(),
|
||||
allow_expect_in_tests,
|
||||
allow_unwrap_in_tests,
|
||||
))
|
||||
});
|
||||
store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));
|
||||
let matches_for_let_else = conf.matches_for_let_else;
|
||||
store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv, matches_for_let_else)));
|
||||
store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv)));
|
||||
store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
|
||||
store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv(), matches_for_let_else)));
|
||||
store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv())));
|
||||
store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv())));
|
||||
store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv())));
|
||||
store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv())));
|
||||
store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv())));
|
||||
store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv())));
|
||||
store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv())));
|
||||
store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv())));
|
||||
store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv())));
|
||||
store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv())));
|
||||
store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv())));
|
||||
store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark));
|
||||
store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv)));
|
||||
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv())));
|
||||
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount));
|
||||
store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod));
|
||||
let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(index_refutable_slice::IndexRefutableSlice::new(
|
||||
max_suggested_slice_pattern_length,
|
||||
msrv,
|
||||
msrv(),
|
||||
))
|
||||
});
|
||||
store.register_late_pass(|_| Box::<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(no_effect::NoEffect));
|
||||
store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment));
|
||||
store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv())));
|
||||
let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(cognitive_complexity::CognitiveComplexity::new(
|
||||
|
@ -806,7 +768,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
|
||||
store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
|
||||
store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
|
||||
store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
|
||||
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
|
||||
store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
|
||||
|
@ -840,7 +802,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::default());
|
||||
store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing));
|
||||
store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10));
|
||||
store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison));
|
||||
store.register_early_pass(move || Box::new(module_style::ModStyle));
|
||||
store.register_late_pass(|_| Box::new(unused_async::UnusedAsync));
|
||||
|
@ -865,14 +827,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
))
|
||||
});
|
||||
store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
|
||||
store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv)));
|
||||
let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args;
|
||||
store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined)));
|
||||
store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
|
||||
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
|
||||
store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
|
||||
store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
|
||||
store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields));
|
||||
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
|
||||
store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
|
||||
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
|
||||
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
|
||||
|
@ -896,20 +859,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
|
||||
store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
|
||||
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
|
||||
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
|
||||
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef));
|
||||
store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch));
|
||||
store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec));
|
||||
store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
|
||||
store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv())));
|
||||
store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv())));
|
||||
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
|
||||
store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
|
||||
store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
|
||||
store.register_late_pass(|_| Box::<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(move |_| Box::new(manual_clamp::ManualClamp::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
|
||||
store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
|
||||
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
|
||||
|
@ -920,7 +883,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods));
|
||||
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
|
||||
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
|
||||
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv)));
|
||||
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::trait_ref_of_method;
|
|||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
|
||||
use rustc_hir::intravisit::{
|
||||
walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
|
||||
walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
|
||||
walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
|
||||
};
|
||||
use rustc_hir::lang_items;
|
||||
|
@ -481,7 +481,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
|||
sub_visitor.visit_fn_decl(decl);
|
||||
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
|
||||
},
|
||||
TyKind::TraitObject(bounds, ref lt, _) => {
|
||||
TyKind::TraitObject(bounds, lt, _) => {
|
||||
if !lt.is_elided() {
|
||||
self.unelided_trait_object_lifetime = true;
|
||||
}
|
||||
|
@ -497,14 +497,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
|||
if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res {
|
||||
self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span);
|
||||
}
|
||||
// Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands.
|
||||
// walk_generic_arg(self, generic_arg);
|
||||
match generic_arg {
|
||||
GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
|
||||
GenericArg::Type(ty) => self.visit_ty(ty),
|
||||
GenericArg::Const(ct) => self.visit_anon_const(&ct.value),
|
||||
GenericArg::Infer(inf) => self.visit_infer(inf),
|
||||
}
|
||||
walk_generic_arg(self, generic_arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ struct PathAndSpan {
|
|||
span: Span,
|
||||
}
|
||||
|
||||
/// `MacroRefData` includes the name of the macro.
|
||||
/// `MacroRefData` includes the name of the macro
|
||||
/// and the path from `SourceMap::span_to_filename`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MacroRefData {
|
||||
name: String,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{get_parent_expr, meets_msrv, msrvs};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::sym;
|
||||
|
||||
|
@ -34,12 +34,12 @@ declare_clippy_lint! {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct ManualBits {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ManualBits {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualBits {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) {
|
||||
if !self.msrv.meets(msrvs::MANUAL_BITS) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,28 +1,25 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::higher::If;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::visitors::is_const_evaluatable;
|
||||
use clippy_utils::MaybePath;
|
||||
use clippy_utils::{
|
||||
eq_expr_value, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_hir::{
|
||||
def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{symbol::sym, Span};
|
||||
use std::ops::Deref;
|
||||
|
||||
use clippy_utils::{
|
||||
diagnostics::{span_lint_and_then, span_lint_hir_and_then},
|
||||
eq_expr_value,
|
||||
higher::If,
|
||||
is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks,
|
||||
peel_blocks_with_stmt,
|
||||
sugg::Sugg,
|
||||
ty::implements_trait,
|
||||
visitors::is_const_evaluatable,
|
||||
MaybePath,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Identifies good opportunities for a clamp function from std or core, and suggests using it.
|
||||
|
@ -87,11 +84,11 @@ declare_clippy_lint! {
|
|||
impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]);
|
||||
|
||||
pub struct ManualClamp {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ManualClamp {
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +111,7 @@ struct InputMinMax<'tcx> {
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualClamp {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if !meets_msrv(self.msrv, msrvs::CLAMP) {
|
||||
if !self.msrv.meets(msrvs::CLAMP) {
|
||||
return;
|
||||
}
|
||||
if !expr.span.from_expansion() {
|
||||
|
@ -130,7 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp {
|
|||
}
|
||||
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
|
||||
if !meets_msrv(self.msrv, msrvs::CLAMP) {
|
||||
if !self.msrv.meets(msrvs::CLAMP) {
|
||||
return;
|
||||
}
|
||||
for suggestion in is_two_if_pattern(cx, block) {
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::{diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, source::snippet};
|
||||
use rustc_ast::LitKind::{Byte, Char};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{def_id::DefId, sym};
|
||||
|
||||
use clippy_utils::{
|
||||
diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Suggests to use dedicated built-in methods,
|
||||
|
@ -45,12 +42,12 @@ declare_clippy_lint! {
|
|||
impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]);
|
||||
|
||||
pub struct ManualIsAsciiCheck {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ManualIsAsciiCheck {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -70,11 +67,11 @@ enum CharRange {
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) {
|
||||
if !self.msrv.meets(msrvs::IS_ASCII_DIGIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) {
|
||||
if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher::IfLetOrMatch;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::peel_blocks;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||
use clippy_utils::{meets_msrv, msrvs, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
|
@ -50,13 +50,13 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct ManualLetElse {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
matches_behaviour: MatchLintBehaviour,
|
||||
}
|
||||
|
||||
impl ManualLetElse {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>, matches_behaviour: MatchLintBehaviour) -> Self {
|
||||
pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
matches_behaviour,
|
||||
|
@ -69,7 +69,7 @@ impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]);
|
|||
impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) {
|
||||
let if_let_or_match = if_chain! {
|
||||
if meets_msrv(self.msrv, msrvs::LET_ELSE);
|
||||
if self.msrv.meets(msrvs::LET_ELSE);
|
||||
if !in_external_macro(cx.sess(), stmt.span);
|
||||
if let StmtKind::Local(local) = stmt.kind;
|
||||
if let Some(init) = local.init;
|
||||
|
@ -141,20 +141,18 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat:
|
|||
// * unused binding collision detection with existing ones
|
||||
// * putting patterns with at the top level | inside ()
|
||||
// for this to be machine applicable.
|
||||
let app = Applicability::HasPlaceholders;
|
||||
let mut app = Applicability::HasPlaceholders;
|
||||
let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app);
|
||||
let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app);
|
||||
let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app);
|
||||
|
||||
if let Some(sn_pat) = snippet_opt(cx, pat.span) &&
|
||||
let Some(sn_expr) = snippet_opt(cx, expr.span) &&
|
||||
let Some(sn_else) = snippet_opt(cx, else_body.span)
|
||||
{
|
||||
let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
|
||||
sn_else
|
||||
} else {
|
||||
format!("{{ {sn_else} }}")
|
||||
};
|
||||
let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
|
||||
diag.span_suggestion(span, "consider writing", sugg, app);
|
||||
}
|
||||
let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
|
||||
sn_else.into_owned()
|
||||
} else {
|
||||
format!("{{ {sn_else} }}")
|
||||
};
|
||||
let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
|
||||
diag.span_suggestion(span, "consider writing", sugg, app);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::is_doc_hidden;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{is_doc_hidden, meets_msrv, msrvs};
|
||||
use rustc_ast::ast::{self, VisibilityKind};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -8,7 +9,6 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
|||
use rustc_hir::{self as hir, Expr, ExprKind, QPath};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::DefIdTree;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::def_id::{DefId, LocalDefId};
|
||||
use rustc_span::{sym, Span};
|
||||
|
@ -63,12 +63,12 @@ declare_clippy_lint! {
|
|||
|
||||
#[expect(clippy::module_name_repetitions)]
|
||||
pub struct ManualNonExhaustiveStruct {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ManualNonExhaustiveStruct {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -77,14 +77,14 @@ impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
|
|||
|
||||
#[expect(clippy::module_name_repetitions)]
|
||||
pub struct ManualNonExhaustiveEnum {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
constructed_enum_variants: FxHashSet<(DefId, DefId)>,
|
||||
potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
|
||||
}
|
||||
|
||||
impl ManualNonExhaustiveEnum {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
constructed_enum_variants: FxHashSet::default(),
|
||||
|
@ -97,7 +97,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
|
|||
|
||||
impl EarlyLintPass for ManualNonExhaustiveStruct {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
|
||||
if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
|
||||
if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
|
||||
if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use clippy_utils::consts::{constant_full_int, FullInt};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{in_constant, meets_msrv, msrvs, path_to_local};
|
||||
use clippy_utils::{in_constant, path_to_local};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -34,12 +34,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct ManualRemEuclid {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ManualRemEuclid {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -48,11 +48,11 @@ impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !meets_msrv(self.msrv, msrvs::REM_EUCLID) {
|
||||
if !self.msrv.meets(msrvs::REM_EUCLID) {
|
||||
return;
|
||||
}
|
||||
|
||||
if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) {
|
||||
if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||
use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
@ -50,12 +50,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct ManualRetain {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ManualRetain {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -71,9 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain {
|
|||
&& let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind
|
||||
&& let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
|
||||
&& match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) {
|
||||
check_into_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
|
||||
check_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
|
||||
check_to_owned(cx, parent_expr, left_expr, target_expr, self.msrv);
|
||||
check_into_iter(cx, parent_expr, left_expr, target_expr, &self.msrv);
|
||||
check_iter(cx, parent_expr, left_expr, target_expr, &self.msrv);
|
||||
check_to_owned(cx, parent_expr, left_expr, target_expr, &self.msrv);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ fn check_into_iter(
|
|||
parent_expr: &hir::Expr<'_>,
|
||||
left_expr: &hir::Expr<'_>,
|
||||
target_expr: &hir::Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind
|
||||
&& let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
|
||||
|
@ -104,7 +104,7 @@ fn check_iter(
|
|||
parent_expr: &hir::Expr<'_>,
|
||||
left_expr: &hir::Expr<'_>,
|
||||
target_expr: &hir::Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
|
||||
&& let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
|
||||
|
@ -127,9 +127,9 @@ fn check_to_owned(
|
|||
parent_expr: &hir::Expr<'_>,
|
||||
left_expr: &hir::Expr<'_>,
|
||||
target_expr: &hir::Expr<'_>,
|
||||
msrv: Option<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 Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
|
||||
&& match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD)
|
||||
|
@ -215,10 +215,10 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo
|
|||
.any(|&method| match_def_path(cx, collect_def_id, method))
|
||||
}
|
||||
|
||||
fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option<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();
|
||||
ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
|
||||
is_type_diagnostic_item(cx, expr_ty, *ty)
|
||||
&& acceptable_msrv.map_or(true, |acceptable_msrv| meets_msrv(msrv, acceptable_msrv))
|
||||
&& acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::usage::mutated_variables;
|
||||
use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths};
|
||||
use clippy_utils::{eq_expr_value, higher, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::def::Res;
|
||||
|
@ -11,7 +12,6 @@ use rustc_hir::BinOpKind;
|
|||
use rustc_hir::{BorrowKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::Span;
|
||||
|
@ -48,12 +48,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct ManualStrip {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ManualStrip {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ enum StripKind {
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
|
||||
if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,13 +23,13 @@ mod single_match;
|
|||
mod try_err;
|
||||
mod wild_in_or_pats;
|
||||
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{snippet_opt, walk_span_to_context};
|
||||
use clippy_utils::{higher, in_constant, is_span_match, meets_msrv, msrvs};
|
||||
use clippy_utils::{higher, in_constant, is_span_match};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
|
||||
use rustc_lexer::{tokenize, TokenKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{Span, SpanData, SyntaxContext};
|
||||
|
||||
|
@ -930,13 +930,13 @@ declare_clippy_lint! {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct Matches {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
infallible_destructuring_match_linted: bool,
|
||||
}
|
||||
|
||||
impl Matches {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
..Matches::default()
|
||||
|
@ -1000,9 +1000,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
|
||||
if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
|
||||
if source == MatchSource::Normal {
|
||||
if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO)
|
||||
&& match_like_matches::check_match(cx, expr, ex, arms))
|
||||
{
|
||||
if !(self.msrv.meets(msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) {
|
||||
match_same_arms::check(cx, arms);
|
||||
}
|
||||
|
||||
|
@ -1034,7 +1032,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
|
||||
if !from_expansion {
|
||||
if let Some(else_expr) = if_let.if_else {
|
||||
if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) {
|
||||
if self.msrv.meets(msrvs::MATCHES_MACRO) {
|
||||
match_like_matches::check_if_let(
|
||||
cx,
|
||||
expr,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths};
|
||||
use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::ResultErr;
|
||||
|
@ -107,7 +107,7 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
|
|||
fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(def, subst) = ty.kind();
|
||||
if match_def_path(cx, def.did(), &paths::POLL);
|
||||
if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did());
|
||||
let ready_ty = subst.type_at(0);
|
||||
|
||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
||||
|
@ -124,7 +124,7 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<
|
|||
fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(def, subst) = ty.kind();
|
||||
if match_def_path(cx, def.did(), &paths::POLL);
|
||||
if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did());
|
||||
let ready_ty = subst.type_at(0);
|
||||
|
||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::ty::is_non_aggregate_primitive_type;
|
||||
use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res};
|
||||
use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::sym;
|
||||
|
@ -227,12 +227,12 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
|
|||
}
|
||||
|
||||
pub struct MemReplace {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl MemReplace {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
|||
then {
|
||||
check_replace_option_with_none(cx, src, dest, expr.span);
|
||||
check_replace_with_uninit(cx, src, dest, expr.span);
|
||||
if meets_msrv(self.msrv, msrvs::MEM_TAKE) {
|
||||
if self.msrv.meets(msrvs::MEM_TAKE) {
|
||||
check_replace_with_default(cx, src, dest, expr.span);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::ty::{get_iterator_item_ty, is_copy};
|
||||
use clippy_utils::{is_trait_method, meets_msrv, msrvs};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::CLONED_INSTEAD_OF_COPIED;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<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 inner_ty = match recv_ty.kind() {
|
||||
// `Option<T>` -> `T`
|
||||
ty::Adt(adt, subst)
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) =>
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && msrv.meets(msrvs::OPTION_COPIED) =>
|
||||
{
|
||||
subst.type_at(0)
|
||||
},
|
||||
_ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => {
|
||||
_ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(msrvs::ITERATOR_COPIED) => {
|
||||
match get_iterator_item_ty(cx, recv_ty) {
|
||||
// <T as Iterator>::Item
|
||||
Some(ty) => ty,
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
use super::ERR_EXPECT;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::ty::has_debug_impl;
|
||||
use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
_expr: &rustc_hir::Expr<'_>,
|
||||
recv: &rustc_hir::Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
expect_span: Span,
|
||||
err_span: Span,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if_chain! {
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
|
||||
// Test the version to make sure the lint can be showed (expect_err has been
|
||||
// introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982)
|
||||
if meets_msrv(msrv, msrvs::EXPECT_ERR);
|
||||
if msrv.meets(msrvs::EXPECT_ERR);
|
||||
|
||||
// Grabs the `Result<T, E>` type
|
||||
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::is_trait_method;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{is_trait_method, meets_msrv, msrvs};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::FILTER_MAP_NEXT;
|
||||
|
@ -14,10 +14,10 @@ pub(super) fn check<'tcx>(
|
|||
expr: &'tcx hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
arg: &'tcx hir::Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) {
|
||||
if !msrv.meets(msrvs::ITERATOR_FIND_MAP) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::symbol::{Symbol, sym};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
|
||||
use super::INEFFICIENT_TO_STRING;
|
||||
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
//! Lint for `c.is_digit(10)`
|
||||
|
||||
use super::IS_DIGIT_ASCII_RADIX;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::{
|
||||
consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
|
||||
source::snippet_with_applicability,
|
||||
consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, source::snippet_with_applicability,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
self_arg: &'tcx Expr<'_>,
|
||||
radix: &'tcx Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) {
|
||||
if !msrv.meets(msrvs::IS_ASCII_DIGIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
|
||||
use clippy_utils::{is_diag_trait_item, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
|
@ -9,19 +10,12 @@ use rustc_lint::LateContext;
|
|||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::MAP_CLONE;
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
e: &hir::Expr<'_>,
|
||||
recv: &hir::Expr<'_>,
|
||||
arg: &hir::Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
|
||||
if cx.tcx.impl_of_method(method_id)
|
||||
|
@ -97,10 +91,10 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
|
|||
);
|
||||
}
|
||||
|
||||
fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
|
||||
fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
|
||||
let (message, sugg_method) = if is_copy && msrv.meets(msrvs::ITERATOR_COPIED) {
|
||||
("you are using an explicit closure for copying elements", "copied")
|
||||
} else {
|
||||
("you are using an explicit closure for cloning elements", "cloned")
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::usage::mutated_variables;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::MAP_UNWRAP_OR;
|
||||
|
@ -19,13 +18,13 @@ pub(super) fn check<'tcx>(
|
|||
recv: &'tcx hir::Expr<'_>,
|
||||
map_arg: &'tcx hir::Expr<'_>,
|
||||
unwrap_arg: &'tcx hir::Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) -> bool {
|
||||
// lint if the caller of `map()` is an `Option`
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
|
||||
|
||||
if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) {
|
||||
if is_result && !msrv.meets(msrvs::RESULT_MAP_OR_ELSE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -104,8 +104,9 @@ mod zst_offset;
|
|||
use bind_instead_of_map::BindInsteadOfMap;
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
|
||||
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
|
||||
|
@ -113,7 +114,6 @@ use rustc_hir_analysis::hir_ty_to_ty;
|
|||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, TraitRef, Ty};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
|
@ -3163,7 +3163,7 @@ declare_clippy_lint! {
|
|||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
allow_expect_in_tests: bool,
|
||||
allow_unwrap_in_tests: bool,
|
||||
}
|
||||
|
@ -3172,7 +3172,7 @@ impl Methods {
|
|||
#[must_use]
|
||||
pub fn new(
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
allow_expect_in_tests: bool,
|
||||
allow_unwrap_in_tests: bool,
|
||||
) -> Self {
|
||||
|
@ -3325,7 +3325,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
single_char_add_str::check(cx, expr, receiver, args);
|
||||
into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
|
||||
single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args);
|
||||
unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv);
|
||||
unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv);
|
||||
},
|
||||
hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
|
||||
let mut info = BinaryExprInfo {
|
||||
|
@ -3501,7 +3501,7 @@ impl Methods {
|
|||
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
||||
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
||||
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
||||
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv),
|
||||
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv),
|
||||
("collect", []) if is_trait_method(cx, expr, sym::Iterator) => {
|
||||
needless_collect::check(cx, span, expr, recv, call_span);
|
||||
match method_call(recv) {
|
||||
|
@ -3512,7 +3512,7 @@ impl Methods {
|
|||
map_collect_result_unit::check(cx, expr, m_recv, m_arg);
|
||||
},
|
||||
Some(("take", take_self_arg, [take_arg], _, _)) => {
|
||||
if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
|
||||
if self.msrv.meets(msrvs::STR_REPEAT) {
|
||||
manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
|
||||
}
|
||||
},
|
||||
|
@ -3539,7 +3539,7 @@ impl Methods {
|
|||
},
|
||||
("expect", [_]) => match method_call(recv) {
|
||||
Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
|
||||
Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
|
||||
Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
|
||||
_ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
|
||||
},
|
||||
("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
|
||||
|
@ -3578,7 +3578,7 @@ impl Methods {
|
|||
unit_hash::check(cx, expr, recv, arg);
|
||||
},
|
||||
("is_file", []) => filetype_is_file::check(cx, expr, recv),
|
||||
("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
|
||||
("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv),
|
||||
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
|
||||
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
|
||||
("iter" | "iter_mut" | "into_iter", []) => {
|
||||
|
@ -3601,7 +3601,7 @@ impl Methods {
|
|||
},
|
||||
(name @ ("map" | "map_err"), [m_arg]) => {
|
||||
if name == "map" {
|
||||
map_clone::check(cx, expr, recv, m_arg, self.msrv);
|
||||
map_clone::check(cx, expr, recv, m_arg, &self.msrv);
|
||||
if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) {
|
||||
iter_kv_map::check(cx, map_name, expr, recv2, m_arg);
|
||||
}
|
||||
|
@ -3610,8 +3610,8 @@ impl Methods {
|
|||
}
|
||||
if let Some((name, recv2, args, span2,_)) = method_call(recv) {
|
||||
match (name, args) {
|
||||
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
|
||||
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv),
|
||||
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv),
|
||||
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
|
||||
("filter", [f_arg]) => {
|
||||
filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
|
||||
},
|
||||
|
@ -3632,7 +3632,7 @@ impl Methods {
|
|||
match (name2, args2) {
|
||||
("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
|
||||
("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
|
||||
("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv),
|
||||
("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv),
|
||||
("iter", []) => iter_next_slice::check(cx, expr, recv2),
|
||||
("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
|
||||
("skip_while", [_]) => skip_while_next::check(cx, expr),
|
||||
|
@ -3680,10 +3680,10 @@ impl Methods {
|
|||
vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
|
||||
},
|
||||
("seek", [arg]) => {
|
||||
if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) {
|
||||
if self.msrv.meets(msrvs::SEEK_FROM_CURRENT) {
|
||||
seek_from_current::check(cx, expr, recv, arg);
|
||||
}
|
||||
if meets_msrv(self.msrv, msrvs::SEEK_REWIND) {
|
||||
if self.msrv.meets(msrvs::SEEK_REWIND) {
|
||||
seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
|
||||
}
|
||||
},
|
||||
|
@ -3699,7 +3699,7 @@ impl Methods {
|
|||
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
|
||||
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
|
||||
suspicious_splitn::check(cx, name, expr, recv, count);
|
||||
str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv);
|
||||
str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv);
|
||||
}
|
||||
},
|
||||
("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
|
||||
|
@ -3717,7 +3717,7 @@ impl Methods {
|
|||
},
|
||||
("take", []) => needless_option_take::check(cx, expr, recv),
|
||||
("then", [arg]) => {
|
||||
if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
|
||||
if !self.msrv.meets(msrvs::BOOL_THEN_SOME) {
|
||||
return;
|
||||
}
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
|
||||
|
@ -3760,7 +3760,7 @@ impl Methods {
|
|||
},
|
||||
("unwrap_or_else", [u_arg]) => match method_call(recv) {
|
||||
Some(("map", recv, [map_arg], _, _))
|
||||
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
|
||||
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {},
|
||||
_ => {
|
||||
unwrap_or_else_default::check(cx, expr, recv, u_arg);
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks};
|
||||
use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::OPTION_AS_REF_DEREF;
|
||||
|
@ -19,9 +19,9 @@ pub(super) fn check(
|
|||
as_ref_recv: &hir::Expr<'_>,
|
||||
map_arg: &hir::Expr<'_>,
|
||||
is_mut: bool,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) {
|
||||
if !msrv.meets(msrvs::OPTION_AS_DEREF) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::usage::local_used_after_expr;
|
||||
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
|
||||
use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
|
||||
use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
|
||||
use core::ops::ControlFlow;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -12,7 +13,6 @@ use rustc_hir::{
|
|||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::{sym, Span, Symbol, SyntaxContext};
|
||||
|
||||
use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
|
||||
|
@ -24,7 +24,7 @@ pub(super) fn check(
|
|||
self_arg: &Expr<'_>,
|
||||
pat_arg: &Expr<'_>,
|
||||
count: u128,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
|
||||
return;
|
||||
|
@ -34,7 +34,7 @@ pub(super) fn check(
|
|||
IterUsageKind::Nth(n) => count > n + 1,
|
||||
IterUsageKind::NextTuple => count > 2,
|
||||
};
|
||||
let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE);
|
||||
let manual = count == 2 && msrv.meets(msrvs::STR_SPLIT_ONCE);
|
||||
|
||||
match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) {
|
||||
Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg),
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
use super::implicit_clone::is_clone_like;
|
||||
use super::unnecessary_iter_cloned::{self, is_into_iter};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
|
||||
use clippy_utils::visitors::find_all_ret_expressions;
|
||||
use clippy_utils::{
|
||||
fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty,
|
||||
};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
|
||||
use rustc_hir_typeck::{FnCtxt, Inherited};
|
||||
|
@ -16,14 +14,9 @@ use rustc_lint::LateContext;
|
|||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
|
||||
use rustc_middle::ty::EarlyBinder;
|
||||
use rustc_middle::ty::{self, Clause, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_middle::ty::{self, Clause, EarlyBinder, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
|
||||
use rustc_span::{sym, Symbol};
|
||||
use rustc_trait_selection::traits::{
|
||||
query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause,
|
||||
};
|
||||
use std::cmp::max;
|
||||
use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
|
||||
|
||||
use super::UNNECESSARY_TO_OWNED;
|
||||
|
||||
|
@ -33,7 +26,7 @@ pub fn check<'tcx>(
|
|||
method_name: Symbol,
|
||||
receiver: &'tcx Expr<'_>,
|
||||
args: &'tcx [Expr<'_>],
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
|
@ -204,7 +197,7 @@ fn check_into_iter_call_arg(
|
|||
expr: &Expr<'_>,
|
||||
method_name: Symbol,
|
||||
receiver: &Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) -> bool {
|
||||
if_chain! {
|
||||
if let Some(parent) = get_parent_expr(cx, expr);
|
||||
|
@ -219,7 +212,7 @@ fn check_into_iter_call_arg(
|
|||
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
|
||||
return true;
|
||||
}
|
||||
let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
|
||||
let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) {
|
||||
"copied"
|
||||
} else {
|
||||
"cloned"
|
||||
|
@ -267,11 +260,22 @@ fn check_other_call_arg<'tcx>(
|
|||
if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
|
||||
if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id;
|
||||
let receiver_ty = cx.typeck_results().expr_ty(receiver);
|
||||
if can_change_type(cx, maybe_arg, receiver_ty);
|
||||
// We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
|
||||
// `Target = T`.
|
||||
if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
|
||||
let n_refs = max(n_refs, usize::from(!is_copy(cx, receiver_ty)));
|
||||
if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) {
|
||||
Some((n_refs, receiver_ty))
|
||||
} else if trait_predicate.def_id() != deref_trait_id {
|
||||
Some((1, cx.tcx.mk_ref(
|
||||
cx.tcx.lifetimes.re_erased,
|
||||
ty::TypeAndMut {
|
||||
ty: receiver_ty,
|
||||
mutbl: Mutability::Not,
|
||||
},
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if can_change_type(cx, maybe_arg, receiver_ty);
|
||||
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
|
@ -345,13 +349,13 @@ fn get_input_traits_and_projections<'tcx>(
|
|||
if trait_predicate.trait_ref.self_ty() == input {
|
||||
trait_predicates.push(trait_predicate);
|
||||
}
|
||||
}
|
||||
},
|
||||
PredicateKind::Clause(Clause::Projection(projection_predicate)) => {
|
||||
if projection_predicate.projection_ty.self_ty() == input {
|
||||
projection_predicates.push(projection_predicate);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
(trait_predicates, projection_predicates)
|
||||
|
@ -403,10 +407,12 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
|
|||
|
||||
let mut trait_predicates = cx.tcx.param_env(callee_def_id)
|
||||
.caller_bounds().iter().filter(|predicate| {
|
||||
if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder()
|
||||
&& trait_predicate.trait_ref.self_ty() == *param_ty {
|
||||
true
|
||||
} else {
|
||||
if let PredicateKind::Clause(Clause::Trait(trait_predicate))
|
||||
= predicate.kind().skip_binder()
|
||||
&& trait_predicate.trait_ref.self_ty() == *param_ty
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
@ -466,12 +472,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id:
|
|||
|
||||
/// Returns true if the named method can be used to convert the receiver to its "owned"
|
||||
/// representation.
|
||||
fn is_to_owned_like<'a>(
|
||||
cx: &LateContext<'a>,
|
||||
call_expr: &Expr<'a>,
|
||||
method_name: Symbol,
|
||||
method_def_id: DefId,
|
||||
) -> bool {
|
||||
fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool {
|
||||
is_clone_like(cx, method_name.as_str(), method_def_id)
|
||||
|| is_cow_into_owned(cx, method_name, method_def_id)
|
||||
|| is_to_string_on_string_like(cx, call_expr, method_name, method_def_id)
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
|
||||
use clippy_utils::ty::has_drop;
|
||||
use clippy_utils::{
|
||||
fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, meets_msrv, msrvs, trait_ref_of_method,
|
||||
};
|
||||
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
|
@ -11,7 +10,6 @@ use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
|
|||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
|
||||
|
@ -75,12 +73,12 @@ declare_clippy_lint! {
|
|||
impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
|
||||
|
||||
pub struct MissingConstForFn {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl MissingConstForFn {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
|||
span: Span,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) {
|
||||
if !self.msrv.meets(msrvs::CONST_IF_MATCH) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -152,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
|||
|
||||
let mir = cx.tcx.optimized_mir(def_id);
|
||||
|
||||
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) {
|
||||
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
|
||||
if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
|
||||
cx.tcx.sess.span_err(span, err.as_ref());
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
use clippy_utils::attrs::is_doc_hidden;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
|
|
@ -92,10 +92,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
|
|||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
use clippy_utils::ptr::get_spans;
|
||||
use clippy_utils::source::{snippet, snippet_opt};
|
||||
use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item};
|
||||
use clippy_utils::ty::{
|
||||
implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item,
|
||||
};
|
||||
use clippy_utils::{get_trait_def_id, is_self, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::Attribute;
|
||||
|
@ -124,7 +126,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
.filter_map(|obligation| {
|
||||
// Note that we do not want to deal with qualified predicates here.
|
||||
match obligation.predicate.kind().no_bound_vars() {
|
||||
Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => Some(pred),
|
||||
Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => {
|
||||
Some(pred)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
|
|
|
@ -6,7 +6,8 @@ use clippy_utils::ty::has_drop;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use std::ops::Deref;
|
||||
|
||||
|
@ -159,8 +160,11 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
||||
if_chain! {
|
||||
if let StmtKind::Semi(expr) = stmt.kind;
|
||||
let ctxt = stmt.span.ctxt();
|
||||
if expr.span.ctxt() == ctxt;
|
||||
if let Some(reduced) = reduce_expression(cx, expr);
|
||||
if !&reduced.iter().any(|e| e.span.from_expansion());
|
||||
if !in_external_macro(cx.sess(), stmt.span);
|
||||
if reduced.iter().all(|e| e.span.ctxt() == ctxt);
|
||||
then {
|
||||
if let ExprKind::Index(..) = &expr.kind {
|
||||
let snippet = if let (Some(arr), Some(func)) =
|
||||
|
|
|
@ -490,7 +490,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
|
|||
ty_name: name.ident.name,
|
||||
method_renames,
|
||||
ref_prefix: RefPrefix {
|
||||
lt: lt.clone(),
|
||||
lt: *lt,
|
||||
mutability,
|
||||
},
|
||||
deref_ty,
|
||||
|
@ -693,9 +693,10 @@ fn matches_preds<'tcx>(
|
|||
cx.tcx,
|
||||
ObligationCause::dummy(),
|
||||
cx.param_env,
|
||||
cx.tcx.mk_predicate(Binder::dummy(
|
||||
PredicateKind::Clause(Clause::Projection(p.with_self_ty(cx.tcx, ty))),
|
||||
)),
|
||||
cx.tcx
|
||||
.mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Projection(
|
||||
p.with_self_ty(cx.tcx, ty),
|
||||
)))),
|
||||
)),
|
||||
ExistentialPredicate::AutoTrait(p) => infcx
|
||||
.type_implements_trait(p, [ty], cx.param_env)
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
|
||||
use clippy_utils::{get_parent_expr, in_constant, is_integer_const, path_to_local};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use std::cmp::Ordering;
|
||||
|
@ -161,12 +161,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct Ranges {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl Ranges {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ impl_lint_pass!(Ranges => [
|
|||
impl<'tcx> LateLintPass<'tcx> for Ranges {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Binary(ref op, l, r) = expr.kind {
|
||||
if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
|
||||
if self.msrv.meets(msrvs::RANGE_CONTAINS) {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,8 +81,8 @@ impl EarlyLintPass for RedundantClosureCall {
|
|||
"try not to call a closure in the expression where it is declared",
|
||||
|diag| {
|
||||
if fn_decl.inputs.is_empty() {
|
||||
let app = Applicability::MachineApplicable;
|
||||
let mut hint = Sugg::ast(cx, body, "..");
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let mut hint = Sugg::ast(cx, body, "..", closure.span.ctxt(), &mut app);
|
||||
|
||||
if asyncness.is_async() {
|
||||
// `async x` is a syntax error, so it becomes `async { x }`
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use rustc_ast::ast::{Expr, ExprKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -37,12 +36,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct RedundantFieldNames {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl RedundantFieldNames {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +50,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
|
|||
|
||||
impl EarlyLintPass for RedundantFieldNames {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) {
|
||||
if !self.msrv.meets(msrvs::FIELD_INIT_SHORTHAND) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -34,12 +33,12 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct RedundantStaticLifetimes {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl RedundantStaticLifetimes {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +95,7 @@ impl RedundantStaticLifetimes {
|
|||
|
||||
impl EarlyLintPass for RedundantStaticLifetimes {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) {
|
||||
if !self.msrv.meets(msrvs::STATIC_IN_CONST) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ use rustc_middle::lint::in_external_macro;
|
|||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::{BytePos, Pos};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -209,13 +210,14 @@ fn check_final_expr<'tcx>(
|
|||
if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
|
||||
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
|
||||
if !borrows {
|
||||
emit_return_lint(
|
||||
cx,
|
||||
peeled_drop_expr.span,
|
||||
semi_spans,
|
||||
inner.as_ref().map(|i| i.span),
|
||||
replacement,
|
||||
);
|
||||
// check if expr return nothing
|
||||
let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
|
||||
extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
|
||||
} else {
|
||||
peeled_drop_expr.span
|
||||
};
|
||||
|
||||
emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -289,3 +291,16 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
|
|||
})
|
||||
.is_some()
|
||||
}
|
||||
|
||||
// Go backwards while encountering whitespace and extend the given Span to that point.
|
||||
fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span {
|
||||
if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) {
|
||||
let ws = [' ', '\t', '\n'];
|
||||
if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) {
|
||||
let len = prev_source.len() - non_ws_pos - 1;
|
||||
return sp.with_lo(sp.lo() - BytePos::from_usize(len));
|
||||
}
|
||||
}
|
||||
|
||||
sp
|
||||
}
|
||||
|
|
|
@ -55,11 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
|
|||
if matches!(cx.tcx.def_kind(id.owner_id), DefKind::Impl)
|
||||
&& let item = cx.tcx.hir().item(id)
|
||||
&& let ItemKind::Impl(Impl {
|
||||
items,
|
||||
of_trait,
|
||||
self_ty,
|
||||
..
|
||||
}) = &item.kind
|
||||
items,
|
||||
of_trait,
|
||||
self_ty,
|
||||
..
|
||||
}) = &item.kind
|
||||
&& let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
|
||||
{
|
||||
if !map.contains_key(res) {
|
||||
|
|
|
@ -16,10 +16,10 @@ mod utils;
|
|||
mod wrong_transmute;
|
||||
|
||||
use clippy_utils::in_constant;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
|
@ -410,7 +410,7 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct Transmute {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
impl_lint_pass!(Transmute => [
|
||||
CROSSPOINTER_TRANSMUTE,
|
||||
|
@ -431,7 +431,7 @@ impl_lint_pass!(Transmute => [
|
|||
]);
|
||||
impl Transmute {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -461,7 +461,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
|
|||
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
|
||||
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
|
||||
| transmuting_null::check(cx, e, arg, to_ty)
|
||||
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
|
||||
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv)
|
||||
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use super::TRANSMUTE_PTR_TO_REF;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{meets_msrv, msrvs, sugg};
|
||||
use clippy_utils::sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitable};
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
/// Checks for `transmute_ptr_to_ref` lint.
|
||||
/// Returns `true` if it's triggered, otherwise returns `false`.
|
||||
|
@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(
|
|||
to_ty: Ty<'tcx>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
path: &'tcx Path<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) -> bool {
|
||||
match (&from_ty.kind(), &to_ty.kind()) {
|
||||
(ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => {
|
||||
|
@ -37,7 +37,7 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
let sugg = if let Some(ty) = get_explicit_type(path) {
|
||||
let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app);
|
||||
if meets_msrv(msrv, msrvs::POINTER_CAST) {
|
||||
if msrv.meets(msrvs::POINTER_CAST) {
|
||||
format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par())
|
||||
} else if from_ptr_ty.has_erased_regions() {
|
||||
sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string()
|
||||
|
@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
} else if from_ptr_ty.ty == *to_ref_ty {
|
||||
if from_ptr_ty.has_erased_regions() {
|
||||
if meets_msrv(msrv, msrvs::POINTER_CAST) {
|
||||
if msrv.meets(msrvs::POINTER_CAST) {
|
||||
format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par())
|
||||
} else {
|
||||
sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}")))
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::source::walk_span_to_context;
|
||||
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
|
||||
use clippy_utils::{get_parent_node, is_lint_allowed};
|
||||
use hir::HirId;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
|
||||
|
@ -59,11 +63,39 @@ declare_clippy_lint! {
|
|||
restriction,
|
||||
"creating an unsafe block without explaining why it is safe"
|
||||
}
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `// SAFETY: ` comments on safe code.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Safe code has no safety requirements, so there is no need to
|
||||
/// describe safety invariants.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::ptr::NonNull;
|
||||
/// let a = &mut 42;
|
||||
///
|
||||
/// // SAFETY: references are guaranteed to be non-null.
|
||||
/// let ptr = NonNull::new(a).unwrap();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::ptr::NonNull;
|
||||
/// let a = &mut 42;
|
||||
///
|
||||
/// let ptr = NonNull::new(a).unwrap();
|
||||
/// ```
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub UNNECESSARY_SAFETY_COMMENT,
|
||||
restriction,
|
||||
"annotating safe code with a safety comment"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
|
||||
declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]);
|
||||
|
||||
impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
|
||||
fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
|
||||
impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
|
||||
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
||||
&& !in_external_macro(cx.tcx.sess, block.span)
|
||||
&& !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
|
||||
|
@ -87,35 +119,175 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
|
|||
"consider adding a safety comment on the preceding line",
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(tail) = block.expr
|
||||
&& !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id)
|
||||
&& !in_external_macro(cx.tcx.sess, tail.span)
|
||||
&& let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id)
|
||||
&& let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos)
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNNECESSARY_SAFETY_COMMENT,
|
||||
tail.span,
|
||||
"expression has unnecessary safety comment",
|
||||
Some(help_span),
|
||||
"consider removing the safety comment",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) {
|
||||
let (
|
||||
hir::StmtKind::Local(&hir::Local { init: Some(expr), .. })
|
||||
| hir::StmtKind::Expr(expr)
|
||||
| hir::StmtKind::Semi(expr)
|
||||
) = stmt.kind else { return };
|
||||
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id)
|
||||
&& !in_external_macro(cx.tcx.sess, stmt.span)
|
||||
&& let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id)
|
||||
&& let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos)
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNNECESSARY_SAFETY_COMMENT,
|
||||
stmt.span,
|
||||
"statement has unnecessary safety comment",
|
||||
Some(help_span),
|
||||
"consider removing the safety comment",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||
if let hir::ItemKind::Impl(imple) = item.kind
|
||||
&& imple.unsafety == hir::Unsafety::Unsafe
|
||||
&& !in_external_macro(cx.tcx.sess, item.span)
|
||||
&& !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
|
||||
&& !is_unsafe_from_proc_macro(cx, item.span)
|
||||
&& !item_has_safety_comment(cx, item)
|
||||
{
|
||||
if in_external_macro(cx.tcx.sess, item.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mk_spans = |pos: BytePos| {
|
||||
let source_map = cx.tcx.sess.source_map();
|
||||
let span = Span::new(pos, pos, SyntaxContext::root(), None);
|
||||
let help_span = source_map.span_extend_to_next_char(span, '\n', true);
|
||||
let span = if source_map.is_multiline(item.span) {
|
||||
source_map.span_until_char(item.span, '\n')
|
||||
} else {
|
||||
item.span
|
||||
};
|
||||
(span, help_span)
|
||||
};
|
||||
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNDOCUMENTED_UNSAFE_BLOCKS,
|
||||
span,
|
||||
"unsafe impl missing a safety comment",
|
||||
None,
|
||||
"consider adding a safety comment on the preceding line",
|
||||
);
|
||||
let item_has_safety_comment = item_has_safety_comment(cx, item);
|
||||
match (&item.kind, item_has_safety_comment) {
|
||||
// lint unsafe impl without safety comment
|
||||
(hir::ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => {
|
||||
if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
|
||||
&& !is_unsafe_from_proc_macro(cx, item.span)
|
||||
{
|
||||
let source_map = cx.tcx.sess.source_map();
|
||||
let span = if source_map.is_multiline(item.span) {
|
||||
source_map.span_until_char(item.span, '\n')
|
||||
} else {
|
||||
item.span
|
||||
};
|
||||
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNDOCUMENTED_UNSAFE_BLOCKS,
|
||||
span,
|
||||
"unsafe impl missing a safety comment",
|
||||
None,
|
||||
"consider adding a safety comment on the preceding line",
|
||||
);
|
||||
}
|
||||
},
|
||||
// lint safe impl with unnecessary safety comment
|
||||
(hir::ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => {
|
||||
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
|
||||
let (span, help_span) = mk_spans(pos);
|
||||
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNNECESSARY_SAFETY_COMMENT,
|
||||
span,
|
||||
"impl has unnecessary safety comment",
|
||||
Some(help_span),
|
||||
"consider removing the safety comment",
|
||||
);
|
||||
}
|
||||
},
|
||||
(hir::ItemKind::Impl(_), _) => {},
|
||||
// const and static items only need a safety comment if their body is an unsafe block, lint otherwise
|
||||
(&hir::ItemKind::Const(.., body) | &hir::ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => {
|
||||
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) {
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if !matches!(
|
||||
body.value.kind, hir::ExprKind::Block(block, _)
|
||||
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
||||
) {
|
||||
let (span, help_span) = mk_spans(pos);
|
||||
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNNECESSARY_SAFETY_COMMENT,
|
||||
span,
|
||||
&format!("{} has unnecessary safety comment", item.kind.descr()),
|
||||
Some(help_span),
|
||||
"consider removing the safety comment",
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Aside from unsafe impls and consts/statics with an unsafe block, items in general
|
||||
// do not have safety invariants that need to be documented, so lint those.
|
||||
(_, HasSafetyComment::Yes(pos)) => {
|
||||
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
|
||||
let (span, help_span) = mk_spans(pos);
|
||||
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNNECESSARY_SAFETY_COMMENT,
|
||||
span,
|
||||
&format!("{} has unnecessary safety comment", item.kind.descr()),
|
||||
Some(help_span),
|
||||
"consider removing the safety comment",
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_has_unnecessary_safety_comment<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
comment_pos: BytePos,
|
||||
) -> Option<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 {
|
||||
let source_map = cx.sess().source_map();
|
||||
let file_pos = source_map.lookup_byte_offset(span.lo());
|
||||
|
@ -170,85 +342,134 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
|
|||
// won't work. This is to avoid dealing with where such a comment should be place relative to
|
||||
// attributes and doc comments.
|
||||
|
||||
span_from_macro_expansion_has_safety_comment(cx, span) || span_in_body_has_safety_comment(cx, span)
|
||||
matches!(
|
||||
span_from_macro_expansion_has_safety_comment(cx, span),
|
||||
HasSafetyComment::Yes(_)
|
||||
) || span_in_body_has_safety_comment(cx, span)
|
||||
}
|
||||
|
||||
enum HasSafetyComment {
|
||||
Yes(BytePos),
|
||||
No,
|
||||
Maybe,
|
||||
}
|
||||
|
||||
/// Checks if the lines immediately preceding the item contain a safety comment.
|
||||
#[allow(clippy::collapsible_match)]
|
||||
fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool {
|
||||
if span_from_macro_expansion_has_safety_comment(cx, item.span) {
|
||||
return true;
|
||||
fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment {
|
||||
match span_from_macro_expansion_has_safety_comment(cx, item.span) {
|
||||
HasSafetyComment::Maybe => (),
|
||||
has_safety_comment => return has_safety_comment,
|
||||
}
|
||||
|
||||
if item.span.ctxt() == SyntaxContext::root() {
|
||||
if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
|
||||
let comment_start = match parent_node {
|
||||
Node::Crate(parent_mod) => {
|
||||
comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item)
|
||||
},
|
||||
Node::Item(parent_item) => {
|
||||
if let ItemKind::Mod(parent_mod) = &parent_item.kind {
|
||||
comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item)
|
||||
} else {
|
||||
// Doesn't support impls in this position. Pretend a comment was found.
|
||||
return true;
|
||||
}
|
||||
},
|
||||
Node::Stmt(stmt) => {
|
||||
if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) {
|
||||
match stmt_parent {
|
||||
Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo),
|
||||
_ => {
|
||||
// Doesn't support impls in this position. Pretend a comment was found.
|
||||
return true;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// Problem getting the parent node. Pretend a comment was found.
|
||||
return true;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if item.span.ctxt() != SyntaxContext::root() {
|
||||
return HasSafetyComment::No;
|
||||
}
|
||||
if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
|
||||
let comment_start = match parent_node {
|
||||
Node::Crate(parent_mod) => {
|
||||
comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item)
|
||||
},
|
||||
Node::Item(parent_item) => {
|
||||
if let ItemKind::Mod(parent_mod) = &parent_item.kind {
|
||||
comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item)
|
||||
} else {
|
||||
// Doesn't support impls in this position. Pretend a comment was found.
|
||||
return true;
|
||||
},
|
||||
};
|
||||
return HasSafetyComment::Maybe;
|
||||
}
|
||||
},
|
||||
Node::Stmt(stmt) => {
|
||||
if let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) {
|
||||
walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo)
|
||||
} else {
|
||||
// Problem getting the parent node. Pretend a comment was found.
|
||||
return HasSafetyComment::Maybe;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// Doesn't support impls in this position. Pretend a comment was found.
|
||||
return HasSafetyComment::Maybe;
|
||||
},
|
||||
};
|
||||
|
||||
let source_map = cx.sess().source_map();
|
||||
if let Some(comment_start) = comment_start
|
||||
&& let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
|
||||
&& let Ok(comment_start_line) = source_map.lookup_line(comment_start)
|
||||
&& Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
|
||||
&& let Some(src) = unsafe_line.sf.src.as_deref()
|
||||
{
|
||||
unsafe_line.sf.lines(|lines| {
|
||||
comment_start_line.line < unsafe_line.line && text_has_safety_comment(
|
||||
let source_map = cx.sess().source_map();
|
||||
if let Some(comment_start) = comment_start
|
||||
&& let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
|
||||
&& let Ok(comment_start_line) = source_map.lookup_line(comment_start)
|
||||
&& Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
|
||||
&& let Some(src) = unsafe_line.sf.src.as_deref()
|
||||
{
|
||||
return unsafe_line.sf.lines(|lines| {
|
||||
if comment_start_line.line >= unsafe_line.line {
|
||||
HasSafetyComment::No
|
||||
} else {
|
||||
match text_has_safety_comment(
|
||||
src,
|
||||
&lines[comment_start_line.line + 1..=unsafe_line.line],
|
||||
unsafe_line.sf.start_pos.to_usize(),
|
||||
)
|
||||
})
|
||||
} else {
|
||||
// Problem getting source text. Pretend a comment was found.
|
||||
true
|
||||
}
|
||||
} else {
|
||||
// No parent node. Pretend a comment was found.
|
||||
true
|
||||
) {
|
||||
Some(b) => HasSafetyComment::Yes(b),
|
||||
None => HasSafetyComment::No,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} 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<'_>,
|
||||
parent_mod: &hir::Mod<'_>,
|
||||
parent_mod_span: Span,
|
||||
imple: &hir::Item<'_>,
|
||||
item: &hir::Item<'_>,
|
||||
) -> Option<BytePos> {
|
||||
parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| {
|
||||
if *item_id == imple.item_id() {
|
||||
if *item_id == item.item_id() {
|
||||
if idx == 0 {
|
||||
// mod A { /* comment */ unsafe impl T {} ... }
|
||||
// ^------------------------------------------^ returns the start of this span
|
||||
|
@ -270,11 +491,11 @@ fn comment_start_before_impl_in_mod(
|
|||
})
|
||||
}
|
||||
|
||||
fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
|
||||
fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment {
|
||||
let source_map = cx.sess().source_map();
|
||||
let ctxt = span.ctxt();
|
||||
if ctxt == SyntaxContext::root() {
|
||||
false
|
||||
HasSafetyComment::Maybe
|
||||
} else {
|
||||
// From a macro expansion. Get the text from the start of the macro declaration to start of the
|
||||
// unsafe block.
|
||||
|
@ -286,15 +507,22 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span
|
|||
&& let Some(src) = unsafe_line.sf.src.as_deref()
|
||||
{
|
||||
unsafe_line.sf.lines(|lines| {
|
||||
macro_line.line < unsafe_line.line && text_has_safety_comment(
|
||||
src,
|
||||
&lines[macro_line.line + 1..=unsafe_line.line],
|
||||
unsafe_line.sf.start_pos.to_usize(),
|
||||
)
|
||||
if macro_line.line < unsafe_line.line {
|
||||
match text_has_safety_comment(
|
||||
src,
|
||||
&lines[macro_line.line + 1..=unsafe_line.line],
|
||||
unsafe_line.sf.start_pos.to_usize(),
|
||||
) {
|
||||
Some(b) => HasSafetyComment::Yes(b),
|
||||
None => HasSafetyComment::No,
|
||||
}
|
||||
} else {
|
||||
HasSafetyComment::No
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// Problem getting source text. Pretend a comment was found.
|
||||
true
|
||||
HasSafetyComment::Maybe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -333,7 +561,7 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
|
|||
src,
|
||||
&lines[body_line.line + 1..=unsafe_line.line],
|
||||
unsafe_line.sf.start_pos.to_usize(),
|
||||
)
|
||||
).is_some()
|
||||
})
|
||||
} else {
|
||||
// Problem getting source text. Pretend a comment was found.
|
||||
|
@ -345,30 +573,34 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
|
|||
}
|
||||
|
||||
/// Checks if the given text has a safety comment for the immediately proceeding line.
|
||||
fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool {
|
||||
fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> Option<BytePos> {
|
||||
let mut lines = line_starts
|
||||
.array_windows::<2>()
|
||||
.rev()
|
||||
.map_while(|[start, end]| {
|
||||
let start = start.to_usize() - offset;
|
||||
let end = end.to_usize() - offset;
|
||||
src.get(start..end).map(|text| (start, text.trim_start()))
|
||||
let text = src.get(start..end)?;
|
||||
let trimmed = text.trim_start();
|
||||
Some((start + (text.len() - trimmed.len()), trimmed))
|
||||
})
|
||||
.filter(|(_, text)| !text.is_empty());
|
||||
|
||||
let Some((line_start, line)) = lines.next() else {
|
||||
return false;
|
||||
return None;
|
||||
};
|
||||
// Check for a sequence of line comments.
|
||||
if line.starts_with("//") {
|
||||
let mut line = line;
|
||||
let (mut line, mut line_start) = (line, line_start);
|
||||
loop {
|
||||
if line.to_ascii_uppercase().contains("SAFETY:") {
|
||||
return true;
|
||||
return Some(BytePos(
|
||||
u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(),
|
||||
));
|
||||
}
|
||||
match lines.next() {
|
||||
Some((_, x)) if x.starts_with("//") => line = x,
|
||||
_ => return false,
|
||||
Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -377,16 +609,19 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) ->
|
|||
let (mut line_start, mut line) = (line_start, line);
|
||||
loop {
|
||||
if line.starts_with("/*") {
|
||||
let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
|
||||
let src = &src[line_start..line_starts.last().unwrap().to_usize() - offset];
|
||||
let mut tokens = tokenize(src);
|
||||
return src[..tokens.next().unwrap().len as usize]
|
||||
return (src[..tokens.next().unwrap().len as usize]
|
||||
.to_ascii_uppercase()
|
||||
.contains("SAFETY:")
|
||||
&& tokens.all(|t| t.kind == TokenKind::Whitespace);
|
||||
&& tokens.all(|t| t.kind == TokenKind::Whitespace))
|
||||
.then_some(BytePos(
|
||||
u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(),
|
||||
));
|
||||
}
|
||||
match lines.next() {
|
||||
Some(x) => (line_start, line) = x,
|
||||
None => return false,
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{meets_msrv, msrvs, over};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::over;
|
||||
use rustc_ast::mut_visit::*;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
|
@ -45,14 +45,13 @@ declare_clippy_lint! {
|
|||
"unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct UnnestedOrPatterns {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl UnnestedOrPatterns {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -61,13 +60,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]);
|
|||
|
||||
impl EarlyLintPass for UnnestedOrPatterns {
|
||||
fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
|
||||
if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
|
||||
if self.msrv.meets(msrvs::OR_PATTERNS) {
|
||||
lint_unnested_or_patterns(cx, &a.pat);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
|
||||
if self.msrv.meets(msrvs::OR_PATTERNS) {
|
||||
if let ast::ExprKind::Let(pat, _, _) = &e.kind {
|
||||
lint_unnested_or_patterns(cx, pat);
|
||||
}
|
||||
|
@ -75,13 +74,13 @@ impl EarlyLintPass for UnnestedOrPatterns {
|
|||
}
|
||||
|
||||
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
|
||||
if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
|
||||
if self.msrv.meets(msrvs::OR_PATTERNS) {
|
||||
lint_unnested_or_patterns(cx, &p.pat);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
|
||||
if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
|
||||
if self.msrv.meets(msrvs::OR_PATTERNS) {
|
||||
lint_unnested_or_patterns(cx, &l.pat);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_ast::ast::{Expr, ExprKind, MethodCall};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
|
@ -29,22 +30,16 @@ declare_clippy_lint! {
|
|||
}
|
||||
declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]);
|
||||
|
||||
fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
|
||||
fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> {
|
||||
if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind
|
||||
&& let method_name = name_ident.ident.name.as_str()
|
||||
&& (method_name == "ceil" || method_name == "round" || method_name == "floor")
|
||||
&& let ExprKind::Lit(token_lit) = &receiver.kind
|
||||
&& token_lit.is_semantic_float() {
|
||||
let mut f_str = token_lit.symbol.to_string();
|
||||
let f = f_str.trim_end_matches('_').parse::<f64>().unwrap();
|
||||
if let Some(suffix) = token_lit.suffix {
|
||||
f_str.push_str(suffix.as_str());
|
||||
}
|
||||
if f.fract() == 0.0 {
|
||||
Some((method_name, f_str))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
&& token_lit.is_semantic_float()
|
||||
&& let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::<f64>() {
|
||||
(f.fract() == 0.0).then(||
|
||||
(method_name, snippet(cx, receiver.span, "..").to_string())
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -52,7 +47,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
|
|||
|
||||
impl EarlyLintPass for UnusedRounding {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let Some((method_name, float)) = is_useless_rounding(expr) {
|
||||
if let Some((method_name, float)) = is_useless_rounding(cx, expr) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNUSED_ROUNDING,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::ty::same_type_and_consts;
|
||||
use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -14,7 +15,6 @@ use rustc_hir::{
|
|||
};
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
|
||||
|
@ -57,13 +57,13 @@ declare_clippy_lint! {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct UseSelf {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
stack: Vec<StackItem>,
|
||||
}
|
||||
|
||||
impl UseSelf {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
..Self::default()
|
||||
|
@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
|||
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
|
||||
if_chain! {
|
||||
if !hir_ty.span.from_expansion();
|
||||
if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
|
||||
if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
|
||||
if let Some(&StackItem::Check {
|
||||
impl_id,
|
||||
in_body,
|
||||
|
@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
|||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion();
|
||||
if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
|
||||
if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
|
||||
if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
|
||||
if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
|
||||
then {} else { return; }
|
||||
|
@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
|||
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
|
||||
if_chain! {
|
||||
if !pat.span.from_expansion();
|
||||
if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
|
||||
if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS);
|
||||
if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
|
||||
// get the path from the pattern
|
||||
if let PatKind::Path(QPath::Resolved(_, path))
|
||||
|
|
|
@ -402,6 +402,10 @@ define_Conf! {
|
|||
/// A list of paths to types that should be treated like `Arc`, i.e. ignored but
|
||||
/// for the generic parameters for determining interior mutability
|
||||
(ignore_interior_mutability: Vec<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.
|
||||
|
|
|
@ -41,7 +41,7 @@ impl LateLintPass<'_> for MsrvAttrImpl {
|
|||
.type_of(f.did)
|
||||
.walk()
|
||||
.filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
|
||||
.any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
|
||||
.any(|t| match_type(cx, t.expect_ty(), &paths::MSRV))
|
||||
});
|
||||
if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
|
||||
then {
|
||||
|
|
|
@ -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> {
|
||||
let mut unique_attr = None;
|
||||
pub fn get_unique_attr<'a>(
|
||||
sess: &'a Session,
|
||||
attrs: &'a [ast::Attribute],
|
||||
name: &'static str,
|
||||
) -> Option<&'a ast::Attribute> {
|
||||
let mut unique_attr: Option<&ast::Attribute> = None;
|
||||
for attr in get_attr(sess, attrs, name) {
|
||||
match attr.style {
|
||||
ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
|
||||
ast::AttrStyle::Inner => {
|
||||
sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
|
||||
.span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
|
||||
.emit();
|
||||
},
|
||||
ast::AttrStyle::Outer => {
|
||||
sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute"));
|
||||
},
|
||||
if let Some(duplicate) = unique_attr {
|
||||
sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
|
||||
.span_note(duplicate.span, "first definition found here")
|
||||
.emit();
|
||||
} else {
|
||||
unique_attr = Some(attr);
|
||||
}
|
||||
}
|
||||
unique_attr
|
||||
|
|
|
@ -91,6 +91,16 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg:
|
|||
}
|
||||
}
|
||||
|
||||
fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res {
|
||||
cx.typeck_results()
|
||||
.expr_ty(e)
|
||||
.has_significant_drop(cx.tcx, cx.param_env)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
|
||||
struct V<'cx, 'tcx> {
|
||||
|
@ -113,13 +123,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
|||
},
|
||||
args,
|
||||
) => match self.cx.qpath_res(path, hir_id) {
|
||||
Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => {
|
||||
if self
|
||||
.cx
|
||||
.typeck_results()
|
||||
.expr_ty(e)
|
||||
.has_significant_drop(self.cx.tcx, self.cx.param_env)
|
||||
{
|
||||
res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => {
|
||||
if res_has_significant_drop(res, self.cx, e) {
|
||||
self.eagerness = ForceNoChange;
|
||||
return;
|
||||
}
|
||||
|
@ -147,6 +152,12 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
|||
self.eagerness |= NoChange;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(ref path) => {
|
||||
if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) {
|
||||
self.eagerness = ForceNoChange;
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(name, ..) => {
|
||||
self.eagerness |= self
|
||||
.cx
|
||||
|
@ -206,7 +217,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
|||
| ExprKind::Match(..)
|
||||
| ExprKind::Closure { .. }
|
||||
| ExprKind::Field(..)
|
||||
| ExprKind::Path(_)
|
||||
| ExprKind::AddrOf(..)
|
||||
| ExprKind::Struct(..)
|
||||
| ExprKind::Repeat(..)
|
||||
|
|
|
@ -105,8 +105,6 @@ use rustc_middle::ty::{
|
|||
layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
|
||||
};
|
||||
use rustc_middle::ty::{FloatTy, IntTy, UintTy};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::sym;
|
||||
|
@ -118,36 +116,17 @@ use crate::consts::{constant, Constant};
|
|||
use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
|
||||
use crate::visitors::for_each_expr;
|
||||
|
||||
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<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_rules! extract_msrv_attr {
|
||||
($context:ident) => {
|
||||
fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
|
||||
let sess = rustc_lint::LintContext::sess(cx);
|
||||
match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
|
||||
Some(msrv_attr) => {
|
||||
if let Some(msrv) = msrv_attr.value_str() {
|
||||
self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
|
||||
} else {
|
||||
sess.span_err(msrv_attr.span, "bad clippy attribute");
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
self.msrv.enter_lint_attrs(sess, attrs);
|
||||
}
|
||||
|
||||
fn exit_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
|
||||
let sess = rustc_lint::LintContext::sess(cx);
|
||||
self.msrv.exit_lint_attrs(sess, attrs);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
use std::sync::OnceLock;
|
||||
|
||||
use rustc_ast::Attribute;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::attrs::get_unique_attr;
|
||||
|
||||
macro_rules! msrv_aliases {
|
||||
($($major:literal,$minor:literal,$patch:literal {
|
||||
|
@ -40,3 +47,97 @@ msrv_aliases! {
|
|||
1,16,0 { STR_REPEAT }
|
||||
1,55,0 { SEEK_REWIND }
|
||||
}
|
||||
|
||||
fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<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")]
|
||||
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
|
||||
pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
|
||||
#[cfg(feature = "internal")]
|
||||
pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];
|
||||
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
|
||||
pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
|
||||
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
|
||||
|
@ -72,7 +74,6 @@ pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekab
|
|||
pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
|
||||
#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
|
||||
pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
|
||||
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
|
||||
pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
|
||||
pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
|
||||
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
|
||||
|
@ -101,8 +102,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
|
|||
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
|
||||
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
|
||||
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
|
||||
#[cfg(feature = "internal")]
|
||||
pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
|
||||
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
|
||||
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
|
||||
pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
|
||||
// differ from the time of `rustc` even if the name stays the same.
|
||||
|
||||
use crate::msrvs::Msrv;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir::{
|
||||
|
@ -18,20 +19,22 @@ use std::borrow::Cow;
|
|||
|
||||
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
||||
|
||||
pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option<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 mut current = def_id;
|
||||
loop {
|
||||
let predicates = tcx.predicates_of(current);
|
||||
for (predicate, _) in predicates.predicates {
|
||||
match predicate.kind().skip_binder() {
|
||||
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_))
|
||||
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_))
|
||||
ty::PredicateKind::Clause(
|
||||
ty::Clause::RegionOutlives(_)
|
||||
| ty::Clause::TypeOutlives(_)
|
||||
| ty::Clause::Projection(_)
|
||||
| ty::Clause::Trait(..),
|
||||
)
|
||||
| ty::PredicateKind::WellFormed(_)
|
||||
| ty::PredicateKind::Clause(ty::Clause::Projection(_))
|
||||
| ty::PredicateKind::ConstEvaluatable(..)
|
||||
| ty::PredicateKind::ConstEquate(..)
|
||||
| ty::PredicateKind::Clause(ty::Clause::Trait(..))
|
||||
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
|
||||
ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"),
|
||||
ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"),
|
||||
|
@ -281,7 +284,7 @@ fn check_terminator<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
terminator: &Terminator<'tcx>,
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: &Msrv,
|
||||
) -> McfResult {
|
||||
let span = terminator.source_info.span;
|
||||
match &terminator.kind {
|
||||
|
@ -365,7 +368,7 @@ fn check_terminator<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
|
||||
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
|
||||
tcx.is_const_fn(def_id)
|
||||
&& tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
|
||||
if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level {
|
||||
|
@ -384,15 +387,12 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bo
|
|||
|
||||
let since = rustc_span::Symbol::intern(short_version);
|
||||
|
||||
crate::meets_msrv(
|
||||
msrv,
|
||||
RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
|
||||
panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
|
||||
}),
|
||||
)
|
||||
msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
|
||||
panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
|
||||
}))
|
||||
} else {
|
||||
// Unstable const fn with the feature enabled.
|
||||
msrv.is_none()
|
||||
msrv.current().is_none()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene;
|
||||
use rustc_span::source_map::{original_sp, SourceMap};
|
||||
use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
|
||||
|
@ -204,11 +205,20 @@ pub fn snippet_with_applicability<'a, T: LintContext>(
|
|||
span: Span,
|
||||
default: &'a str,
|
||||
applicability: &mut Applicability,
|
||||
) -> Cow<'a, str> {
|
||||
snippet_with_applicability_sess(cx.sess(), span, default, applicability)
|
||||
}
|
||||
|
||||
fn snippet_with_applicability_sess<'a>(
|
||||
sess: &Session,
|
||||
span: Span,
|
||||
default: &'a str,
|
||||
applicability: &mut Applicability,
|
||||
) -> Cow<'a, str> {
|
||||
if *applicability != Applicability::Unspecified && span.from_expansion() {
|
||||
*applicability = Applicability::MaybeIncorrect;
|
||||
}
|
||||
snippet_opt(cx, span).map_or_else(
|
||||
snippet_opt_sess(sess, span).map_or_else(
|
||||
|| {
|
||||
if *applicability == Applicability::MachineApplicable {
|
||||
*applicability = Applicability::HasPlaceholders;
|
||||
|
@ -226,8 +236,12 @@ pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, defau
|
|||
}
|
||||
|
||||
/// Converts a span to a code snippet. Returns `None` if not available.
|
||||
pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
||||
cx.sess().source_map().span_to_snippet(span).ok()
|
||||
pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
|
||||
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.
|
||||
|
@ -277,8 +291,8 @@ pub fn snippet_block<'a, T: LintContext>(
|
|||
|
||||
/// Same as `snippet_block`, but adapts the applicability level by the rules of
|
||||
/// `snippet_with_applicability`.
|
||||
pub fn snippet_block_with_applicability<'a, T: LintContext>(
|
||||
cx: &T,
|
||||
pub fn snippet_block_with_applicability<'a>(
|
||||
cx: &impl LintContext,
|
||||
span: Span,
|
||||
default: &'a str,
|
||||
indent_relative_to: Option<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.
|
||||
pub fn snippet_with_context<'a>(
|
||||
cx: &LateContext<'_>,
|
||||
cx: &impl LintContext,
|
||||
span: Span,
|
||||
outer: SyntaxContext,
|
||||
default: &'a str,
|
||||
applicability: &mut Applicability,
|
||||
) -> (Cow<'a, str>, bool) {
|
||||
snippet_with_context_sess(cx.sess(), span, outer, default, applicability)
|
||||
}
|
||||
|
||||
fn snippet_with_context_sess<'a>(
|
||||
sess: &Session,
|
||||
span: Span,
|
||||
outer: SyntaxContext,
|
||||
default: &'a str,
|
||||
|
@ -318,7 +342,7 @@ pub fn snippet_with_context<'a>(
|
|||
);
|
||||
|
||||
(
|
||||
snippet_with_applicability(cx, span, default, applicability),
|
||||
snippet_with_applicability_sess(sess, span, default, applicability),
|
||||
is_macro_call,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -176,25 +176,28 @@ impl<'a> Sugg<'a> {
|
|||
}
|
||||
|
||||
/// Prepare a suggestion from an expression.
|
||||
pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
|
||||
pub fn ast(
|
||||
cx: &EarlyContext<'_>,
|
||||
expr: &ast::Expr,
|
||||
default: &'a str,
|
||||
ctxt: SyntaxContext,
|
||||
app: &mut Applicability,
|
||||
) -> Self {
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
|
||||
let snippet_without_expansion = |cx, span: Span, default| {
|
||||
if span.from_expansion() {
|
||||
snippet_with_macro_callsite(cx, span, default)
|
||||
} else {
|
||||
snippet(cx, span, default)
|
||||
}
|
||||
};
|
||||
|
||||
#[expect(clippy::match_wildcard_for_single_variants)]
|
||||
match expr.kind {
|
||||
_ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
|
||||
ast::ExprKind::AddrOf(..)
|
||||
| ast::ExprKind::Box(..)
|
||||
| ast::ExprKind::Closure { .. }
|
||||
| ast::ExprKind::If(..)
|
||||
| ast::ExprKind::Let(..)
|
||||
| ast::ExprKind::Unary(..)
|
||||
| ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)),
|
||||
| ast::ExprKind::Match(..) => match snippet_with_context(cx, expr.span, ctxt, default, app) {
|
||||
(snip, false) => Sugg::MaybeParen(snip),
|
||||
(snip, true) => Sugg::NonParen(snip),
|
||||
},
|
||||
ast::ExprKind::Async(..)
|
||||
| ast::ExprKind::Block(..)
|
||||
| ast::ExprKind::Break(..)
|
||||
|
@ -224,45 +227,49 @@ impl<'a> Sugg<'a> {
|
|||
| ast::ExprKind::Array(..)
|
||||
| ast::ExprKind::While(..)
|
||||
| ast::ExprKind::Await(..)
|
||||
| ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)),
|
||||
| ast::ExprKind::Err => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
|
||||
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
|
||||
AssocOp::DotDot,
|
||||
lhs.as_ref()
|
||||
.map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
|
||||
rhs.as_ref()
|
||||
.map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
|
||||
lhs.as_ref().map_or("".into(), |lhs| {
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0
|
||||
}),
|
||||
rhs.as_ref().map_or("".into(), |rhs| {
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0
|
||||
}),
|
||||
),
|
||||
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
|
||||
AssocOp::DotDotEq,
|
||||
lhs.as_ref()
|
||||
.map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
|
||||
rhs.as_ref()
|
||||
.map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
|
||||
lhs.as_ref().map_or("".into(), |lhs| {
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0
|
||||
}),
|
||||
rhs.as_ref().map_or("".into(), |rhs| {
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0
|
||||
}),
|
||||
),
|
||||
ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
|
||||
AssocOp::Assign,
|
||||
snippet_without_expansion(cx, lhs.span, default),
|
||||
snippet_without_expansion(cx, rhs.span, default),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||
),
|
||||
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
|
||||
astbinop2assignop(op),
|
||||
snippet_without_expansion(cx, lhs.span, default),
|
||||
snippet_without_expansion(cx, rhs.span, default),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||
),
|
||||
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
|
||||
AssocOp::from_ast_binop(op.node),
|
||||
snippet_without_expansion(cx, lhs.span, default),
|
||||
snippet_without_expansion(cx, rhs.span, default),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
|
||||
),
|
||||
ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
|
||||
AssocOp::As,
|
||||
snippet_without_expansion(cx, lhs.span, default),
|
||||
snippet_without_expansion(cx, ty.span, default),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, ty.span, ctxt, default, app).0,
|
||||
),
|
||||
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
|
||||
AssocOp::Colon,
|
||||
snippet_without_expansion(cx, lhs.span, default),
|
||||
snippet_without_expansion(cx, ty.span, default),
|
||||
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
|
||||
snippet_with_context(cx, ty.span, ctxt, default, app).0,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,10 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
|
||||
use rustc_infer::infer::{TyCtxtInferExt, type_variable::{TypeVariableOrigin, TypeVariableOriginKind}};
|
||||
use rustc_infer::infer::{
|
||||
type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
|
||||
TyCtxtInferExt,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::interpret::{ConstValue, Scalar};
|
||||
use rustc_middle::ty::{
|
||||
|
@ -189,7 +192,13 @@ pub fn implements_trait<'tcx>(
|
|||
trait_id: DefId,
|
||||
ty_params: &[GenericArg<'tcx>],
|
||||
) -> bool {
|
||||
implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params.iter().map(|&arg| Some(arg)))
|
||||
implements_trait_with_env(
|
||||
cx.tcx,
|
||||
cx.param_env,
|
||||
ty,
|
||||
trait_id,
|
||||
ty_params.iter().map(|&arg| Some(arg)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
|
||||
|
@ -212,7 +221,11 @@ pub fn implements_trait_with_env<'tcx>(
|
|||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
span: DUMMY_SP,
|
||||
};
|
||||
let ty_params = tcx.mk_substs(ty_params.into_iter().map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())));
|
||||
let ty_params = tcx.mk_substs(
|
||||
ty_params
|
||||
.into_iter()
|
||||
.map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())),
|
||||
);
|
||||
infcx
|
||||
.type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
|
||||
.must_apply_modulo_regions()
|
||||
|
@ -712,7 +725,9 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
|
|||
}
|
||||
inputs = Some(i);
|
||||
},
|
||||
PredicateKind::Clause(ty::Clause::Projection(p)) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
|
||||
PredicateKind::Clause(ty::Clause::Projection(p))
|
||||
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() =>
|
||||
{
|
||||
if output.is_some() {
|
||||
// Multiple different fn trait impls. Is this even allowed?
|
||||
return None;
|
||||
|
@ -992,14 +1007,12 @@ pub fn make_projection<'tcx>(
|
|||
|
||||
debug_assert!(
|
||||
generic_count == substs.len(),
|
||||
"wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\
|
||||
"wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\
|
||||
note: the expected parameters are: {:#?}\n\
|
||||
the given arguments are: `{:#?}`",
|
||||
the given arguments are: `{substs:#?}`",
|
||||
assoc_item.def_id,
|
||||
substs.len(),
|
||||
generic_count,
|
||||
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
|
||||
substs,
|
||||
);
|
||||
|
||||
if let Some((idx, (param, arg))) = params
|
||||
|
@ -1017,14 +1030,11 @@ pub fn make_projection<'tcx>(
|
|||
{
|
||||
debug_assert!(
|
||||
false,
|
||||
"mismatched subst type at index {}: expected a {}, found `{:?}`\n\
|
||||
"mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\
|
||||
note: the expected parameters are {:#?}\n\
|
||||
the given arguments are {:#?}",
|
||||
idx,
|
||||
the given arguments are {substs:#?}",
|
||||
param.descr(),
|
||||
arg,
|
||||
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
|
||||
substs,
|
||||
params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,22 +170,22 @@ where
|
|||
cb: F,
|
||||
}
|
||||
|
||||
struct WithStmtGuarg<'a, F> {
|
||||
struct WithStmtGuard<'a, F> {
|
||||
val: &'a mut RetFinder<F>,
|
||||
prev_in_stmt: bool,
|
||||
}
|
||||
|
||||
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);
|
||||
WithStmtGuarg {
|
||||
WithStmtGuard {
|
||||
val: self,
|
||||
prev_in_stmt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
|
||||
impl<F> std::ops::Deref for WithStmtGuard<'_, F> {
|
||||
type Target = RetFinder<F>;
|
||||
|
||||
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 {
|
||||
self.val
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Drop for WithStmtGuarg<'_, F> {
|
||||
impl<F> Drop for WithStmtGuard<'_, F> {
|
||||
fn drop(&mut self) {
|
||||
self.val.in_stmt = self.prev_in_stmt;
|
||||
}
|
||||
|
|
|
@ -120,8 +120,8 @@ impl ClippyWarning {
|
|||
format!("$CARGO_HOME/{}", stripped.display())
|
||||
} else {
|
||||
format!(
|
||||
"target/lintcheck/sources/{}-{}/{}",
|
||||
crate_name, crate_version, span.file_name
|
||||
"target/lintcheck/sources/{crate_name}-{crate_version}/{}",
|
||||
span.file_name
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -322,13 +322,13 @@ impl Crate {
|
|||
|
||||
if config.max_jobs == 1 {
|
||||
println!(
|
||||
"{}/{} {}% Linting {} {}",
|
||||
index, total_crates_to_lint, perc, &self.name, &self.version
|
||||
"{index}/{total_crates_to_lint} {perc}% Linting {} {}",
|
||||
&self.name, &self.version
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"{}/{} {}% Linting {} {} in target dir {:?}",
|
||||
index, total_crates_to_lint, perc, &self.name, &self.version, thread_index
|
||||
"{index}/{total_crates_to_lint} {perc}% Linting {} {} in target dir {thread_index:?}",
|
||||
&self.name, &self.version
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -398,8 +398,7 @@ impl Crate {
|
|||
.output()
|
||||
.unwrap_or_else(|error| {
|
||||
panic!(
|
||||
"Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
|
||||
error,
|
||||
"Encountered error:\n{error:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
|
||||
&cargo_clippy_path.display(),
|
||||
&self.path.display()
|
||||
);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#![feature(rustc_private)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(once_cell)]
|
||||
#![feature(lint_reasons)]
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
|
@ -90,6 +91,10 @@ fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option<String>) {
|
|||
|
||||
// During development track the `clippy-driver` executable so that cargo will re-run clippy whenever
|
||||
// it is rebuilt
|
||||
#[expect(
|
||||
clippy::collapsible_if,
|
||||
reason = "Due to a bug in let_chains this if statement can't be collapsed"
|
||||
)]
|
||||
if cfg!(debug_assertions) {
|
||||
if let Ok(current_exe) = env::current_exe()
|
||||
&& let Some(current_exe) = current_exe.to_str()
|
||||
|
|
|
@ -11,9 +11,9 @@ extern crate rustc_middle;
|
|||
#[macro_use]
|
||||
extern crate rustc_session;
|
||||
use clippy_utils::extract_msrv_attr;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
declare_lint! {
|
||||
pub TEST_LINT,
|
||||
|
@ -22,7 +22,7 @@ declare_lint! {
|
|||
}
|
||||
|
||||
struct Pass {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Pass => [TEST_LINT]);
|
||||
|
|
|
@ -11,9 +11,9 @@ extern crate rustc_middle;
|
|||
#[macro_use]
|
||||
extern crate rustc_session;
|
||||
use clippy_utils::extract_msrv_attr;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
declare_lint! {
|
||||
pub TEST_LINT,
|
||||
|
@ -22,7 +22,7 @@ declare_lint! {
|
|||
}
|
||||
|
||||
struct Pass {
|
||||
msrv: Option<RustcVersion>,
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
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
|
||||
--> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36
|
||||
|
|
||||
|
@ -14,6 +5,7 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
|
|||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: convert all references to use `sym::Deref`
|
||||
= note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
|
||||
|
||||
error: hardcoded path to a diagnostic item
|
||||
--> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
|
||||
|
@ -23,5 +15,13 @@ LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref",
|
|||
|
|
||||
= help: convert all references to use `sym::deref_method`
|
||||
|
||||
error: hardcoded path to a language item
|
||||
--> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
|
||||
|
|
||||
LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: convert all references to use `LangItem::DerefMut`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue