Auto merge of #106914 - matthiaskrgr:rollup-yh0x4gq, r=matthiaskrgr

Rollup of 5 pull requests

Successful merges:

 - #106888 (Add tidy check to ensure that rustdoc GUI tests start with a small description)
 - #106896 (suggest `is_empty` for collections when casting to `bool`)
 - #106900 (Fix regression in `unused_braces` with macros)
 - #106906 (remove redundant clones)
 - #106909 (Only suggest adding type param if path being resolved was a type)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-01-15 21:35:50 +00:00
commit 9a19e76044
18 changed files with 213 additions and 28 deletions

View File

@ -31,7 +31,9 @@
use super::FnCtxt; use super::FnCtxt;
use crate::type_error_struct; use crate::type_error_struct;
use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed}; use rustc_errors::{
struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::mir::Mutability; use rustc_middle::mir::Mutability;
@ -270,6 +272,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
} }
)); ));
} }
self.try_suggest_collection_to_bool(fcx, &mut err);
err.emit(); err.emit();
} }
CastError::NeedViaInt => { CastError::NeedViaInt => {
@ -517,6 +522,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
} else { } else {
err.span_label(self.span, "invalid cast"); err.span_label(self.span, "invalid cast");
} }
self.try_suggest_collection_to_bool(fcx, &mut err);
err.emit(); err.emit();
} }
CastError::SizedUnsizedCast => { CastError::SizedUnsizedCast => {
@ -1080,4 +1088,40 @@ impl<'a, 'tcx> CastCheck<'tcx> {
}, },
); );
} }
/// Attempt to suggest using `.is_empty` when trying to cast from a
/// collection type to a boolean.
fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diagnostic) {
if self.cast_ty.is_bool() {
let derefed = fcx
.autoderef(self.expr_span, self.expr_ty)
.silence_errors()
.find(|t| matches!(t.0.kind(), ty::Str | ty::Slice(..)));
if let Some((deref_ty, _)) = derefed {
// Give a note about what the expr derefs to.
if deref_ty != self.expr_ty.peel_refs() {
err.span_note(
self.expr_span,
format!(
"this expression `Deref`s to `{}` which implements `is_empty`",
fcx.ty_to_string(deref_ty)
),
);
}
// Create a multipart suggestion: add `!` and `.is_empty()` in
// place of the cast.
let suggestion = vec![
(self.expr_span.shrink_to_lo(), "!".to_string()),
(self.span.with_lo(self.expr_span.hi()), ".is_empty()".to_string()),
];
err.multipart_suggestion_verbose(format!(
"consider using the `is_empty` method on `{}` to determine if it contains anything",
fcx.ty_to_string(self.expr_ty),
), suggestion, Applicability::MaybeIncorrect);
}
}
}
} }

View File

@ -187,9 +187,9 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
}, },
None => cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindDiag), None => cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindDiag),
} }
} else if !ty.span.from_expansion() && path.segments.len() > 1 && let Some(t) = is_ty_or_ty_ctxt(cx, &path) { } else if !ty.span.from_expansion() && path.segments.len() > 1 && let Some(ty) = is_ty_or_ty_ctxt(cx, &path) {
cx.emit_spanned_lint(USAGE_OF_QUALIFIED_TY, path.span, TyQualified { cx.emit_spanned_lint(USAGE_OF_QUALIFIED_TY, path.span, TyQualified {
ty: t.clone(), ty,
suggestion: path.span, suggestion: path.span,
}); });
} }

View File

@ -32,7 +32,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue {
cx.emit_spanned_lint( cx.emit_spanned_lint(
PASS_BY_VALUE, PASS_BY_VALUE,
ty.span, ty.span,
PassByValueDiag { ty: t.clone(), suggestion: ty.span }, PassByValueDiag { ty: t, suggestion: ty.span },
); );
} }
} }

View File

