mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-30 02:33:55 +00:00
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:
commit
9a19e76044
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
33
src/tools/tidy/src/rustdoc_gui_tests.rs
Normal file
33
src/tools/tidy/src/rustdoc_gui_tests.rs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
@ -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"
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
27
tests/ui/cast/issue-106883-is-empty.rs
Normal file
27
tests/ui/cast/issue-106883-is-empty.rs
Normal 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]
|
||||||
|
}
|
58
tests/ui/cast/issue-106883-is-empty.stderr
Normal file
58
tests/ui/cast/issue-106883-is-empty.stderr
Normal 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`.
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user