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:
commit
ad8ae0504c
128 changed files with 2905 additions and 436 deletions
|
@ -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
|
||||
|
|
33
compiler/rustc_error_codes/src/error_codes/E0514.md
Normal file
33
compiler/rustc_error_codes/src/error_codes/E0514.md
Normal 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.
|
40
compiler/rustc_error_codes/src/error_codes/E0519.md
Normal file
40
compiler/rustc_error_codes/src/error_codes/E0519.md
Normal 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`).
|
|
@ -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:
|
||||
//
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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",
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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_) => {
|
||||
|
|
|
@ -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);
|
||||
|
|
7
src/test/rustdoc-ui/issue-106213.rs
Normal file
7
src/test/rustdoc-ui/issue-106213.rs
Normal 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 )
|
||||
}
|
9
src/test/rustdoc-ui/issue-106213.stderr
Normal file
9
src/test/rustdoc-ui/issue-106213.stderr
Normal 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`.
|
|
@ -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 `()`
|
||||
|
|
|
@ -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 `()`
|
||||
|
|
|
@ -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);
|
||||
| | -
|
||||
|
|
|
@ -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);
|
||||
| | -
|
||||
|
|
8
src/test/ui/error-codes/E0519.rs
Normal file
8
src/test/ui/error-codes/E0519.rs
Normal 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
|
9
src/test/ui/error-codes/E0519.stderr
Normal file
9
src/test/ui/error-codes/E0519.stderr
Normal 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`.
|
|
@ -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
|
||||
|
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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() {}
|
|
@ -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
|
||||
|
|
@ -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
|
||||
| |_______________|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
| |________|
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
106
src/tools/clippy/clippy_lints/src/fn_null_check.rs
Normal file
106
src/tools/clippy/clippy_lints/src/fn_null_check.rs
Normal 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);
|
||||
},
|
||||
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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!`");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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`
|
||||
}
|
||||
|
||||
|
|
|
@ -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`"
|
||||
));
|
||||
},
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, "..")),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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" }
|
||||
),
|
||||
|
|
|
@ -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()
|
||||
),
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
73
src/tools/clippy/clippy_lints/src/size_of_ref.rs
Normal file
73
src/tools/clippy/clippy_lints/src/size_of_ref.rs
Normal 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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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`?")
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
},
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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}>`"
|
||||
));
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(..)
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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
|
||||
|
|
3
src/tools/clippy/tests/ui/crashes/ice-10044.rs
Normal file
3
src/tools/clippy/tests/ui/crashes/ice-10044.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
[0; usize::MAX];
|
||||
}
|
10
src/tools/clippy/tests/ui/crashes/ice-10044.stderr
Normal file
10
src/tools/clippy/tests/ui/crashes/ice-10044.stderr
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
21
src/tools/clippy/tests/ui/fn_null_check.rs
Normal file
21
src/tools/clippy/tests/ui/fn_null_check.rs
Normal 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 {}
|
||||
}
|
43
src/tools/clippy/tests/ui/fn_null_check.stderr
Normal file
43
src/tools/clippy/tests/ui/fn_null_check.stderr
Normal 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
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
Loading…
Add table
Reference in a new issue