@ -1095,17 +1095,21 @@ impl UnusedDelimLint for UnusedBraces {
// ``` // ```
// - the block has no attribute and was not created inside a macro // - the block has no attribute and was not created inside a macro
// - if the block is an `anon_const`, the inner expr must be a literal // - if the block is an `anon_const`, the inner expr must be a literal
// (do not lint `struct A<const N: usize>; let _: A<{ 2 + 3 }>;`) // not created by a macro, i.e. do not lint on:
// // ```
// struct A<const N: usize>;
// let _: A<{ 2 + 3 }>;
// let _: A<{produces_literal!()}>;
// ```
// FIXME(const_generics): handle paths when #67075 is fixed. // FIXME(const_generics): handle paths when #67075 is fixed.
if let [stmt] = inner.stmts.as_slice() { if let [stmt] = inner.stmts.as_slice() {
if let ast::StmtKind::Expr(ref expr) = stmt.kind { if let ast::StmtKind::Expr(ref expr) = stmt.kind {
if !Self::is_expr_delims_necessary(expr, followed_by_block, false) if !Self::is_expr_delims_necessary(expr, followed_by_block, false)
&& (ctx != UnusedDelimsCtx::AnonConst && (ctx != UnusedDelimsCtx::AnonConst
|| matches!(expr.kind, ast::ExprKind::Lit(_))) || (matches!(expr.kind, ast::ExprKind::Lit(_))
&& !expr.span.from_expansion()))
&& !cx.sess().source_map().is_multiline(value.span) && !cx.sess().source_map().is_multiline(value.span)
&& value.attrs.is_empty() && value.attrs.is_empty()
&& !expr.span.from_expansion()
&& !value.span.from_expansion() && !value.span.from_expansion()
&& !inner.span.from_expansion() && !inner.span.from_expansion()
{ {

View File

@ -3373,7 +3373,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
sugg.to_string(), sugg.to_string(),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
)) ))
} else if res.is_none() { } else if res.is_none() && matches!(source, PathSource::Type) {
this.report_missing_type_error(path) this.report_missing_type_error(path)
} else { } else {
None None

View File

@ -62,6 +62,7 @@ pub mod features;
pub mod mir_opt_tests; pub mod mir_opt_tests;
pub mod pal; pub mod pal;
pub mod primitive_docs; pub mod primitive_docs;
pub mod rustdoc_gui_tests;
pub mod style; pub mod style;
pub mod target_specific_tests; pub mod target_specific_tests;
pub mod tests_placement; pub mod tests_placement;

View File

@ -80,6 +80,7 @@ fn main() {
check!(debug_artifacts, &tests_path); check!(debug_artifacts, &tests_path);
check!(ui_tests, &tests_path); check!(ui_tests, &tests_path);
check!(mir_opt_tests, &tests_path, bless); check!(mir_opt_tests, &tests_path, bless);
check!(rustdoc_gui_tests, &tests_path);
// Checks that only make sense for the compiler. // Checks that only make sense for the compiler.
check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose); check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose);

View File

@ -0,0 +1,33 @@
//! Tidy check to ensure that rustdoc GUI tests start with a small description.
use std::path::Path;
pub fn check(path: &Path, bad: &mut bool) {
crate::walk::walk(
&path.join("rustdoc-gui"),
&mut |p| {
// If there is no extension, it's very likely a folder and we want to go into it.
p.extension().map(|e| e != "goml").unwrap_or(false)
},
&mut |entry, content| {
for line in content.lines() {
if !line.starts_with("// ") {
tidy_error!(
bad,
"{}: rustdoc-gui tests must start with a small description",
entry.path().display(),
);
return;
} else if line.starts_with("// ") {
let parts = line[2..].trim();
// We ignore tidy comments.
if parts.starts_with("// tidy-") {
continue;
}
// All good!
return;
}
}
},
);
}

View File

@ -1,3 +1,5 @@
// Small test to ensure the "src-line-numbers" element is only present once on
// the page.
goto: "file://" + |DOC_PATH| + "/test_docs/index.html" goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
click: ".srclink" click: ".srclink"
wait-for: ".src-line-numbers" wait-for: ".src-line-numbers"

View File

@ -2,8 +2,12 @@ fn main() {
let u = 5 as bool; //~ ERROR cannot cast as `bool` let u = 5 as bool; //~ ERROR cannot cast as `bool`
//~| HELP compare with zero instead //~| HELP compare with zero instead
//~| SUGGESTION 5 != 0 //~| SUGGESTION 5 != 0
let t = (1 + 2) as bool; //~ ERROR cannot cast as `bool` let t = (1 + 2) as bool; //~ ERROR cannot cast as `bool`
//~| HELP compare with zero instead //~| HELP compare with zero instead
//~| SUGGESTION (1 + 2) != 0 //~| SUGGESTION (1 + 2) != 0
let v = "hello" as bool; //~ ERROR casting `&'static str` as `bool` is invalid
let v = "hello" as bool;
//~^ ERROR casting `&'static str` as `bool` is invalid
//~| HELP consider using the `is_empty` method on `&'static str` to determine if it contains anything
} }

View File

@ -5,16 +5,21 @@ LL | let u = 5 as bool;
| ^^^^^^^^^ help: compare with zero instead: `5 != 0` | ^^^^^^^^^ help: compare with zero instead: `5 != 0`
error[E0054]: cannot cast as `bool` error[E0054]: cannot cast as `bool`
--> $DIR/cast-as-bool.rs:5:13 --> $DIR/cast-as-bool.rs:6:13
| |
LL | let t = (1 + 2) as bool; LL | let t = (1 + 2) as bool;
| ^^^^^^^^^^^^^^^ help: compare with zero instead: `(1 + 2) != 0` | ^^^^^^^^^^^^^^^ help: compare with zero instead: `(1 + 2) != 0`
error[E0606]: casting `&'static str` as `bool` is invalid error[E0606]: casting `&'static str` as `bool` is invalid
--> $DIR/cast-as-bool.rs:8:13 --> $DIR/cast-as-bool.rs:10:13
| |
LL | let v = "hello" as bool; LL | let v = "hello" as bool;
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
|
help: consider using the `is_empty` method on `&'static str` to determine if it contains anything
|
LL | let v = !"hello".is_empty();
| + ~~~~~~~~~~~
error: aborting due to 3 previous errors error: aborting due to 3 previous errors

View File

@ -0,0 +1,27 @@
use std::ops::Deref;
struct Foo;
impl Deref for Foo {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&[]
}
}
fn main() {
let _ = "foo" as bool;
//~^ ERROR casting `&'static str` as `bool` is invalid [E0606]
let _ = String::from("foo") as bool;
//~^ ERROR non-primitive cast: `String` as `bool` [E0605]
let _ = Foo as bool;
//~^ ERROR non-primitive cast: `Foo` as `bool` [E0605]
}
fn _slice(bar: &[i32]) -> bool {
bar as bool
//~^ ERROR casting `&[i32]` as `bool` is invalid [E0606]
}

