Auto merge of #106266 - matthiaskrgr:rollup-cxrdbzy, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - #104531 (Provide a better error and a suggestion for `Fn` traits with lifetime params)
 - #105899 (`./x doc library --open` opens `std`)
 - #106190 (Account for multiple multiline spans with empty padding)
 - #106202 (Trim more paths in obligation types)
 - #106234 (rustdoc: simplify settings, help, and copy button CSS by not reusing)
 - #106236 (docs/test: add docs and a UI test for `E0514` and `E0519`)
 - #106259 (Update Clippy)
 - #106260 (Fix index out of bounds issues in rustdoc)
 - #106263 (Formatter should not try to format non-Rust files)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2022-12-29 19:40:06 +00:00
commit ad8ae0504c
128 changed files with 2905 additions and 436 deletions

View file

@ -276,10 +276,12 @@ E0509: include_str!("./error_codes/E0509.md"),
E0510: include_str!("./error_codes/E0510.md"),
E0511: include_str!("./error_codes/E0511.md"),
E0512: include_str!("./error_codes/E0512.md"),
E0514: include_str!("./error_codes/E0514.md"),
E0515: include_str!("./error_codes/E0515.md"),
E0516: include_str!("./error_codes/E0516.md"),
E0517: include_str!("./error_codes/E0517.md"),
E0518: include_str!("./error_codes/E0518.md"),
E0519: include_str!("./error_codes/E0519.md"),
E0520: include_str!("./error_codes/E0520.md"),
E0521: include_str!("./error_codes/E0521.md"),
E0522: include_str!("./error_codes/E0522.md"),
@ -615,8 +617,6 @@ E0791: include_str!("./error_codes/E0791.md"),
// E0488, // lifetime of variable does not enclose its declaration
// E0489, // type/lifetime parameter not in scope here
E0490, // a value of type `..` is borrowed for too long
E0514, // metadata version mismatch
E0519, // local crate and dependency have same (crate-name, disambiguator)
E0523, // two dependencies have same (crate-name, disambiguator) but different SVH
// E0526, // shuffle indices are not constant
// E0540, // multiple rustc_deprecated attributes

View file

