[`unnecessary_unwrap`]: lint on `.as_ref().unwrap()`
Closes#11371
This turned out to be a little more code than I originally thought, because the lint also makes sure to not lint if the user tries to mutate the option:
```rs
if option.is_some() {
option = None;
option.unwrap(); // don't lint here
}
```
... which means that even if we taught this lint to recognize `.as_mut()`, it would *still* not lint because that would count as a mutation. So we need to allow `.as_mut()` calls but reject other kinds of mutations.
Unfortunately it doesn't look like this is possible with `is_potentially_mutated` (seeing what kind of mutation happened).
This replaces it with a custom little visitor that does basically what it did before, but also allows `.as_mut()`.
changelog: [`unnecessary_unwrap`]: lint on `.as_ref().unwrap()`
skip float_cmp check if lhs is a custom type
*Please write a short comment explaining your change (or "none" for internal only changes)*
changelog: [`float_cmp`]: allow float eq comparison when lhs is a custom type that implements PartialEq<f32/f64>
If the lhs of a comparison is not float, it means there is a user implemented PartialEq, and the caller is invoking that custom version of `==`, instead of the default floating point equal comparison.
People may wrap f32 with a struct (say `MyF32`) and implement its PartialEq that will do the `is_close()` check, so that `MyF32` can be compared with either f32 or `MyF32`.
[`if_then_some_else_none`]: look into local initializers for early returns
Fixes#11394
As the PR title says, problem was that it only looked for early returns in semi statements. Local variables don't count as such, so it didn't count `let _v = x?;` (or even just `let _ = return;`) as a possible early return and didn't realize that it can't lint then.
Imo the `stmts_contains_early_return` function that was used before is redundant. `contains_return` could already do that if we just made the parameter a bit more generic, just like `for_each_expr`, which can already accept `&[Stmt]`
changelog: [`if_then_some_else_none`]: look into local initializers for early returns
fix the uitest `enum_clike_unportable_variant`
*Please write a short comment explaining your change (or "none" for internal only changes)*
changelog: none
fix "derivable_impls: attributes are ignored"
*Please write a short comment explaining your change (or "none" for internal only changes)*
changelog: [`derivable_impls`]: allow the lint when the trait-impl methods has any attribute.
Update changelog stable note
Roses are red,
violets are blue,
new release,
tiny todo
---
![cat gif](https://cataas.com/cat/SbbeZwoC81vSTzBX)
---
changelog: none
new lint: `implied_bounds_in_impls`
Closes#10849
A new lint that looks for explicitly specified bounds that are already implied by other bounds in `impl Trait` return position types.
One example, as shown in the linked example, would be
```rs
fn foo<T>(x: T) -> impl Deref<Target = T> + DerefMut<Target = T> {
Box::new(x)
}
```
`DerefMut<Target = T>` requires `Deref<Target = T>` to be wellformed, so specifying `Deref` there is unnecessary.
This currently "ignores" (i.e., does not lint at all) transitive supertrait bounds (e.g. `trait A {} trait B: A {} trait C: B {}`, then having an `impl A + C` type), because that seems a bit more difficult and I think this isn't technically a blocker. It leads to FNs, but shouldn't bring any FPs
changelog: new lint [`implied_bounds_in_impls`]
Added new lint: `reserve_after_initialization`
Closes https://github.com/rust-lang/rust-clippy/issues/11330.
A new lint that informs the user about a more concise way to create a vector with a known capacity.
Example:
```rust
let mut v: Vec<usize> = vec![];
v.reserve(10);
```
Produces the following help:
```rust
|
2 | / let mut v: Vec<usize> = vec![];
3 | | v.reserve(10);
| |__________________^ help: consider using `Vec::with_capacity(space_hint)`: `let v: Vec<usize> = Vec::with_capacity(10);`
|
```
And can be rewritten as:
```rust
let v: Vec<usize> = Vec::with_capacity(10);
```
changelog: new lint [`reserve_after_initialization`]
Fix tuple_array_conversions lint on nightly
```
changelog: ICE: [`tuple_array_conversions`]: Don't expect array length to always be usize
```
tl;dr: changed [`Const::eval_target_usize`](https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/consts.rs#L359) to [`Consts::try_eval_target_usize`](https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/consts.rs#L327) to get rid of ICE.
I have encountered a problem with clippy: it caught ICE when working with a codebase that uses a lot of nightly features.
Here's a (stripped) ICE info:
```
error: internal compiler error: /rustc/5c6a7e71cd66705c31c9af94077901a220f0870c/compiler/rustc_middle/src/ty/consts.rs:361:32: expected usize, got Const { ty: usize, kind: N/#1 }
thread 'rustc' panicked at /rustc/5c6a7e71cd66705c31c9af94077901a220f0870c/compiler/rustc_errors/src/lib.rs:1635:9:
Box<dyn Any>
stack backtrace:
...
16: 0x110b9c590 - rustc_middle[449edf845976488d]::util:🐛:bug_fmt
17: 0x102f76ae0 - clippy_lints[71754038dd04c2d2]::tuple_array_conversions::all_bindings_are_for_conv
...
```
I don't really know what's going on low-level-wise, but seems like this lin assumed that the length of the array can always be treated as `usize`, and *I assume* this doesn't play well with `feat(generic_const_exprs)`.
I wasn't able to build a minimal reproducible example, but locally this fix does resolve the issue.
Add error annotations in UI tests
As discussed on [zulip](https://rust-lang.zulipchat.com/#narrow/stream/257328-clippy/topic/Improve.20UI.20error.20checks), this PR adds missing error annotations in UI tests.
I used this script to generate them:
<details>
```python
import os
def handle_err(line, stderr, elems, kind, i):
msg = line.split("{}: ".format(kind), 1)[1]
i += 1
try:
line_nb = int(stderr[i].split(":")[1])
except Exception:
return i
in_macro = False
note_found = False
help_found = False
elem = {"kind": kind, "msg": msg, "line": line_nb, "notes": [], "helps": []}
while i < len(stderr):
if len(stderr[i]) == 0:
break
elif stderr[i].startswith("note:"):
note_found = True # error checker doesn't like multi-note apparently.
elif stderr[i].startswith(" = note:"):
if not note_found and not help_found and "this error originates in the macro" not in stderr[i]:
elem["notes"].append(stderr[i].split(" = note:", 1)[1].strip())
note_found = True # error checker doesn't like multi-note apparently.
elif stderr[i].startswith(" = help:") or stderr[i].startswith("help:"):
help_found = True
# elif stderr[i].startswith("help:"):
# if not help_found:
# elem["helps"].append(stderr[i].split("help:", 1)[1].strip())
# help_found = True # error checker doesn't like multi-help apparently.
elif "in this macro invocation" in stderr[i]:
in_macro = True
i += 1
if not in_macro:
elems.append(elem)
return i
def show_kind(kind):
if kind == "error":
return "ERROR"
elif kind == "warning":
return "WARNING"
elif kind == "note":
return "NOTE"
return "HELP"
def generate_code_err(indent, elem, up):
content = "{}//~{} {}: {}".format(indent, up, show_kind(elem["kind"]), elem["msg"])
if up == "^":
up = "|"
for note in elem["notes"]:
content += "\n{}//~{} {}: {}".format(indent, up, show_kind("note"), note)
for help_msg in elem["helps"]:
content += "\n{}//~{} {}: {}".format(indent, up, show_kind("help"), help_msg)
return content, up
def update_content(p, content):
TO_IGNORE = [
"needless_bool/simple.rs", # https://github.com/rust-lang/rust-clippy/issues/11248
"crashes/ice-7868.rs", # Has errors but in another file.
"trivially_copy_pass_by_ref.rs", # the `N` in the stderr needs to be replaced by the number
"tests/ui/large_types_passed_by_value.rs", # the `N` in the stderr needs to be replaced by the number
]
for to_ignore in TO_IGNORE:
if p.endswith(to_ignore):
return
try:
with open(p.replace(".rs", ".stderr"), "r", encoding="utf8") as f:
stderr = f.read().split('\n')
except Exception:
return
print("Updating `{}`".format(p))
i = 0
elems = []
while i < len(stderr):
line = stderr[i]
if line.startswith("error: ") and not line.startswith("error: aborting due to"):
i = handle_err(line, stderr, elems, "error", i)
elif line.startswith("warning: ") and not line.endswith("warning emitted") and line.endswith("warnings emitted"):
i = handle_err(line, stderr, elems, "warning", i)
i += 1
elems.sort(key=lambda e: e["line"], reverse=True)
i = 0
while i < len(elems):
elem = elems[i]
indent = ""
c = 0
line = content[elem["line"] - 1]
while c < len(line) and line[c] == ' ':
indent += " "
c += 1
new_content, up = generate_code_err(indent, elem, "^")
i += 1
while i < len(elems) and elems[i]["line"] == elem["line"]:
elem = elems[i]
ret = generate_code_err(indent, elem, up)
new_content += "\n" + ret[0]
up = ret[1]
i += 1
content.insert(elem["line"], new_content)
with open(p, "w", encoding="utf8") as f:
f.write("\n".join(content))
def check_if_contains_ui_test(p):
if not p.endswith(".rs"):
return
with open(p, "r", encoding="utf8") as f:
x = f.read()
if "//~" not in x and "`@run-rustfix"` not in x and "`@aux-build"` not in x:
update_content(p, x.split("\n"))
for path, subdirs, files in os.walk("tests/ui"):
for name in files:
check_if_contains_ui_test(os.path.join(path, name))
```
</details>
Then ran `cargo uibless`.
changelog: none