View File

@ -0,0 +1,58 @@
error[E0606]: casting `&'static str` as `bool` is invalid
--> $DIR/issue-106883-is-empty.rs:14:13
|
LL | let _ = "foo" as bool;
| ^^^^^^^^^^^^^
|
help: consider using the `is_empty` method on `&'static str` to determine if it contains anything
|
LL | let _ = !"foo".is_empty();
| + ~~~~~~~~~~~
error[E0605]: non-primitive cast: `String` as `bool`
--> $DIR/issue-106883-is-empty.rs:17:13
|
LL | let _ = String::from("foo") as bool;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
|
note: this expression `Deref`s to `str` which implements `is_empty`
--> $DIR/issue-106883-is-empty.rs:17:13
|
LL | let _ = String::from("foo") as bool;
| ^^^^^^^^^^^^^^^^^^^
help: consider using the `is_empty` method on `String` to determine if it contains anything
|
LL | let _ = !String::from("foo").is_empty();
| + ~~~~~~~~~~~
error[E0605]: non-primitive cast: `Foo` as `bool`
--> $DIR/issue-106883-is-empty.rs:20:13
|
LL | let _ = Foo as bool;
| ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
|
note: this expression `Deref`s to `[u8]` which implements `is_empty`
--> $DIR/issue-106883-is-empty.rs:20:13
|
LL | let _ = Foo as bool;
| ^^^
help: consider using the `is_empty` method on `Foo` to determine if it contains anything
|
LL | let _ = !Foo.is_empty();
| + ~~~~~~~~~~~
error[E0606]: casting `&[i32]` as `bool` is invalid
--> $DIR/issue-106883-is-empty.rs:25:5
|
LL | bar as bool
| ^^^^^^^^^^^
|
help: consider using the `is_empty` method on `&[i32]` to determine if it contains anything
|
LL | !bar.is_empty()
| + ~~~~~~~~~~~
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0605, E0606.
For more information about an error, try `rustc --explain E0605`.

View File

@ -50,4 +50,8 @@ fn main() {
if { return } { if { return } {
} }
// regression test for https://github.com/rust-lang/rust/issues/106899
return println!("!");
//~^ WARN unnecessary braces
} }

View File

@ -50,4 +50,8 @@ fn main() {
if { return } { if { return } {
} }
// regression test for https://github.com/rust-lang/rust/issues/106899
return { println!("!") };
//~^ WARN unnecessary braces
} }

View File

@ -68,5 +68,17 @@ LL - consume({ 7 });
LL + consume(7); LL + consume(7);
| |
warning: 5 warnings emitted warning: unnecessary braces around `return` value
--> $DIR/unused_braces.rs:55:12
|
LL | return { println!("!") };
| ^^ ^^
|
help: remove these braces
|
LL - return { println!("!") };
LL + return println!("!");
|
warning: 6 warnings emitted

View File

@ -24,16 +24,7 @@ error[E0425]: cannot find value `F` in this scope
--> $DIR/constrain-suggest-ice.rs:6:9 --> $DIR/constrain-suggest-ice.rs:6:9
| |
LL | F LL | F
| ^ | ^ help: a local variable with a similar name exists: `x`
|
help: a local variable with a similar name exists
|
LL | x
| ~
help: you might be missing a type parameter
|
LL | struct Bug<S, F>{
| +++
error: generic `Self` types are currently not permitted in anonymous constants error: generic `Self` types are currently not permitted in anonymous constants
--> $DIR/constrain-suggest-ice.rs:3:21 --> $DIR/constrain-suggest-ice.rs:3:21

View File

@ -3,11 +3,6 @@ error[E0405]: cannot find trait `Oops` in this scope
| |
LL | let _: S<impl Oops> = S; LL | let _: S<impl Oops> = S;
| ^^^^ not found in this scope | ^^^^ not found in this scope
|
help: you might be missing a type parameter
|
LL | fn f<Oops>() {
| ++++++
error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in variable binding error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in variable binding
--> $DIR/issue-104513-ice.rs:3:14 --> $DIR/issue-104513-ice.rs:3:14