@ -0,0 +1,33 @@
Dependency compiled with different version of `rustc`.
Example of erroneous code:
`a.rs`
```ignore (cannot-link-with-other-tests)
// compiled with stable `rustc`
#[crate_type = "lib"]
```
`b.rs`
```ignore (cannot-link-with-other-tests)
// compiled with nightly `rustc`
#[crate_type = "lib"]
extern crate a; // error: found crate `a` compiled by an incompatible version
// of rustc
```
This error is caused when the version of `rustc` used to compile a crate, as
stored in the binary's metadata, differs from the version of one of its
dependencies. Many parts of Rust binaries are considered unstable. For
instance, the Rust ABI is not stable between compiler versions. This means that
the compiler cannot be sure about *how* to call a function between compiler
versions, and therefore this error occurs.
This error can be fixed by:
* Using [Cargo](../cargo/index.html), the Rust package manager and
[Rustup](https://rust-lang.github.io/rustup/), the Rust toolchain installer,
automatically fixing this issue.
* Recompiling the crates with a uniform `rustc` version.

View file

@ -0,0 +1,40 @@
The current crate is indistinguishable from one of its dependencies, in terms
of metadata.
Example of erroneous code:
`a.rs`
```ignore (cannot-link-with-other-tests)
#![crate_name = "a"]
#![crate_type = "lib"]
pub fn foo() {}
```
`b.rs`
```ignore (cannot-link-with-other-tests)
#![crate_name = "a"]
#![crate_type = "lib"]
// error: the current crate is indistinguishable from one of its dependencies:
// it has the same crate-name `a` and was compiled with the same
// `-C metadata` arguments. This will result in symbol conflicts between
// the two.
extern crate a;
pub fn foo() {}
fn bar() {
a::foo(); // is this calling the local crate or the dependency?
}
```
The above example compiles two crates with exactly the same name and
`crate_type` (plus any other metadata). This causes an error because it becomes
impossible for the compiler to distinguish between symbols (`pub` item names).
This error can be fixed by:
* Using [Cargo](../cargo/index.html), the Rust package manager, automatically
fixing this issue.
* Recompiling the crate with different metadata (different name/
`crate_type`).

View file

@ -845,7 +845,10 @@ impl EmitterWriter {
// 3 | |
// 4 | | }
// | |_^ test
if let [ann] = &line.annotations[..] {
let mut buffer_ops = vec![];
let mut annotations = vec![];
let mut short_start = true;
for ann in &line.annotations {
if let AnnotationType::MultilineStart(depth) = ann.annotation_type {
if source_string.chars().take(ann.start_col).all(|c| c.is_whitespace()) {
let style = if ann.is_primary {
@ -853,11 +856,24 @@ impl EmitterWriter {
} else {
Style::UnderlineSecondary
};
buffer.putc(line_offset, width_offset + depth - 1, '/', style);
return vec![(depth, style)];
annotations.push((depth, style));
buffer_ops.push((line_offset, width_offset + depth - 1, '/', style));
} else {
short_start = false;
break;
}
} else if let AnnotationType::MultilineLine(_) = ann.annotation_type {
} else {
short_start = false;
break;
}
}
if short_start {
for (y, x, c, s) in buffer_ops {
buffer.putc(y, x, c, s);
}
return annotations;
}
// We want to display like this:
//

View file

@ -2456,7 +2456,7 @@ impl<'a> Parser<'a> {
}
/// Parses the parameter list of a function, including the `(` and `)` delimiters.
fn parse_fn_params(&mut self, req_name: ReqName) -> PResult<'a, Vec<Param>> {
pub(super) fn parse_fn_params(&mut self, req_name: ReqName) -> PResult<'a, Vec<Param>> {
let mut first_param = true;
// Parse the arguments, starting out with `self` being allowed...
let (mut params, _) = self.parse_paren_comma_seq(|p| {

View file

@ -933,8 +933,8 @@ impl<'a> Parser<'a> {
has_parens: bool,
modifiers: BoundModifiers,
) -> PResult<'a, GenericBound> {
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
let path = if self.token.is_keyword(kw::Fn)
let mut lifetime_defs = self.parse_late_bound_lifetime_defs()?;
let mut path = if self.token.is_keyword(kw::Fn)
&& self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
&& let Some(path) = self.recover_path_from_fn()
{
@ -942,6 +942,11 @@ impl<'a> Parser<'a> {
} else {
self.parse_path(PathStyle::Type)?
};
if self.may_recover() && self.token == TokenKind::OpenDelim(Delimiter::Parenthesis) {
self.recover_fn_trait_with_lifetime_params(&mut path, &mut lifetime_defs)?;
}
if has_parens {
if self.token.is_like_plus() {
// Someone has written something like `&dyn (Trait + Other)`. The correct code
@ -1016,6 +1021,92 @@ impl<'a> Parser<'a> {
}
}
/// Recover from `Fn`-family traits (Fn, FnMut, FnOnce) with lifetime arguments
/// (e.g. `FnOnce<'a>(&'a str) -> bool`). Up to generic arguments have already
/// been eaten.
fn recover_fn_trait_with_lifetime_params(
&mut self,
fn_path: &mut ast::Path,
lifetime_defs: &mut Vec<GenericParam>,
) -> PResult<'a, ()> {
let fn_path_segment = fn_path.segments.last_mut().unwrap();
let generic_args = if let Some(p_args) = &fn_path_segment.args {
p_args.clone().into_inner()
} else {
// Normally it wouldn't come here because the upstream should have parsed
// generic parameters (otherwise it's impossible to call this function).
return Ok(());
};
let lifetimes =
if let ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { span: _, args }) =
&generic_args
{
args.into_iter()
.filter_map(|arg| {
if let ast::AngleBracketedArg::Arg(generic_arg) = arg
&& let ast::GenericArg::Lifetime(lifetime) = generic_arg {
Some(lifetime)
} else {
None
}
})
.collect()
} else {
Vec::new()
};
// Only try to recover if the trait has lifetime params.
if lifetimes.is_empty() {
return Ok(());
}
// Parse `(T, U) -> R`.
let inputs_lo = self.token.span;
let inputs: Vec<_> =
self.parse_fn_params(|_| false)?.into_iter().map(|input| input.ty).collect();
let inputs_span = inputs_lo.to(self.prev_token.span);
let output = self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?;
let args = ast::ParenthesizedArgs {
span: fn_path_segment.span().to(self.prev_token.span),
inputs,
inputs_span,
output,
}
.into();
*fn_path_segment =
ast::PathSegment { ident: fn_path_segment.ident, args, id: ast::DUMMY_NODE_ID };
// Convert parsed `<'a>` in `Fn<'a>` into `for<'a>`.
let mut generic_params = lifetimes
.iter()
.map(|lt| GenericParam {
id: lt.id,
ident: lt.ident,
attrs: ast::AttrVec::new(),
bounds: Vec::new(),
is_placeholder: false,
kind: ast::GenericParamKind::Lifetime,
colon_span: None,
})
.collect::<Vec<GenericParam>>();
lifetime_defs.append(&mut generic_params);
let generic_args_span = generic_args.span();
let mut err =
self.struct_span_err(generic_args_span, "`Fn` traits cannot take lifetime parameters");
let snippet = format!(
"for<{}> ",
lifetimes.iter().map(|lt| lt.ident.as_str()).intersperse(", ").collect::<String>(),
);
let before_fn_path = fn_path.span.shrink_to_lo();
err.multipart_suggestion(
"consider using a higher-ranked trait bound instead",
vec![(generic_args_span, "".to_owned()), (before_fn_path, snippet)],
Applicability::MaybeIncorrect,
)
.emit();
Ok(())
}
pub(super) fn check_lifetime(&mut self) -> bool {
self.expected_tokens.push(TokenType::Lifetime);
self.token.is_lifetime()

View file

@ -2692,7 +2692,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Don't print the tuple of capture types
'print: {
if !is_upvar_tys_infer_tuple {
let msg = format!("required because it appears within the type `{}`", ty);
let msg = with_forced_trimmed_paths!(format!(
"required because it appears within the type `{ty}`",
));
match ty.kind() {
ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) {
Some(ident) => err.span_note(ident.span, &msg),
@ -2733,7 +2735,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut msg =
"required because it captures the following types: ".to_owned();
for ty in bound_tys.skip_binder() {
write!(msg, "`{}`, ", ty).unwrap();
with_forced_trimmed_paths!(write!(msg, "`{}`, ", ty).unwrap());
}
err.note(msg.trim_end_matches(", "))
}
@ -2744,7 +2746,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let kind = tcx.generator_kind(def_id).unwrap().descr();
err.span_note(
sp,
&format!("required because it's used within this {}", kind),
with_forced_trimmed_paths!(&format!(
"required because it's used within this {kind}",
)),
)
}
ty::Closure(def_id, _) => err.span_note(
@ -2968,7 +2972,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let expr_ty = with_forced_trimmed_paths!(self.ty_to_string(expr_ty));
err.span_label(
expr_span,
format!("return type was inferred to be `{expr_ty}` here"),
with_forced_trimmed_paths!(format!(
"return type was inferred to be `{expr_ty}` here",
)),
);
}
}

View file

@ -506,7 +506,11 @@ impl Step for Std {
// Look for library/std, library/core etc in the `x.py doc` arguments and
// open the corresponding rendered docs.
for requested_crate in requested_crates {
if STD_PUBLIC_CRATES.iter().any(|k| *k == requested_crate.as_str()) {
if requested_crate == "library" {
// For `x.py doc library --open`, open `std` by default.
let index = out.join("std").join("index.html");
builder.open_in_browser(index);
} else if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) {
let index = out.join(requested_crate).join("index.html");
builder.open_in_browser(index);
}

View file

@ -74,11 +74,11 @@ fn update_rustfmt_version(build: &Builder<'_>) {
t!(std::fs::write(stamp_file, version))
}
/// Returns the files modified between the `merge-base` of HEAD and
/// Returns the Rust files modified between the `merge-base` of HEAD and
/// rust-lang/master and what is now on the disk.
///
/// Returns `None` if all files should be formatted.
fn get_modified_files(build: &Builder<'_>) -> Option<Vec<String>> {
fn get_modified_rs_files(build: &Builder<'_>) -> Option<Vec<String>> {
let Ok(remote) = get_rust_lang_rust_remote() else {return None;};
if !verify_rustfmt_version(build) {
return None;
@ -95,6 +95,7 @@ fn get_modified_files(build: &Builder<'_>) -> Option<Vec<String>> {
)
.lines()
.map(|s| s.trim().to_owned())
.filter(|f| Path::new(f).extension().map_or(false, |ext| ext == "rs"))
.collect(),
)
}
@ -195,7 +196,7 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) {
ignore_fmt.add(&format!("!/{}", untracked_path)).expect(&untracked_path);
}
if !check && paths.is_empty() {
if let Some(files) = get_modified_files(build) {
if let Some(files) = get_modified_rs_files(build) {
for file in files {
println!("formatting modified file {file}");
ignore_fmt.add(&format!("/{file}")).expect(&file);

View file

@ -1740,7 +1740,7 @@ impl Type {
fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> {
let t: PrimitiveType = match *self {
Type::Path { ref path } => return Some(path.def_id()),
DynTrait(ref bounds, _) => return Some(bounds[0].trait_.def_id()),
DynTrait(ref bounds, _) => return bounds.get(0).map(|b| b.trait_.def_id()),
Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()),
BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference,
BorrowedRef { ref type_, .. } => return type_.inner_def_id(cache),

View file

@ -322,8 +322,7 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
match *clean_type {
clean::Type::Path { ref path, .. } => Some(RenderTypeId::DefId(path.def_id())),
clean::DynTrait(ref bounds, _) => {
let path = &bounds[0].trait_;
Some(RenderTypeId::DefId(path.def_id()))
bounds.get(0).map(|b| RenderTypeId::DefId(b.trait_.def_id()))
}
clean::Primitive(p) => Some(RenderTypeId::Primitive(p)),
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {

View file

@ -1317,15 +1317,11 @@ a.test-arrow:hover {
-webkit-appearance: none;
opacity: 1;
}
#settings-menu, #help-button {
margin-left: 4px;
display: flex;
}
#settings-menu > a, #help-button > a, #copy-path {
width: 33px;
}
#settings-menu > a, #help-button > a {
display: flex;
align-items: center;
@ -1337,6 +1333,7 @@ a.test-arrow:hover {
/* Rare exception to specifying font sizes in rem. Since this is acting
as an icon, it's okay to specify their sizes in pixels. */
font-size: 20px;
width: 33px;
}
#settings-menu > a:hover, #settings-menu > a:focus,
@ -1352,6 +1349,7 @@ a.test-arrow:hover {
padding: 0;
padding-left: 2px;
border: 0;
width: 33px;
}
#copy-path > img {
filter: var(--copy-path-img-filter);

View file

@ -0,0 +1,7 @@
// compile-flags: --document-private-items
// edition:2021
fn use_avx() -> dyn {
//~^ ERROR at least one trait is required for an object type
!( ident_error )
}

View file

@ -0,0 +1,9 @@
error[E0224]: at least one trait is required for an object type
--> $DIR/issue-106213.rs:4:17
|
LL | fn use_avx() -> dyn {
| ^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0224`.

View file

@ -3,10 +3,7 @@ error[E0308]: mismatched types
|
LL | #[alloc_error_handler]
| ---------------------- in this procedural macro expansion
LL | fn oom(
| __^
| | _|
| ||
LL | // fn oom(
LL | || info: &Layout,
LL | || ) -> ()
| ||_______- arguments to this function are incorrect
@ -29,10 +26,7 @@ error[E0308]: mismatched types
|
LL | #[alloc_error_handler]
| ---------------------- in this procedural macro expansion
LL | fn oom(
| __^
| | _|
| ||
LL | // fn oom(
LL | || info: &Layout,
LL | || ) -> ()
| ||_______^ expected `!`, found `()`

View file

@ -3,10 +3,7 @@ error[E0308]: mismatched types
|
LL | #[alloc_error_handler]
| ---------------------- in this procedural macro expansion
LL | fn oom(
| __^
| | _|
| ||
LL | // fn oom(
LL | || info: Layout,
LL | || ) {
| ||_- arguments to this function are incorrect
@ -36,10 +33,7 @@ error[E0308]: mismatched types
|
LL | #[alloc_error_handler]
| ---------------------- in this procedural macro expansion
LL | fn oom(
| __^
| | _|
| ||
LL | // fn oom(
LL | || info: Layout,
LL | || ) {
| ||_^ expected `!`, found `()`

View file

@ -30,12 +30,7 @@ error: asm outputs are not allowed with the `noreturn` option
LL | asm!("", $in(x) x, $out(x) x, $lateout(x) x, $inout(x) x, $inlateout(x) x,
| ^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^
...
LL | m!(in out lateout inout inlateout const sym
| _____-
| |_____|
| |_____|
| |_____|
| |
LL | / m!(in out lateout inout inlateout const sym
LL | | pure nomem readonly preserves_flags
LL | | noreturn nostack options);
| | -

View file

@ -30,12 +30,7 @@ error: asm outputs are not allowed with the `noreturn` option
LL | asm!("", $in(x) x, $out(x) x, $lateout(x) x, $inout(x) x, $inlateout(x) x,
| ^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^
...
LL | m!(in out lateout inout inlateout const sym
| _____-
| |_____|
| |_____|
| |_____|
| |
LL | / m!(in out lateout inout inlateout const sym
LL | | pure nomem readonly preserves_flags
LL | | noreturn nostack att_syntax options);
| | -

View file

@ -0,0 +1,8 @@
// no need to create a new aux file, we can use an existing.
// aux-build: crateresolve1-1.rs
// set same metadata as `crateresolve1`
#![crate_name = "crateresolve1"]
#![crate_type = "lib"]
extern crate crateresolve1; //~ ERROR E0519

View file

@ -0,0 +1,9 @@
error[E0519]: the current crate is indistinguishable from one of its dependencies: it has the same crate-name `crateresolve1` and was compiled with the same `-C metadata` arguments. This will result in symbol conflicts between the two.
--> $DIR/E0519.rs:8:1
|
LL | extern crate crateresolve1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0519`.

View file

@ -23,7 +23,7 @@ LL | foo::<for<'a> fn(&'a ()) -> (dyn std::fmt::Display + 'a), _>(None, (&()
| required by a bound introduced by this call
|
= help: within `for<'a> fn(&'a ()) -> (dyn std::fmt::Display + 'a)`, the trait `for<'a> Sized` is not implemented for `(dyn std::fmt::Display + 'a)`
= note: required because it appears within the type `for<'a> fn(&'a ()) -> (dyn std::fmt::Display + 'a)`
= note: required because it appears within the type `for<'a> fn(&'a ()) -> (dyn Display + 'a)`
note: required by a bound in `foo`
--> $DIR/unsized-ret.rs:5:11
|

View file

@ -35,7 +35,7 @@ note: required because it's used within this generator
|
LL | || {
| ^^
note: required because it appears within the type `Opaque(DefId(0:35 ~ generator_print_verbose_1[749a]::make_gen2::{opaque#0}), [std::sync::Arc<std::cell::RefCell<i32>>])`
note: required because it appears within the type `Opaque(DefId(0:35 ~ generator_print_verbose_1[749a]::make_gen2::{opaque#0}), [Arc<RefCell<i32>>])`
--> $DIR/generator-print-verbose-1.rs:41:30
|
LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {

View file

@ -0,0 +1,20 @@
// Test that Fn-family traits with lifetime parameters shouldn't compile and
// we suggest the usage of higher-rank trait bounds instead.
fn fa(_: impl Fn<'a>(&'a str) -> bool) {}
//~^ ERROR `Fn` traits cannot take lifetime parameters
fn fb(_: impl FnMut<'a, 'b>(&'a str, &'b str) -> bool) {}
//~^ ERROR `Fn` traits cannot take lifetime parameters
fn fc(_: impl std::fmt::Display + FnOnce<'a>(&'a str) -> bool + std::fmt::Debug) {}
//~^ ERROR `Fn` traits cannot take lifetime parameters
use std::ops::Fn as AliasedFn;
fn fd(_: impl AliasedFn<'a>(&'a str) -> bool) {}
//~^ ERROR `Fn` traits cannot take lifetime parameters
fn fe<F>(_: F) where F: Fn<'a>(&'a str) -> bool {}
//~^ ERROR `Fn` traits cannot take lifetime parameters
fn main() {}

View file

@ -0,0 +1,62 @@
error: `Fn` traits cannot take lifetime parameters
--> $DIR/hrtb-malformed-lifetime-generics.rs:4:17
|
LL | fn fa(_: impl Fn<'a>(&'a str) -> bool) {}
| ^^^^
|
help: consider using a higher-ranked trait bound instead
|
LL - fn fa(_: impl Fn<'a>(&'a str) -> bool) {}
LL + fn fa(_: impl for<'a> Fn(&'a str) -> bool) {}
|
error: `Fn` traits cannot take lifetime parameters
--> $DIR/hrtb-malformed-lifetime-generics.rs:7:20
|
LL | fn fb(_: impl FnMut<'a, 'b>(&'a str, &'b str) -> bool) {}
| ^^^^^^^^
|
help: consider using a higher-ranked trait bound instead
|
LL - fn fb(_: impl FnMut<'a, 'b>(&'a str, &'b str) -> bool) {}
LL + fn fb(_: impl for<'a, 'b> FnMut(&'a str, &'b str) -> bool) {}
|
error: `Fn` traits cannot take lifetime parameters
--> $DIR/hrtb-malformed-lifetime-generics.rs:10:41
|
LL | fn fc(_: impl std::fmt::Display + FnOnce<'a>(&'a str) -> bool + std::fmt::Debug) {}
| ^^^^
|
help: consider using a higher-ranked trait bound instead
|
LL - fn fc(_: impl std::fmt::Display + FnOnce<'a>(&'a str) -> bool + std::fmt::Debug) {}
LL + fn fc(_: impl std::fmt::Display + for<'a> FnOnce(&'a str) -> bool + std::fmt::Debug) {}
|
error: `Fn` traits cannot take lifetime parameters
--> $DIR/hrtb-malformed-lifetime-generics.rs:14:24
|
LL | fn fd(_: impl AliasedFn<'a>(&'a str) -> bool) {}
| ^^^^
|
help: consider using a higher-ranked trait bound instead
|
LL - fn fd(_: impl AliasedFn<'a>(&'a str) -> bool) {}
LL + fn fd(_: impl for<'a> AliasedFn(&'a str) -> bool) {}
|
error: `Fn` traits cannot take lifetime parameters
--> $DIR/hrtb-malformed-lifetime-generics.rs:17:27
|
LL | fn fe<F>(_: F) where F: Fn<'a>(&'a str) -> bool {}
| ^^^^
|
help: consider using a higher-ranked trait bound instead
|
LL - fn fe<F>(_: F) where F: Fn<'a>(&'a str) -> bool {}
LL + fn fe<F>(_: F) where F: for<'a> Fn(&'a str) -> bool {}
|
error: aborting due to 5 previous errors

View file

@ -1,10 +1,7 @@
error[E0515]: cannot return value referencing local variable `rawLines`
--> $DIR/issue-13497-2.rs:3:5
|
LL | rawLines
| ______^
| | _____|
| ||
LL | // rawLines
LL | || .iter().map(|l| l.trim()).collect()
| ||_______________-___________________________^ returns a value referencing data owned by the current function
| |_______________|

View file

@ -7,7 +7,7 @@ LL | foo::<HashMap<Rc<()>, Rc<()>>>();
= help: within `(Rc<()>, Rc<()>)`, the trait `Send` is not implemented for `Rc<()>`
= note: required because it appears within the type `(Rc<()>, Rc<()>)`
= note: required for `hashbrown::raw::RawTable<(Rc<()>, Rc<()>)>` to implement `Send`
= note: required because it appears within the type `hashbrown::map::HashMap<Rc<()>, Rc<()>, RandomState>`
= note: required because it appears within the type `HashMap<Rc<()>, Rc<()>, RandomState>`
= note: required because it appears within the type `HashMap<Rc<()>, Rc<()>>`
note: required by a bound in `foo`
--> $DIR/issue-21763.rs:6:11

View file

@ -4,10 +4,7 @@ error[E0618]: expected function, found enum variant `Alias::Unit`
LL | Unit,
| ---- enum variant `Alias::Unit` defined here
...
LL | Alias::
| ______^
| | _____|
| ||
LL | // Alias::
LL | || Unit();
| ||________^_- call expression requires function
| |________|

View file

@ -50,7 +50,7 @@ LL | is_send((8, TestType));
| required by a bound introduced by this call
|
= help: within `({integer}, dummy1c::TestType)`, the trait `Send` is not implemented for `dummy1c::TestType`
= note: required because it appears within the type `({integer}, dummy1c::TestType)`
= note: required because it appears within the type `({integer}, TestType)`
note: required by a bound in `is_send`
--> $DIR/negated-auto-traits-error.rs:16:15
|
@ -67,7 +67,7 @@ LL | is_send(Box::new(TestType));
|
= note: the trait bound `Unique<dummy2::TestType>: Send` is not satisfied
= note: required for `Unique<dummy2::TestType>` to implement `Send`
= note: required because it appears within the type `Box<dummy2::TestType>`
= note: required because it appears within the type `Box<TestType>`
note: required by a bound in `is_send`
--> $DIR/negated-auto-traits-error.rs:16:15
|
@ -87,13 +87,13 @@ LL | is_send(Box::new(Outer2(TestType)));
| required by a bound introduced by this call
|
= help: within `Outer2<dummy3::TestType>`, the trait `Send` is not implemented for `dummy3::TestType`
note: required because it appears within the type `Outer2<dummy3::TestType>`
note: required because it appears within the type `Outer2<TestType>`
--> $DIR/negated-auto-traits-error.rs:12:8
|
LL | struct Outer2<T>(T);
| ^^^^^^
= note: required for `Unique<Outer2<dummy3::TestType>>` to implement `Send`
= note: required because it appears within the type `Box<Outer2<dummy3::TestType>>`
= note: required because it appears within the type `Box<Outer2<TestType>>`
note: required by a bound in `is_send`
--> $DIR/negated-auto-traits-error.rs:16:15
|

View file

@ -4203,6 +4203,7 @@ Released 2018-09-13
[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
[`fn_null_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_null_check
[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
[`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any
@ -4460,6 +4461,7 @@ Released 2018-09-13
[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
[`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
@ -4545,6 +4547,7 @@ Released 2018-09-13
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
@ -4590,6 +4593,7 @@ Released 2018-09-13
[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
[`transmute_null_to_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_null_to_fn
[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref

View file

@ -54,7 +54,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv
diag.span_suggestion(
expr.span,
&format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
sugg,
rustc_errors::Applicability::HasPlaceholders,
);

View file

@ -161,6 +161,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::float_literal::LOSSY_FLOAT_LITERAL_INFO,
crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO,
crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO,
crate::fn_null_check::FN_NULL_CHECK_INFO,
crate::format::USELESS_FORMAT_INFO,
crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO,
crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO,
@ -494,6 +495,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO,
crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO,
crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO,
crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO,
crate::precedence::PRECEDENCE_INFO,
crate::ptr::CMP_NULL_INFO,
crate::ptr::INVALID_NULL_PTR_USAGE_INFO,
@ -535,6 +537,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
crate::size_of_ref::SIZE_OF_REF_INFO,
crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO,
crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO,
crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO,
@ -568,6 +571,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO,
crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO,
crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO,
crate::transmute::TRANSMUTE_NULL_TO_FN_INFO,
crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO,
crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO,
crate::transmute::TRANSMUTE_PTR_TO_REF_INFO,

View file

@ -1282,10 +1282,10 @@ fn referent_used_exactly_once<'tcx>(
possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
}
let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
// itself. See the comment in that method for an explanation as to why.
possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
// If `place.local` were not included here, the `copyable_iterator::warn` test would fail. The
// reason is that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible
// borrower of itself. See the comment in that method for an explanation as to why.
possible_borrower.at_most_borrowers(cx, &[local, place.local], place.local, location)
&& used_exactly_once(mir, place.local).unwrap_or(false)
} else {
false

View file

@ -324,7 +324,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
let maybe_neg_sugg = |expr, hir_id| {
let sugg = Sugg::hir(cx, expr, "..");
if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
format!("-{sugg}")
format!("-{}", sugg.maybe_par())
} else {
sugg.to_string()
}

View file

@ -0,0 +1,106 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
/// Checks for comparing a function pointer to null.
///
/// ### Why is this bad?
/// Function pointers are assumed to not be null.
///
/// ### Example
/// ```rust,ignore
/// let fn_ptr: fn() = /* somehow obtained nullable function pointer */
///
/// if (fn_ptr as *const ()).is_null() { ... }
/// ```
/// Use instead:
/// ```rust,ignore
/// let fn_ptr: Option<fn()> = /* somehow obtained nullable function pointer */
///
/// if fn_ptr.is_none() { ... }
/// ```
#[clippy::version = "1.67.0"]
pub FN_NULL_CHECK,
correctness,
"`fn()` type assumed to be nullable"
}
declare_lint_pass!(FnNullCheck => [FN_NULL_CHECK]);
fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) {
span_lint_and_help(
cx,
FN_NULL_CHECK,
expr.span,
"function pointer assumed to be nullable, even though it isn't",
None,
"try wrapping your function pointer type in `Option<T>` instead, and using `is_none` to check for null pointer value",
);
}
fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let ExprKind::Cast(cast_expr, cast_ty) = expr.kind
&& let TyKind::Ptr(_) = cast_ty.kind
{
cx.typeck_results().expr_ty_adjusted(cast_expr).is_fn()
} else {
false
}
}
impl<'tcx> LateLintPass<'tcx> for FnNullCheck {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
match expr.kind {
// Catching:
// (fn_ptr as *<const/mut> <ty>).is_null()
ExprKind::MethodCall(method_name, receiver, _, _)
if method_name.ident.as_str() == "is_null" && is_fn_ptr_cast(cx, receiver) =>
{
lint_expr(cx, expr);
},
ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => {
let to_check: &Expr<'_>;
if is_fn_ptr_cast(cx, left) {
to_check = right;
} else if is_fn_ptr_cast(cx, right) {
to_check = left;
} else {
return;
}
match to_check.kind {
// Catching:
// (fn_ptr as *<const/mut> <ty>) == (0 as <ty>)
ExprKind::Cast(cast_expr, _) if is_integer_literal(cast_expr, 0) => {
lint_expr(cx, expr);
},
// Catching:
// (fn_ptr as *<const/mut> <ty>) == std::ptr::null()
ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::ptr_null) => {
lint_expr(cx, expr);
},
// Catching:
// (fn_ptr as *<const/mut> <ty>) == <const that evaluates to null_ptr>
_ if matches!(
constant(cx, cx.typeck_results(), to_check),
Some((Constant::RawPtr(0), _))
) =>
{
lint_expr(cx, expr);
},
_ => {},
}
},
_ => {},
}
}
}

View file

@ -382,7 +382,7 @@ fn check_format_in_format_args(
call_site,
&format!("`format!` in `{name}!` args"),
|diag| {
diag.help(&format!(
diag.help(format!(
"combine the `format!(..)` arguments with the outer `{name}!(..)` call"
));
diag.help("or consider changing `format!` to `format_args!`");

View file

@ -63,23 +63,40 @@ declare_clippy_lint! {
/// arguments but are not marked `unsafe`.
///
/// ### Why is this bad?
/// The function should probably be marked `unsafe`, since
/// for an arbitrary raw pointer, there is no way of telling for sure if it is
/// valid.
/// The function should almost definitely be marked `unsafe`, since for an
/// arbitrary raw pointer, there is no way of telling for sure if it is valid.
///
/// In general, this lint should **never be disabled** unless it is definitely a
/// false positive (please submit an issue if so) since it breaks Rust's
/// soundness guarantees, directly exposing API users to potentially dangerous
/// program behavior. This is also true for internal APIs, as it is easy to leak
/// unsoundness.
///
/// ### Context
/// In Rust, an `unsafe {...}` block is used to indicate that the code in that
/// section has been verified in some way that the compiler can not. For a
/// function that accepts a raw pointer then accesses the pointer's data, this is
/// generally impossible as the incoming pointer could point anywhere, valid or
/// not. So, the signature should be marked `unsafe fn`: this indicates that the
/// function's caller must provide some verification that the arguments it sends
/// are valid (and then call the function within an `unsafe` block).
///
/// ### Known problems
/// * It does not check functions recursively so if the pointer is passed to a
/// private non-`unsafe` function which does the dereferencing, the lint won't
/// trigger.
/// trigger (false negative).
/// * It only checks for arguments whose type are raw pointers, not raw pointers
/// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
/// `some_argument.get_raw_ptr()`).
/// `some_argument.get_raw_ptr()`) (false negative).
///
/// ### Example
/// ```rust,ignore
/// pub fn foo(x: *const u8) {
/// println!("{}", unsafe { *x });
/// }
///
/// // this call "looks" safe but will segfault or worse!
/// // foo(invalid_ptr);
/// ```
///
/// Use instead:
@ -87,6 +104,12 @@ declare_clippy_lint! {
/// pub unsafe fn foo(x: *const u8) {
/// println!("{}", unsafe { *x });
/// }
///
/// // this would cause a compiler error for calling without `unsafe`
/// // foo(invalid_ptr);
///
/// // sound call if the caller knows the pointer is valid
/// unsafe { foo(valid_ptr); }
/// ```
#[clippy::version = "pre 1.29.0"]
pub NOT_UNSAFE_PTR_ARG_DEREF,

View file

@ -34,12 +34,12 @@ declare_clippy_lint! {
}
pub struct LargeConstArrays {
maximum_allowed_size: u64,
maximum_allowed_size: u128,
}
impl LargeConstArrays {
#[must_use]
pub fn new(maximum_allowed_size: u64) -> Self {
pub fn new(maximum_allowed_size: u128) -> Self {
Self { maximum_allowed_size }
}
}
@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();
if let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx);
if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes());
if self.maximum_allowed_size < element_count * element_size;
if self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size);
then {
let hi_pos = item.ident.span.lo() - BytePos::from_usize(1);

View file

@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
);
diag.span_label(
def.variants[variants_size[1].ind].span,
&if variants_size[1].fields_size.is_empty() {
if variants_size[1].fields_size.is_empty() {
"the second-largest variant carries no data at all".to_owned()
} else {
format!(

View file

@ -24,12 +24,12 @@ declare_clippy_lint! {
}
pub struct LargeStackArrays {
maximum_allowed_size: u64,
maximum_allowed_size: u128,
}
impl LargeStackArrays {
#[must_use]
pub fn new(maximum_allowed_size: u64) -> Self {
pub fn new(maximum_allowed_size: u128) -> Self {
Self { maximum_allowed_size }
}
}
@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
&& let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
&& !cx.tcx.hir().parent_iter(expr.hir_id)
.any(|(_, node)| matches!(node, Node::Item(Item { kind: ItemKind::Static(..), .. })))
&& self.maximum_allowed_size < element_count * element_size {
&& self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) {
span_lint_and_help(
cx,
LARGE_STACK_ARRAYS,

View file

@ -361,7 +361,7 @@ fn check_for_is_empty<'tcx>(
db.span_note(span, "`is_empty` defined here");
}
if let Some(self_kind) = self_kind {
db.note(&output.expected_sig(self_kind));
db.note(output.expected_sig(self_kind));
}
});
}

View file

@ -125,6 +125,7 @@ mod explicit_write;
mod fallible_impl_from;
mod float_literal;
mod floating_point_arithmetic;
mod fn_null_check;
mod format;
mod format_args;
mod format_impl;
@ -234,6 +235,7 @@ mod partialeq_ne_impl;
mod partialeq_to_none;
mod pass_by_ref_or_value;
mod pattern_type_mismatch;
mod permissions_set_readonly_false;
mod precedence;
mod ptr;
mod ptr_offset_with_cast;
@ -263,6 +265,7 @@ mod shadow;
mod single_char_lifetime_names;
mod single_component_path_imports;
mod size_of_in_element_count;
mod size_of_ref;
mod slow_vector_initialization;
mod std_instead_of_core;
mod strings;
@ -334,7 +337,7 @@ pub fn read_conf(sess: &Session, path: &io::Result<Option<PathBuf>>) -> Conf {
Ok(Some(path)) => path,
Ok(None) => return Conf::default(),
Err(error) => {
sess.struct_err(&format!("error finding Clippy's configuration file: {error}"))
sess.struct_err(format!("error finding Clippy's configuration file: {error}"))
.emit();
return Conf::default();
},
@ -902,6 +905,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock));
store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck));
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -77,7 +77,7 @@ pub(super) fn check<'tcx>(
applicability,
);
diag.note(&format!(
diag.note(format!(
"`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`"
));
},

View file

@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
let help = format!("make the function `async` and {ret_sugg}");
diag.span_suggestion(
header_span,
&help,
help,
format!("async {}{ret_snip}", &header_snip[..ret_pos]),
Applicability::MachineApplicable
);

View file

@ -35,6 +35,9 @@ declare_clippy_lint! {
/// Some may consider panicking in these situations to be desirable, but it also may
/// introduce panicking where there wasn't any before.
///
/// See also [the discussion in the
/// PR](https://github.com/rust-lang/rust-clippy/pull/9484#issuecomment-1278922613).
///
/// ### Examples
/// ```rust
/// # let (input, min, max) = (0, -2, 1);
@ -78,7 +81,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "1.66.0"]
pub MANUAL_CLAMP,
complexity,
nursery,
"using a clamp pattern instead of the clamp function"
}
impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]);

View file

@ -109,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
let test_span = expr.span.until(then.span);
span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| {
diag.span_note(test_span, &format!("the {kind_word} was tested here"));
diag.span_note(test_span, format!("the {kind_word} was tested here"));
multispan_sugg(
diag,
&format!("try using the `strip_{kind_word}` method"),

View file

@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::contains_unsafe_block;
use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id};
use rustc_hir::LangItem::OptionSome;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind};
use rustc_lint::LateContext;
use rustc_span::{sym, SyntaxContext};
@ -25,15 +25,13 @@ fn get_cond_expr<'tcx>(
if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr);
if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind;
if let PatKind::Binding(_,target, ..) = pat.kind;
if let (then_visitor, else_visitor)
= (is_some_expr(cx, target, ctxt, then_expr),
is_some_expr(cx, target, ctxt, else_expr));
if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None`
if is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr)
|| is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr); // check that one expr resolves to `Some(x)`, the other to `None`
then {
return Some(SomeExpr {
expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()),
needs_unsafe_block: contains_unsafe_block(cx, expr),
needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond
needs_negated: is_none_expr(cx, then_expr) // if the `then_expr` resolves to `None`, need to negate the cond
})
}
};
@ -74,6 +72,13 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr:
false
}
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) {
return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone);
};
false
}
// given the closure: `|<pattern>| <expr>`
// returns `|&<pattern>| <expr>`
fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String {

View file

@ -31,19 +31,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
};
// Do we need to add ';' to suggestion ?
match match_body.kind {
ExprKind::Block(block, _) => {
// macro + expr_ty(body) == ()
if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
snippet_body.push(';');
}
},
_ => {
// expr_ty(body) == ()
if cx.typeck_results().expr_ty(match_body).is_unit() {
snippet_body.push(';');
}
},
if let ExprKind::Block(block, _) = match_body.kind {
// macro + expr_ty(body) == ()
if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
snippet_body.push(';');
}
}
let mut applicability = Applicability::MaybeIncorrect;

View file

@ -30,7 +30,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
let mut has_non_wild = false;
for arm in arms {
match peel_hir_pat_refs(arm.pat).0.kind {
PatKind::Wild => wildcard_span = Some(arm.pat.span),
PatKind::Wild if arm.guard.is_none() => wildcard_span = Some(arm.pat.span),
PatKind::Binding(_, _, ident, None) => {
wildcard_span = Some(arm.pat.span);
wildcard_ident = Some(ident);

View file

@ -36,7 +36,7 @@ pub fn check(
expr.span,
&format!("calling `to_string` on `{arg_ty}`"),
|diag| {
diag.help(&format!(
diag.help(format!(
"`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`"
));
let mut applicability = Applicability::MachineApplicable;

View file

@ -29,7 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
application = Applicability::Unspecified;
diag.span_help(
pat.span,
&format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")),
format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")),
);
}
}

View file

@ -84,7 +84,7 @@ pub(super) fn check<'tcx>(
suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, ")));
}
diag.multipart_suggestion(&format!("use `{suggest}` instead"), suggestion, applicability);
diag.multipart_suggestion(format!("use `{suggest}` instead"), suggestion, applicability);
});
}
}

View file

@ -167,7 +167,7 @@ fn check_manual_split_once_indirect(
};
diag.span_suggestion_verbose(
local.span,
&format!("try `{r}split_once`"),
format!("try `{r}split_once`"),
format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"),
app,
);

View file

@ -62,7 +62,7 @@ pub(super) fn check<'tcx>(
span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
diag.span_suggestion(
span,
&format!("use `{simplify_using}(..)` instead"),
format!("use `{simplify_using}(..)` instead"),
format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")),
applicability,
);

View file

@ -284,7 +284,7 @@ fn check<'tcx>(
diag.span_suggestion(
assign.lhs_span,
&format!("declare `{binding_name}` here"),
format!("declare `{binding_name}` here"),
let_snippet,
Applicability::MachineApplicable,
);
@ -304,7 +304,7 @@ fn check<'tcx>(
diag.span_suggestion_verbose(
usage.stmt.span.shrink_to_lo(),
&format!("declare `{binding_name}` here"),
format!("declare `{binding_name}` here"),
format!("{let_snippet} = "),
applicability,
);
@ -335,7 +335,7 @@ fn check<'tcx>(
diag.span_suggestion_verbose(
usage.stmt.span.shrink_to_lo(),
&format!("declare `{binding_name}` here"),
format!("declare `{binding_name}` here"),
format!("{let_snippet} = "),
applicability,
);

View file

@ -125,7 +125,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
if is_string { "string" } else { "byte string" }
),
|diag| {
diag.help(&format!(
diag.help(format!(
"octal escapes are not supported, `\\0` is always a null {}",
if is_string { "character" } else { "byte" }
));
@ -139,7 +139,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
// suggestion 2: unambiguous null byte
diag.span_suggestion(
span,
&format!(
format!(
"if the null {} is intended, disambiguate using",
if is_string { "character" } else { "byte" }
),

View file

@ -50,7 +50,7 @@ fn lint_misrefactored_assign_op(
let long = format!("{snip_a} = {}", sugg::make_binop(op.into(), a, r));
diag.span_suggestion(
expr.span,
&format!(
format!(
"did you mean `{snip_a} = {snip_a} {} {snip_r}` or `{long}`? Consider replacing it with",
op.as_str()
),

View file

@ -0,0 +1,52 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::paths;
use clippy_utils::ty::match_type;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`.
///
/// ### Why is this bad?
/// On Unix platforms this results in the file being world writable,
/// equivalent to `chmod a+w <file>`.
/// ### Example
/// ```rust
/// use std::fs::File;
/// let f = File::create("foo.txt").unwrap();
/// let metadata = f.metadata().unwrap();
/// let mut permissions = metadata.permissions();
/// permissions.set_readonly(false);
/// ```
#[clippy::version = "1.66.0"]
pub PERMISSIONS_SET_READONLY_FALSE,
suspicious,
"Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`"
}
declare_lint_pass!(PermissionsSetReadonlyFalse => [PERMISSIONS_SET_READONLY_FALSE]);
impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind
&& match_type(cx, cx.typeck_results().expr_ty(receiver), &paths::PERMISSIONS)
&& path.ident.name == sym!(set_readonly)
&& let ExprKind::Lit(lit) = &arg.kind
&& LitKind::Bool(false) == lit.node
{
span_lint_and_then(
cx,
PERMISSIONS_SET_READONLY_FALSE,
expr.span,
"call to `set_readonly` with argument `false`",
|diag| {
diag.note("on Unix platforms this results in the file being world writable");
diag.help("you can set the desired permissions using `PermissionsExt`. For more information, see\n\
https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html");
}
);
}
}
}

View file

@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
// `res = clone(arg)` can be turned into `res = move arg;`
// if `arg` is the only borrow of `cloned` at this point.
if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) {
if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg], cloned, loc) {
continue;
}
@ -178,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
// StorageDead(pred_arg);
// res = to_path_buf(cloned);
// ```
if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) {
if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg, cloned], local, loc) {
continue;
}

View file

@ -6,7 +6,7 @@ use core::ops::ControlFlow;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, LangItem, MatchSource, PatKind, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
@ -207,6 +207,12 @@ fn check_final_expr<'tcx>(
match &peeled_drop_expr.kind {
// simple return is always "bad"
ExprKind::Ret(ref inner) => {
// if desugar of `do yeet`, don't lint
if let Some(inner_expr) = inner
&& let ExprKind::Call(path_expr, _) = inner_expr.kind
&& let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind {
return;
}
if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
if !borrows {

View file

@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
|diag| {
diag.span_note(
trait_method_span,
&format!("existing `{method_name}` defined here"),
format!("existing `{method_name}` defined here"),
);
},
);
@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
// iterate on trait_spans?
diag.span_note(
trait_spans[0],
&format!("existing `{method_name}` defined here"),
format!("existing `{method_name}` defined here"),
);
},
);

View file

@ -0,0 +1,73 @@
use clippy_utils::{diagnostics::span_lint_and_help, path_def_id, ty::peel_mid_ty_refs};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
///
/// Checks for calls to `std::mem::size_of_val()` where the argument is
/// a reference to a reference.
///
/// ### Why is this bad?
///
/// Calling `size_of_val()` with a reference to a reference as the argument
/// yields the size of the reference-type, not the size of the value behind
/// the reference.
///
/// ### Example
/// ```rust
/// struct Foo {
/// buffer: [u8],
/// }
///
/// impl Foo {
/// fn size(&self) -> usize {
/// // Note that `&self` as an argument is a `&&Foo`: Because `self`
/// // is already a reference, `&self` is a double-reference.
/// // The return value of `size_of_val()` therefor is the
/// // size of the reference-type, not the size of `self`.
/// std::mem::size_of_val(&self)
/// }
/// }
/// ```
/// Use instead:
/// ```rust
/// struct Foo {
/// buffer: [u8],
/// }
///
/// impl Foo {
/// fn size(&self) -> usize {
/// // Correct
/// std::mem::size_of_val(self)
/// }
/// }
/// ```
#[clippy::version = "1.67.0"]
pub SIZE_OF_REF,
suspicious,
"Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended"
}
declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]);
impl LateLintPass<'_> for SizeOfRef {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
if let ExprKind::Call(path, [arg]) = expr.kind
&& let Some(def_id) = path_def_id(cx, path)
&& cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id)
&& let arg_ty = cx.typeck_results().expr_ty(arg)
&& peel_mid_ty_refs(arg_ty).1 > 1
{
span_lint_and_help(
cx,
SIZE_OF_REF,
expr.span,
"argument to `std::mem::size_of_val()` is a reference to a reference",
None,
"dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type",
);
}
}
}

View file

@ -132,7 +132,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
applicability,
);
if !is_xor_based {
diag.note(&format!("or maybe you should use `{sugg}::mem::replace`?"));
diag.note(format!("or maybe you should use `{sugg}::mem::replace`?"));
}
},
);
@ -214,7 +214,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
Applicability::MaybeIncorrect,
);
diag.note(
&format!("or maybe you should use `{sugg}::mem::replace`?")
format!("or maybe you should use `{sugg}::mem::replace`?")
);
}
});

View file

@ -3,6 +3,7 @@ mod transmute_float_to_int;
mod transmute_int_to_bool;
mod transmute_int_to_char;
mod transmute_int_to_float;
mod transmute_null_to_fn;
mod transmute_num_to_bytes;
mod transmute_ptr_to_ptr;
mod transmute_ptr_to_ref;
@ -409,6 +410,34 @@ declare_clippy_lint! {
"transmutes from a null pointer to a reference, which is undefined behavior"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for null function pointer creation through transmute.
///
/// ### Why is this bad?
/// Creating a null function pointer is undefined behavior.
///
/// More info: https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization
///
/// ### Known problems
/// Not all cases can be detected at the moment of this writing.
/// For example, variables which hold a null pointer and are then fed to a `transmute`
/// call, aren't detectable yet.
///
/// ### Example
/// ```rust
/// let null_fn: fn() = unsafe { std::mem::transmute( std::ptr::null::<()>() ) };
/// ```
/// Use instead:
/// ```rust
/// let null_fn: Option<fn()> = None;
/// ```
#[clippy::version = "1.67.0"]
pub TRANSMUTE_NULL_TO_FN,
correctness,
"transmute results in a null function pointer, which is undefined behavior"
}
pub struct Transmute {
msrv: Msrv,
}
@ -428,6 +457,7 @@ impl_lint_pass!(Transmute => [
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
TRANSMUTE_UNDEFINED_REPR,
TRANSMUTING_NULL,
TRANSMUTE_NULL_TO_FN,
]);
impl Transmute {
#[must_use]
@ -461,6 +491,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
| transmuting_null::check(cx, e, arg, to_ty)
| transmute_null_to_fn::check(cx, e, arg, to_ty)
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv)
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)

View file

@ -0,0 +1,64 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
use rustc_span::symbol::sym;
use super::TRANSMUTE_NULL_TO_FN;
fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) {
span_lint_and_then(
cx,
TRANSMUTE_NULL_TO_FN,
expr.span,
"transmuting a known null pointer into a function pointer",
|diag| {
diag.span_label(expr.span, "this transmute results in undefined behavior");
diag.help(
"try wrapping your function pointer type in `Option<T>` instead, and using `None` as a null pointer value"
);
},
);
}
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool {
if !to_ty.is_fn() {
return false;
}
match arg.kind {
// Catching:
// transmute over constants that resolve to `null`.
ExprKind::Path(ref _qpath)
if matches!(constant(cx, cx.typeck_results(), arg), Some((Constant::RawPtr(0), _))) =>
{
lint_expr(cx, expr);
true
},
// Catching:
// `std::mem::transmute(0 as *const i32)`
ExprKind::Cast(inner_expr, _cast_ty) if is_integer_literal(inner_expr, 0) => {
lint_expr(cx, expr);
true
},
// Catching:
// `std::mem::transmute(std::ptr::null::<i32>())`
ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => {
lint_expr(cx, expr);
true
},
_ => {
// FIXME:
// Also catch transmutations of variables which are known nulls.
// To do this, MIR const propagation seems to be the better tool.
// Whenever MIR const prop routines are more developed, this will
// become available. As of this writing (25/03/19) it is not yet.
false
},
}
}

View file

@ -77,7 +77,7 @@ pub(super) fn check<'tcx>(
&format!("transmute from `{from_ty_orig}` which has an undefined layout"),
|diag| {
if from_ty_orig.peel_refs() != from_ty.peel_refs() {
diag.note(&format!("the contained type `{from_ty}` has an undefined layout"));
diag.note(format!("the contained type `{from_ty}` has an undefined layout"));
}
},
);
@ -91,7 +91,7 @@ pub(super) fn check<'tcx>(
&format!("transmute to `{to_ty_orig}` which has an undefined layout"),
|diag| {
if to_ty_orig.peel_refs() != to_ty.peel_refs() {
diag.note(&format!("the contained type `{to_ty}` has an undefined layout"));
diag.note(format!("the contained type `{to_ty}` has an undefined layout"));
}
},
);
@ -119,16 +119,16 @@ pub(super) fn check<'tcx>(
),
|diag| {
if let Some(same_adt_did) = same_adt_did {
diag.note(&format!(
diag.note(format!(
"two instances of the same generic type (`{}`) may have different layouts",
cx.tcx.item_name(same_adt_did)
));
} else {
if from_ty_orig.peel_refs() != from_ty {
diag.note(&format!("the contained type `{from_ty}` has an undefined layout"));
diag.note(format!("the contained type `{from_ty}` has an undefined layout"));
}
if to_ty_orig.peel_refs() != to_ty {
diag.note(&format!("the contained type `{to_ty}` has an undefined layout"));
diag.note(format!("the contained type `{to_ty}` has an undefined layout"));
}
}
},
@ -146,7 +146,7 @@ pub(super) fn check<'tcx>(
&format!("transmute from `{from_ty_orig}` which has an undefined layout"),
|diag| {
if from_ty_orig.peel_refs() != from_ty {
diag.note(&format!("the contained type `{from_ty}` has an undefined layout"));
diag.note(format!("the contained type `{from_ty}` has an undefined layout"));
}
},
);
@ -163,7 +163,7 @@ pub(super) fn check<'tcx>(
&format!("transmute into `{to_ty_orig}` which has an undefined layout"),
|diag| {
if to_ty_orig.peel_refs() != to_ty {
diag.note(&format!("the contained type `{to_ty}` has an undefined layout"));
diag.note(format!("the contained type `{to_ty}` has an undefined layout"));
}
},
);

View file

@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(
&format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"),
|diag| {
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
let sugg = arg.as_ty(&to_ty.to_string()).to_string();
let sugg = arg.as_ty(to_ty.to_string()).to_string();
diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
}
},

View file

@ -18,8 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
// Catching transmute over constants that resolve to `null`.
let mut const_eval_context = constant_context(cx, cx.typeck_results());
if let ExprKind::Path(ref _qpath) = arg.kind &&
let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) &&
x == 0
let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg)
{
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
return true;

View file

@ -61,7 +61,7 @@ pub(super) fn check<'tcx>(
"transmute from an integer to a pointer",
|diag| {
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
diag.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()), Applicability::Unspecified);
diag.span_suggestion(e.span, "try", arg.as_ty(to_ty.to_string()), Applicability::Unspecified);
}
},
);

View file

@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
&format!("usage of `{outer_sym}<{generic_snippet}>`"),
|diag| {
diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability);
diag.note(&format!(
diag.note(format!(
"`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap"
));
},
@ -78,7 +78,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
format!("{outer_sym}<{generic_snippet}>"),
applicability,
);
diag.note(&format!(
diag.note(format!(
"`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
));
},
@ -91,10 +91,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
hir_ty.span,
&format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"),
|diag| {
diag.note(&format!(
diag.note(format!(
"`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
));
diag.help(&format!(
diag.help(format!(
"consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`"
));
},

View file

@ -129,7 +129,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
if arg_snippets_without_empty_blocks.is_empty() {
db.multipart_suggestion(
&format!("use {singular}unit literal{plural} instead"),
format!("use {singular}unit literal{plural} instead"),
args_to_recover
.iter()
.map(|arg| (arg.span, "()".to_string()))
@ -142,7 +142,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
let it_or_them = if plural { "them" } else { "it" };
db.span_suggestion(
expr.span,
&format!(
format!(
"{or}move the expression{empty_or_s} in front of the call and replace {it_or_them} with the unit literal `()`"
),
sugg,

View file

@ -1,11 +1,11 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths};
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
@ -81,16 +81,24 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
}
}
if is_trait_method(cx, e, sym::IntoIterator) && name.ident.name == sym::into_iter {
if let Some(parent_expr) = get_parent_expr(cx, e) {
if let ExprKind::MethodCall(parent_name, ..) = parent_expr.kind {
if parent_name.ident.name != sym::into_iter {
return;
}
}
if get_parent_expr(cx, e).is_some() &&
let Some(id) = path_to_local(recv) &&
let Node::Pat(pat) = cx.tcx.hir().get(id) &&
let PatKind::Binding(ann, ..) = pat.kind &&
ann != BindingAnnotation::MUT
{
// Do not remove .into_iter() applied to a non-mutable local variable used in
// a larger expression context as it would differ in mutability.
return;
}
let a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(recv);
if same_type_and_consts(a, b) {
// If the types are identical then .into_iter() can be removed, unless the type
// implements Copy, in which case .into_iter() returns a copy of the receiver and
// cannot be safely omitted.
if same_type_and_consts(a, b) && !is_copy(cx, b) {
let sugg = snippet(cx, recv.span, "<expr>").into_owned();
span_lint_and_sugg(
cx,

View file

@ -333,7 +333,7 @@ define_Conf! {
/// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
///
/// The maximum allowed size for arrays on the stack
(array_size_threshold: u64 = 512_000),
(array_size_threshold: u128 = 512_000),
/// Lint: VEC_BOX.
///
/// The size of the boxed type in bytes, where boxing in a `Vec` is allowed

View file

@ -558,8 +558,8 @@ impl fmt::Display for ClippyConfiguration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"* `{}`: `{}`: {} (defaults to `{}`)",
self.name, self.config_type, self.doc, self.default
"* `{}`: `{}`(defaults to `{}`): {}",
self.name, self.config_type, self.default, self.doc
)
}
}

View file

@ -377,7 +377,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
// print!("\n"), write!(f, "\n")
diag.multipart_suggestion(
&format!("use `{name}ln!` instead"),
format!("use `{name}ln!` instead"),
vec![(name_span, format!("{name}ln")), (format_string_span, String::new())],
Applicability::MachineApplicable,
);
@ -388,7 +388,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
diag.multipart_suggestion(
&format!("use `{name}ln!` instead"),
format!("use `{name}ln!` instead"),
vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
Applicability::MachineApplicable,
);

View file

@ -620,12 +620,7 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -
ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
int.try_into().expect("invalid f64 bit representation"),
))),
ty::RawPtr(type_and_mut) => {
if let ty::Uint(_) = type_and_mut.ty.kind() {
return Some(Constant::RawPtr(int.assert_bits(int.size())));
}
None
},
ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
// FIXME: implement other conversions.
_ => None,
}

View file

@ -17,7 +17,7 @@ use std::env;
fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) {
if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
if let Some(lint) = lint.name_lower().strip_prefix("clippy::") {
diag.help(&format!(
diag.help(format!(
"for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}",
&option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
// extract just major + minor version and ignore patch versions

View file

@ -1,52 +0,0 @@
use rustc_index::bit_set::BitSet;
use rustc_middle::mir;
use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis};
/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
#[derive(Copy, Clone)]
pub(super) struct MaybeStorageLive;
impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
type Domain = BitSet<mir::Local>;
const NAME: &'static str = "maybe_storage_live";
fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
// bottom = dead
BitSet::new_empty(body.local_decls.len())
}
fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
for arg in body.args_iter() {
state.insert(arg);
}
}
}
impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
type Idx = mir::Local;
fn statement_effect(&self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: mir::Location) {
match stmt.kind {
mir::StatementKind::StorageLive(l) => trans.gen(l),
mir::StatementKind::StorageDead(l) => trans.kill(l),
_ => (),
}
}
fn terminator_effect(
&self,
_trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
_loc: mir::Location,
) {
}
fn call_return_effect(
&self,
_trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {
// Nothing to do when a call returns successfully
}
}

View file

@ -5,8 +5,6 @@ use rustc_middle::mir::{
};
use rustc_middle::ty::TyCtxt;
mod maybe_storage_live;
mod possible_borrower;
pub use possible_borrower::PossibleBorrowerMap;

View file

@ -1,92 +1,137 @@
use super::{
maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor,
transitive_relation::TransitiveRelation,
};
use super::possible_origin::PossibleOriginVisitor;
use crate::ty::is_copy;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_index::bit_set::{BitSet, HybridBitSet};
use rustc_lint::LateContext;
use rustc_middle::mir::{self, visit::Visitor as _, Mutability};
use rustc_middle::ty::{self, visit::TypeVisitor};
use rustc_mir_dataflow::{Analysis, ResultsCursor};
use rustc_middle::mir::{
self, visit::Visitor as _, BasicBlock, Local, Location, Mutability, Statement, StatementKind, Terminator,
};
use rustc_middle::ty::{self, visit::TypeVisitor, TyCtxt};
use rustc_mir_dataflow::{
fmt::DebugWithContext, impls::MaybeStorageLive, lattice::JoinSemiLattice, Analysis, AnalysisDomain,
CallReturnPlaces, ResultsCursor,
};
use std::borrow::Cow;
use std::ops::ControlFlow;
/// Collects the possible borrowers of each local.
/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
/// possible borrowers of `a`.
#[allow(clippy::module_name_repetitions)]
struct PossibleBorrowerVisitor<'a, 'b, 'tcx> {
possible_borrower: TransitiveRelation,
struct PossibleBorrowerAnalysis<'b, 'tcx> {
tcx: TyCtxt<'tcx>,
body: &'b mir::Body<'tcx>,
cx: &'a LateContext<'tcx>,
possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
}
impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> {
#[derive(Clone, Debug, Eq, PartialEq)]
struct PossibleBorrowerState {
map: FxIndexMap<Local, BitSet<Local>>,
domain_size: usize,
}
impl PossibleBorrowerState {
fn new(domain_size: usize) -> Self {
Self {
map: FxIndexMap::default(),
domain_size,
}
}
#[allow(clippy::similar_names)]
fn add(&mut self, borrowed: Local, borrower: Local) {
self.map
.entry(borrowed)
.or_insert(BitSet::new_empty(self.domain_size))
.insert(borrower);
}
}
impl<C> DebugWithContext<C> for PossibleBorrowerState {
fn fmt_with(&self, _ctxt: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<_ as std::fmt::Debug>::fmt(self, f)
}
fn fmt_diff_with(&self, _old: &Self, _ctxt: &C, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
unimplemented!()
}
}
impl JoinSemiLattice for PossibleBorrowerState {
fn join(&mut self, other: &Self) -> bool {
let mut changed = false;
for (&borrowed, borrowers) in other.map.iter() {
if !borrowers.is_empty() {
changed |= self
.map
.entry(borrowed)
.or_insert(BitSet::new_empty(self.domain_size))
.union(borrowers);
}
}
changed
}
}
impl<'b, 'tcx> AnalysisDomain<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> {
type Domain = PossibleBorrowerState;
const NAME: &'static str = "possible_borrower";
fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
PossibleBorrowerState::new(body.local_decls.len())
}
fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _entry_set: &mut Self::Domain) {}
}
impl<'b, 'tcx> PossibleBorrowerAnalysis<'b, 'tcx> {
fn new(
cx: &'a LateContext<'tcx>,
tcx: TyCtxt<'tcx>,
body: &'b mir::Body<'tcx>,
possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
) -> Self {
Self {
possible_borrower: TransitiveRelation::default(),
cx,
tcx,
body,
possible_origin,
}
}
fn into_map(
self,
cx: &'a LateContext<'tcx>,
maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>,
) -> PossibleBorrowerMap<'b, 'tcx> {
let mut map = FxHashMap::default();
for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
if is_copy(cx, self.body.local_decls[row].ty) {
continue;
}
let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len());
borrowers.remove(mir::Local::from_usize(0));
if !borrowers.is_empty() {
map.insert(row, borrowers);
}
}
let bs = BitSet::new_empty(self.body.local_decls.len());
PossibleBorrowerMap {
map,
maybe_live,
bitset: (bs.clone(), bs),
}
}
}
impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> {
fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
let lhs = place.local;
match rvalue {
mir::Rvalue::Ref(_, _, borrowed) => {
self.possible_borrower.add(borrowed.local, lhs);
},
other => {
if ContainsRegion
.visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
.is_continue()
{
return;
}
rvalue_locals(other, |rhs| {
if lhs != rhs {
self.possible_borrower.add(rhs, lhs);
impl<'b, 'tcx> Analysis<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> {
fn apply_call_return_effect(
&self,
_state: &mut Self::Domain,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {
}
fn apply_statement_effect(&self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location) {
if let StatementKind::Assign(box (place, rvalue)) = &statement.kind {
let lhs = place.local;
match rvalue {
mir::Rvalue::Ref(_, _, borrowed) => {
state.add(borrowed.local, lhs);
},
other => {
if ContainsRegion
.visit_ty(place.ty(&self.body.local_decls, self.tcx).ty)
.is_continue()
{
return;
}
});
},
rvalue_locals(other, |rhs| {
if lhs != rhs {
state.add(rhs, lhs);
}
});
},
}
}
}
fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) {
fn apply_terminator_effect(&self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location) {
if let mir::TerminatorKind::Call {
args,
destination: mir::Place { local: dest, .. },
@ -126,10 +171,10 @@ impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b,
for y in mutable_variables {
for x in &immutable_borrowers {
self.possible_borrower.add(*x, y);
state.add(*x, y);
}
for x in &mutable_borrowers {
self.possible_borrower.add(*x, y);
state.add(*x, y);
}
}
}
@ -165,73 +210,98 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
}
}
/// Result of `PossibleBorrowerVisitor`.
/// Result of `PossibleBorrowerAnalysis`.
#[allow(clippy::module_name_repetitions)]
pub struct PossibleBorrowerMap<'b, 'tcx> {
/// Mapping `Local -> its possible borrowers`
pub map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>,
// Caches to avoid allocation of `BitSet` on every query
pub bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
body: &'b mir::Body<'tcx>,
possible_borrower: ResultsCursor<'b, 'tcx, PossibleBorrowerAnalysis<'b, 'tcx>>,
maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'b>>,
pushed: BitSet<Local>,
stack: Vec<Local>,
}
impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> {
pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self {
impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> {
pub fn new(cx: &LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self {
let possible_origin = {
let mut vis = PossibleOriginVisitor::new(mir);
vis.visit_body(mir);
vis.into_map(cx)
};
let maybe_storage_live_result = MaybeStorageLive
let possible_borrower = PossibleBorrowerAnalysis::new(cx.tcx, mir, possible_origin)
.into_engine(cx.tcx, mir)
.pass_name("redundant_clone")
.pass_name("possible_borrower")
.iterate_to_fixpoint()
.into_results_cursor(mir);
let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
vis.visit_body(mir);
vis.into_map(cx, maybe_storage_live_result)
let maybe_live = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len())))
.into_engine(cx.tcx, mir)
.pass_name("possible_borrower")
.iterate_to_fixpoint()
.into_results_cursor(mir);
PossibleBorrowerMap {
body: mir,
possible_borrower,
maybe_live,
pushed: BitSet::new_empty(mir.local_decls.len()),
stack: Vec::with_capacity(mir.local_decls.len()),
}
}
/// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
self.bounded_borrowers(borrowers, borrowers, borrowed, at)
}
/// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below`
/// but no more than `above`.
pub fn bounded_borrowers(
/// Returns true if the set of borrowers of `borrowed` living at `at` includes no more than
/// `borrowers`.
/// Notes:
/// 1. It would be nice if `PossibleBorrowerMap` could store `cx` so that `at_most_borrowers`
/// would not require it to be passed in. But a `PossibleBorrowerMap` is stored in `LintPass`
/// `Dereferencing`, which outlives any `LateContext`.
/// 2. In all current uses of `at_most_borrowers`, `borrowers` is a slice of at most two
/// elements. Thus, `borrowers.contains(...)` is effectively a constant-time operation. If
/// `at_most_borrowers`'s uses were to expand beyond this, its implementation might have to be
/// adjusted.
pub fn at_most_borrowers(
&mut self,
below: &[mir::Local],
above: &[mir::Local],
cx: &LateContext<'tcx>,
borrowers: &[mir::Local],
borrowed: mir::Local,
at: mir::Location,
) -> bool {
self.maybe_live.seek_after_primary_effect(at);
if is_copy(cx, self.body.local_decls[borrowed].ty) {
return true;
}
self.bitset.0.clear();
let maybe_live = &mut self.maybe_live;
if let Some(bitset) = self.map.get(&borrowed) {
for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) {
self.bitset.0.insert(b);
self.possible_borrower.seek_before_primary_effect(at);
self.maybe_live.seek_before_primary_effect(at);
let possible_borrower = &self.possible_borrower.get().map;
let maybe_live = &self.maybe_live;
self.pushed.clear();
self.stack.clear();
if let Some(borrowers) = possible_borrower.get(&borrowed) {
for b in borrowers.iter() {
if self.pushed.insert(b) {
self.stack.push(b);
}
}
} else {
return false;
// Nothing borrows `borrowed` at `at`.
return true;
}
self.bitset.1.clear();
for b in below {
self.bitset.1.insert(*b);
while let Some(borrower) = self.stack.pop() {
if maybe_live.contains(borrower) && !borrowers.contains(&borrower) {
return false;
}
if let Some(borrowers) = possible_borrower.get(&borrower) {
for b in borrowers.iter() {
if self.pushed.insert(b) {
self.stack.push(b);
}
}
}
}
if !self.bitset.0.superset(&self.bitset.1) {
return false;
}
for b in above {
self.bitset.0.remove(*b);
}
self.bitset.0.is_empty()
true
}
pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {

View file

@ -185,7 +185,6 @@ impl<'a> Sugg<'a> {
) -> Self {
use rustc_ast::ast::RangeLimits;
#[expect(clippy::match_wildcard_for_single_variants)]
match expr.kind {
_ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
ast::ExprKind::AddrOf(..)

View file

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2022-12-17"
channel = "nightly-2022-12-29"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View file

@ -3,8 +3,7 @@ error: an async construct yields a type which is itself awaitable
|
LL | let _h = async {
| _____________________-
LL | | async {
| | _________^
LL | |/ async {
LL | || 3
LL | || }
| ||_________^ awaitable value not awaited
@ -37,8 +36,7 @@ error: an async construct yields a type which is itself awaitable
|
LL | let _j = async || {
| ________________________-
LL | | async {
| | _________^
LL | |/ async {
LL | || 3
LL | || }
| ||_________^ awaitable value not awaited

View file

@ -0,0 +1,3 @@
fn main() {
[0; usize::MAX];
}

View file

@ -0,0 +1,10 @@
error: statement with no effect
--> $DIR/ice-10044.rs:2:5
|
LL | [0; usize::MAX];
| ^^^^^^^^^^^^^^^^
|
= note: `-D clippy::no-effect` implied by `-D warnings`
error: aborting due to previous error

View file

@ -14,6 +14,15 @@ fn main() {
let _ = (y as f32).mul_add(y as f32, x);
let _ = x.mul_add(x, y).sqrt();
let _ = y.mul_add(y, x).sqrt();
let _ = (x - 1.0).mul_add(x - 1.0, -y);
let _ = (x - 1.0).mul_add(x - 1.0, -y) + 3.0;
let _ = (x - 1.0).mul_add(x - 1.0, -(y + 3.0));
let _ = (y + 1.0).mul_add(-(y + 1.0), x);
let _ = (3.0 * y).mul_add(-(3.0 * y), x);
let _ = (y + 1.0 + x).mul_add(-(y + 1.0 + x), x);
let _ = (y + 1.0 + 2.0).mul_add(-(y + 1.0 + 2.0), x);
// Cases where the lint shouldn't be applied
let _ = x.powi(2);
let _ = x.powi(1 + 1);

View file

@ -14,6 +14,15 @@ fn main() {
let _ = x + (y as f32).powi(2);
let _ = (x.powi(2) + y).sqrt();
let _ = (x + y.powi(2)).sqrt();
let _ = (x - 1.0).powi(2) - y;
let _ = (x - 1.0).powi(2) - y + 3.0;
let _ = (x - 1.0).powi(2) - (y + 3.0);
let _ = x - (y + 1.0).powi(2);
let _ = x - (3.0 * y).powi(2);
let _ = x - (y + 1.0 + x).powi(2);
let _ = x - (y + 1.0 + 2.0).powi(2);
// Cases where the lint shouldn't be applied
let _ = x.powi(2);
let _ = x.powi(1 + 1);

View file

@ -42,5 +42,47 @@ error: multiply and add expressions can be calculated more efficiently and accur
LL | let _ = (x + y.powi(2)).sqrt();
| ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
error: aborting due to 7 previous errors
error: multiply and add expressions can be calculated more efficiently and accurately
--> $DIR/floating_point_powi.rs:18:13
|
LL | let _ = (x - 1.0).powi(2) - y;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -y)`
error: multiply and add expressions can be calculated more efficiently and accurately
--> $DIR/floating_point_powi.rs:19:13
|
LL | let _ = (x - 1.0).powi(2) - y + 3.0;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -y)`
error: multiply and add expressions can be calculated more efficiently and accurately
--> $DIR/floating_point_powi.rs:20:13
|
LL | let _ = (x - 1.0).powi(2) - (y + 3.0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -(y + 3.0))`
error: multiply and add expressions can be calculated more efficiently and accurately
--> $DIR/floating_point_powi.rs:21:13
|
LL | let _ = x - (y + 1.0).powi(2);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0).mul_add(-(y + 1.0), x)`
error: multiply and add expressions can be calculated more efficiently and accurately
--> $DIR/floating_point_powi.rs:22:13
|
LL | let _ = x - (3.0 * y).powi(2);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(3.0 * y).mul_add(-(3.0 * y), x)`
error: multiply and add expressions can be calculated more efficiently and accurately
--> $DIR/floating_point_powi.rs:23:13
|
LL | let _ = x - (y + 1.0 + x).powi(2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0 + x).mul_add(-(y + 1.0 + x), x)`
error: multiply and add expressions can be calculated more efficiently and accurately
--> $DIR/floating_point_powi.rs:24:13
|
LL | let _ = x - (y + 1.0 + 2.0).powi(2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0 + 2.0).mul_add(-(y + 1.0 + 2.0), x)`
error: aborting due to 14 previous errors

View file

@ -0,0 +1,21 @@
#![allow(unused)]
#![warn(clippy::fn_null_check)]
#![allow(clippy::cmp_null)]
#![allow(clippy::ptr_eq)]
#![allow(clippy::zero_ptr)]
pub const ZPTR: *const () = 0 as *const _;
pub const NOT_ZPTR: *const () = 1 as *const _;
fn main() {
let fn_ptr = main;
if (fn_ptr as *mut ()).is_null() {}
if (fn_ptr as *const u8).is_null() {}
if (fn_ptr as *const ()) == std::ptr::null() {}
if (fn_ptr as *const ()) == (0 as *const ()) {}
if (fn_ptr as *const ()) == ZPTR {}
// no lint
if (fn_ptr as *const ()) == NOT_ZPTR {}
}

View file

@ -0,0 +1,43 @@
error: function pointer assumed to be nullable, even though it isn't
--> $DIR/fn_null_check.rs:13:8
|
LL | if (fn_ptr as *mut ()).is_null() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: try wrapping your function pointer type in `Option<T>` instead, and using `is_none` to check for null pointer value
= note: `-D clippy::fn-null-check` implied by `-D warnings`
error: function pointer assumed to be nullable, even though it isn't
--> $DIR/fn_null_check.rs:14:8
|
LL | if (fn_ptr as *const u8).is_null() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: try wrapping your function pointer type in `Option<T>` instead, and using `is_none` to check for null pointer value
error: function pointer assumed to be nullable, even though it isn't
--> $DIR/fn_null_check.rs:15:8
|
LL | if (fn_ptr as *const ()) == std::ptr::null() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: try wrapping your function pointer type in `Option<T>` instead, and using `is_none` to check for null pointer value
error: function pointer assumed to be nullable, even though it isn't
--> $DIR/fn_null_check.rs:16:8
|
LL | if (fn_ptr as *const ()) == (0 as *const ()) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: try wrapping your function pointer type in `Option<T>` instead, and using `is_none` to check for null pointer value
error: function pointer assumed to be nullable, even though it isn't
--> $DIR/fn_null_check.rs:17:8
|
LL | if (fn_ptr as *const ()) == ZPTR {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: try wrapping your function pointer type in `Option<T>` instead, and using `is_none` to check for null pointer value
error: aborting due to 5 previous errors

View file

@ -116,4 +116,45 @@ fn main() {
},
None => None,
};
match Some(20) {
// Don't Lint, because `Some(3*x)` is not `None`
None => None,
Some(x) => {
if x > 0 {
Some(3 * x)
} else {
Some(x)
}
},
};
// Don't lint: https://github.com/rust-lang/rust-clippy/issues/10088
let result = if let Some(a) = maybe_some() {
if let Some(b) = maybe_some() {
Some(a + b)
} else {
Some(a)
}
} else {
None
};
let allowed_integers = vec![3, 4, 5, 6];
// Don't lint, since there's a side effect in the else branch
match Some(21) {
Some(x) => {
if allowed_integers.contains(&x) {
Some(x)
} else {
println!("Invalid integer: {x:?}");
None
}
},
None => None,
};
}
fn maybe_some() -> Option<u32> {
Some(0)
}

View file

@ -240,4 +240,45 @@ fn main() {
},
None => None,
};
match Some(20) {
// Don't Lint, because `Some(3*x)` is not `None`
None => None,
Some(x) => {
if x > 0 {
Some(3 * x)
} else {
Some(x)
}
},
};
// Don't lint: https://github.com/rust-lang/rust-clippy/issues/10088
let result = if let Some(a) = maybe_some() {
if let Some(b) = maybe_some() {
Some(a + b)
} else {
Some(a)
}
} else {
None
};
let allowed_integers = vec![3, 4, 5, 6];
// Don't lint, since there's a side effect in the else branch
match Some(21) {
Some(x) => {
if allowed_integers.contains(&x) {
Some(x)
} else {
println!("Invalid integer: {x:?}");
None
}
},
None => None,
};
}
fn maybe_some() -> Option<u32> {
Some(0)
}

View file

@ -1,6 +1,6 @@
// run-rustfix
#![warn(clippy::manual_retain)]
#![allow(unused)]
#![allow(unused, clippy::redundant_clone)]
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::BinaryHeap;

View file

@ -1,6 +1,6 @@
// run-rustfix
#![warn(clippy::manual_retain)]
#![allow(unused)]
#![allow(unused, clippy::redundant_clone)]
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::BinaryHeap;

View file

@ -133,3 +133,16 @@ fn issue_9575() {
println!("Needs curlies");
};
}
#[allow(dead_code)]
fn issue_9725(r: Option<u32>) {
let x = r;
match x {
Some(_) => {
println!("Some");
},
None => {
println!("None");
},
};
}

View file

@ -148,3 +148,17 @@ fn issue_9575() {
_ => println!("Needs curlies"),
};
}
#[allow(dead_code)]
fn issue_9725(r: Option<u32>) {
match r {
x => match x {
Some(_) => {
println!("Some");
},
None => {
println!("None");
},
},
};
}

View file

@ -213,5 +213,30 @@ LL + println!("Needs curlies");
LL ~ };
|
error: aborting due to 14 previous errors
error: this match could be written as a `let` statement
--> $DIR/match_single_binding.rs:154:5
|
LL | / match r {
LL | | x => match x {
LL | | Some(_) => {
LL | | println!("Some");
... |
LL | | },
LL | | };
| |_____^
|
help: consider using a `let` statement
|
LL ~ let x = r;
LL + match x {
LL + Some(_) => {
LL + println!("Some");
LL + },
LL + None => {
LL + println!("None");
LL + },
LL ~ };
|
error: aborting due to 15 previous errors

View file

@ -132,3 +132,25 @@ fn main() {
}
}
}
mod issue9993 {
enum Foo {
A(bool),
B,
}
fn test() {
let _ = match Foo::A(true) {
_ if false => 0,
Foo::A(true) => 1,
Foo::A(false) => 2,
Foo::B => 3,
};
let _ = match Foo::B {
_ if false => 0,
Foo::A(_) => 1,
Foo::B => 2,
};
}
}

View file

@ -132,3 +132,25 @@ fn main() {
}
}
}
mod issue9993 {
enum Foo {
A(bool),
B,
}
fn test() {
let _ = match Foo::A(true) {
_ if false => 0,
Foo::A(true) => 1,
Foo::A(false) => 2,
Foo::B => 3,
};
let _ = match Foo::B {
_ if false => 0,
Foo::A(_) => 1,
_ => 2,
};
}
}

View file

@ -48,5 +48,11 @@ error: wildcard matches only a single variant and will also match any future add
LL | _ => (),
| ^ help: try this: `Color::Blue`
error: aborting due to 8 previous errors
error: wildcard matches only a single variant and will also match any future added variants
--> $DIR/match_wildcard_for_single_variants.rs:153:13
|
LL | _ => 2,
| ^ help: try this: `Foo::B`
error: aborting due to 9 previous errors

View file

@ -1,5 +1,5 @@
// run-rustfix
#![feature(lint_reasons)]
#![feature(custom_inner_attributes, lint_reasons, rustc_private)]
#![allow(
unused,
clippy::uninlined_format_args,
@ -491,3 +491,14 @@ mod issue_9782_method_variant {
S.foo::<&[u8; 100]>(&a);
}
}
extern crate rustc_lint;
extern crate rustc_span;
#[allow(dead_code)]
mod span_lint {
use rustc_lint::{LateContext, Lint, LintContext};
fn foo(cx: &LateContext<'_>, lint: &'static Lint) {
cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(String::new()));
}
}

Some files were not shown because too many files have changed in this diff Show more