Auto merge of #155596 - jhpratt:rollup-zyXU16X, r=jhpratt

Rollup of 6 pull requests

Successful merges:

 - rust-lang/rust#155028 (tests: add whitespace tests for vertical tab behavior)
 - rust-lang/rust#155582 (Rewrite `FlatMapInPlace`.)
 - rust-lang/rust#151194 (Fix wrong suggestion for returning async closure)
 - rust-lang/rust#154377 (Fix `#[expect(dead_code)]` liveness propagation)
 - rust-lang/rust#155572 (Move diagnostic attribute target checks from check_attr)
 - rust-lang/rust#155586 (Ensure we don't feed owners from ast lowering if we ever make that query tracked)
This commit is contained in:
bors
2026-04-21 13:03:12 +00:00
34 changed files with 840 additions and 297 deletions
@@ -1,11 +1,16 @@
use rustc_errors::Diagnostic;
use rustc_feature::{AttributeTemplate, template};
use rustc_hir::Target;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES;
use rustc_session::lint::builtin::{
MALFORMED_DIAGNOSTIC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES,
};
use rustc_span::{Symbol, sym};
use crate::attributes::{OnDuplicate, SingleAttributeParser};
use crate::context::{AcceptContext, Stage};
use crate::errors::IncorrectDoNotRecommendLocation;
use crate::parser::ArgParser;
use crate::target_checking::{ALL_TARGETS, AllowedTargets};
@@ -13,7 +18,8 @@
impl<S: Stage> SingleAttributeParser<S> for DoNotRecommendParser {
const PATH: &[Symbol] = &[sym::diagnostic, sym::do_not_recommend];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Checked in check_attr.
// "Allowed" on any target, noop on all but trait impls
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
const TEMPLATE: AttributeTemplate = template!(Word /*doesn't matter */);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
@@ -25,6 +31,19 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
attr_span,
);
}
if !matches!(cx.target, Target::Impl { of_trait: true }) {
let target_span = cx.target_span;
cx.emit_dyn_lint(
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
move |dcx, level| {
IncorrectDoNotRecommendLocation { target_span }.into_diag(dcx, level)
},
attr_span,
);
return None;
}
Some(AttributeKind::DoNotRecommend { attr_span })
}
}
@@ -1,8 +1,10 @@
use rustc_errors::Diagnostic;
use rustc_hir::attrs::diagnostic::Directive;
use rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES;
use crate::attributes::diagnostic::*;
use crate::attributes::prelude::*;
use crate::errors::DiagnosticOnConstOnlyForTraitImpls;
#[derive(Default)]
pub(crate) struct OnConstParser {
span: Option<Span>,
@@ -21,6 +23,21 @@ impl<S: Stage> AttributeParser<S> for OnConstParser {
let span = cx.attr_span;
this.span = Some(span);
// FIXME(mejrs) no constness field on `Target`,
// so non-constness is still checked in check_attr.rs
if !matches!(cx.target, Target::Impl { of_trait: true }) {
let target_span = cx.target_span;
cx.emit_dyn_lint(
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
move |dcx, level| {
DiagnosticOnConstOnlyForTraitImpls { target_span }.into_diag(dcx, level)
},
span,
);
return;
}
let mode = Mode::DiagnosticOnConst;
let Some(items) = parse_list(cx, args, mode) else { return };
@@ -32,7 +49,8 @@ impl<S: Stage> AttributeParser<S> for OnConstParser {
},
)];
//FIXME Still checked in `check_attr.rs`
// "Allowed" on all targets; noop on anything but non-const trait impls;
// this linted on in parser.
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
@@ -1,10 +1,13 @@
use rustc_errors::Diagnostic;
use rustc_feature::template;
use rustc_hir::attrs::AttributeKind;
use rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES;
use rustc_span::sym;
use crate::attributes::diagnostic::*;
use crate::attributes::prelude::*;
use crate::context::{AcceptContext, Stage};
use crate::errors::DiagnosticOnMoveOnlyForAdt;
use crate::parser::ArgParser;
use crate::target_checking::{ALL_TARGETS, AllowedTargets};
@@ -29,6 +32,15 @@ fn parse<'sess, S: Stage>(
let span = cx.attr_span;
self.span = Some(span);
if !matches!(cx.target, Target::Enum | Target::Struct | Target::Union) {
cx.emit_dyn_lint(
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
move |dcx, level| DiagnosticOnMoveOnlyForAdt.into_diag(dcx, level),
span,
);
return;
}
let Some(items) = parse_list(cx, args, mode) else { return };
if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) {
@@ -44,6 +56,8 @@ impl<S: Stage> AttributeParser<S> for OnMoveParser {
this.parse(cx, args, Mode::DiagnosticOnMove);
},
)];
// "Allowed" for all targets but noop if used on not-adt.
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
@@ -1,7 +1,10 @@
use rustc_errors::Diagnostic;
use rustc_hir::attrs::diagnostic::Directive;
use rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES;
use crate::attributes::diagnostic::*;
use crate::attributes::prelude::*;
use crate::errors::DiagnosticOnUnimplementedOnlyForTraits;
#[derive(Default)]
pub(crate) struct OnUnimplementedParser {
@@ -19,11 +22,12 @@ fn parse<'sess, S: Stage>(
let span = cx.attr_span;
self.span = Some(span);
// If target is not a trait, returning early will make `finalize` emit a
// `AttributeKind::OnUnimplemented {span, directive: None }`, to prevent it being
// accidentally used on non-trait items like trait aliases.
if !matches!(cx.target, Target::Trait) {
// Lint later emitted in check_attr
cx.emit_dyn_lint(
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
move |dcx, level| DiagnosticOnUnimplementedOnlyForTraits.into_diag(dcx, level),
span,
);
return;
}
@@ -1,7 +1,11 @@
use rustc_errors::Diagnostic;
use rustc_hir::attrs::diagnostic::Directive;
use rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES;
use crate::ShouldEmit;
use crate::attributes::diagnostic::*;
use crate::attributes::prelude::*;
use crate::errors::DiagnosticOnUnknownOnlyForImports;
#[derive(Default)]
pub(crate) struct OnUnknownParser {
@@ -25,6 +29,22 @@ fn parse<'sess, S: Stage>(
let span = cx.attr_span;
self.span = Some(span);
// At early parsing we get passed `Target::Crate` regardless of the item we're on.
// Only do target checking if we're late.
let early = matches!(cx.stage.should_emit(), ShouldEmit::Nothing);
if !early && !matches!(cx.target, Target::Use) {
let target_span = cx.target_span;
cx.emit_dyn_lint(
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
move |dcx, level| {
DiagnosticOnUnknownOnlyForImports { target_span }.into_diag(dcx, level)
},
span,
);
return;
}
let Some(items) = parse_list(cx, args, mode) else { return };
if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) {
@@ -41,7 +61,7 @@ impl<S: Stage> AttributeParser<S> for OnUnknownParser {
this.parse(cx, args, Mode::DiagnosticOnUnknown);
},
)];
//FIXME attribute is not parsed for non-use statements but diagnostics are issued in `check_attr.rs`
// "Allowed" for all targets, but noop for all but use statements.
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
+29
View File
@@ -252,3 +252,32 @@ pub(crate) struct DocUnknownAny {
#[derive(Diagnostic)]
#[diag("expected boolean for `#[doc(auto_cfg = ...)]`")]
pub(crate) struct DocAutoCfgWrongLiteral;
#[derive(Diagnostic)]
#[diag("`#[diagnostic::on_const]` can only be applied to non-const trait implementations")]
pub(crate) struct DiagnosticOnConstOnlyForTraitImpls {
#[label("not a trait implementation")]
pub target_span: Span,
}
#[derive(Diagnostic)]
#[diag("`#[diagnostic::on_move]` can only be applied to enums, structs or unions")]
pub(crate) struct DiagnosticOnMoveOnlyForAdt;
#[derive(Diagnostic)]
#[diag("`#[diagnostic::on_unimplemented]` can only be applied to trait definitions")]
pub(crate) struct DiagnosticOnUnimplementedOnlyForTraits;
#[derive(Diagnostic)]
#[diag("`#[diagnostic::on_unknown]` can only be applied to `use` statements")]
pub(crate) struct DiagnosticOnUnknownOnlyForImports {
#[label("not an import")]
pub target_span: Span,
}
#[derive(Diagnostic)]
#[diag("`#[diagnostic::do_not_recommend]` can only be placed on trait implementations")]
pub(crate) struct IncorrectDoNotRecommendLocation {
#[label("not a trait implementation")]
pub target_span: Span,
}
@@ -1,81 +1,157 @@
use std::{mem, ptr};
use smallvec::{Array, SmallVec};
use smallvec::SmallVec;
use thin_vec::ThinVec;
pub trait FlatMapInPlace<T>: Sized {
pub trait FlatMapInPlace<T> {
/// `f` turns each element into 0..many elements. This function will consume the existing
/// elements in a vec-like structure and replace them with any number of new elements — fewer,
/// more, or the same number — as efficiently as possible.
fn flat_map_in_place<F, I>(&mut self, f: F)
where
F: FnMut(T) -> I,
I: IntoIterator<Item = T>;
}
// The implementation of this method is syntactically identical for all the
// different vector types.
macro_rules! flat_map_in_place {
($vec:ident $( where T: $bound:path)?) => {
fn flat_map_in_place<F, I>(&mut self, mut f: F)
where
F: FnMut(T) -> I,
I: IntoIterator<Item = T>,
{
struct LeakGuard<'a, T $(: $bound)?>(&'a mut $vec<T>);
// Blanket impl for all vec-like types that impl `FlatMapInPlaceVec`.
impl<V: FlatMapInPlaceVec> FlatMapInPlace<V::Elem> for V {
fn flat_map_in_place<F, I>(&mut self, mut f: F)
where
F: FnMut(V::Elem) -> I,
I: IntoIterator<Item = V::Elem>,
{
struct LeakGuard<'a, V: FlatMapInPlaceVec>(&'a mut V);
impl<'a, T $(: $bound)?> Drop for LeakGuard<'a, T> {
fn drop(&mut self) {
unsafe {
self.0.set_len(0); // make sure we just leak elements in case of panic
}
impl<'a, V: FlatMapInPlaceVec> Drop for LeakGuard<'a, V> {
fn drop(&mut self) {
unsafe {
// Leak all elements in case of panic.
self.0.set_len(0);
}
}
let this = LeakGuard(self);
let mut read_i = 0;
let mut write_i = 0;
unsafe {
while read_i < this.0.len() {
// move the read_i'th item out of the vector and map it
// to an iterator
let e = ptr::read(this.0.as_ptr().add(read_i));
let iter = f(e).into_iter();
read_i += 1;
for e in iter {
if write_i < read_i {
ptr::write(this.0.as_mut_ptr().add(write_i), e);
write_i += 1;
} else {
// If this is reached we ran out of space
// in the middle of the vector.
// However, the vector is in a valid state here,
// so we just do a somewhat inefficient insert.
this.0.insert(write_i, e);
read_i += 1;
write_i += 1;
}
}
}
// write_i tracks the number of actually written new items.
this.0.set_len(write_i);
// The ThinVec is in a sane state again. Prevent the LeakGuard from leaking the data.
mem::forget(this);
}
}
};
let guard = LeakGuard(self);
let mut read_i = 0;
let mut write_i = 0;
unsafe {
while read_i < guard.0.len() {
// Move the read_i'th item out of the vector and map it to an iterator.
let e = ptr::read(guard.0.as_ptr().add(read_i));
let iter = f(e).into_iter();
read_i += 1;
for e in iter {
if write_i < read_i {
ptr::write(guard.0.as_mut_ptr().add(write_i), e);
write_i += 1;
} else {
// If this is reached we ran out of space in the middle of the vector.
// However, the vector is in a valid state here, so we just do a somewhat
// inefficient insert.
guard.0.insert(write_i, e);
read_i += 1;
write_i += 1;
}
}
}
// `write_i` tracks the number of actually written new items.
guard.0.set_len(write_i);
// `vec` is in a sane state again. Prevent the LeakGuard from leaking the data.
mem::forget(guard);
}
}
}
impl<T> FlatMapInPlace<T> for Vec<T> {
flat_map_in_place!(Vec);
// A vec-like type must implement these operations to support `flat_map_in_place`.
pub trait FlatMapInPlaceVec {
type Elem;
fn len(&self) -> usize;
unsafe fn set_len(&mut self, len: usize);
fn as_ptr(&self) -> *const Self::Elem;
fn as_mut_ptr(&mut self) -> *mut Self::Elem;
fn insert(&mut self, idx: usize, elem: Self::Elem);
}
impl<T, A: Array<Item = T>> FlatMapInPlace<T> for SmallVec<A> {
flat_map_in_place!(SmallVec where T: Array);
impl<T> FlatMapInPlaceVec for Vec<T> {
type Elem = T;
fn len(&self) -> usize {
self.len()
}
unsafe fn set_len(&mut self, len: usize) {
unsafe {
self.set_len(len);
}
}
fn as_ptr(&self) -> *const Self::Elem {
self.as_ptr()
}
fn as_mut_ptr(&mut self) -> *mut Self::Elem {
self.as_mut_ptr()
}
fn insert(&mut self, idx: usize, elem: Self::Elem) {
self.insert(idx, elem);
}
}
impl<T> FlatMapInPlace<T> for ThinVec<T> {
flat_map_in_place!(ThinVec);
impl<T> FlatMapInPlaceVec for ThinVec<T> {
type Elem = T;
fn len(&self) -> usize {
self.len()
}
unsafe fn set_len(&mut self, len: usize) {
unsafe {
self.set_len(len);
}
}
fn as_ptr(&self) -> *const Self::Elem {
self.as_slice().as_ptr()
}
fn as_mut_ptr(&mut self) -> *mut Self::Elem {
self.as_mut_slice().as_mut_ptr()
}
fn insert(&mut self, idx: usize, elem: Self::Elem) {
self.insert(idx, elem);
}
}
impl<T, const N: usize> FlatMapInPlaceVec for SmallVec<[T; N]> {
type Elem = T;
fn len(&self) -> usize {
self.len()
}
unsafe fn set_len(&mut self, len: usize) {
unsafe {
self.set_len(len);
}
}
fn as_ptr(&self) -> *const Self::Elem {
self.as_ptr()
}
fn as_mut_ptr(&mut self) -> *mut Self::Elem {
self.as_mut_ptr()
}
fn insert(&mut self, idx: usize, elem: Self::Elem) {
self.insert(idx, elem);
}
}
+1
View File
@@ -700,6 +700,7 @@ pub fn feed_anon_const_type(self, key: LocalDefId, value: ty::EarlyBinder<'tcx,
/// Feeds the HIR delayed owner during AST -> HIR delayed lowering.
pub fn feed_delayed_owner(self, key: LocalDefId, owner: MaybeOwner<'tcx>) {
self.dep_graph.assert_ignored();
TyCtxtFeed { tcx: self, key }.delayed_owner(owner);
}
}
+17 -111
View File
@@ -51,31 +51,9 @@
use crate::errors;
#[derive(Diagnostic)]
#[diag("`#[diagnostic::on_unimplemented]` can only be applied to trait definitions")]
struct DiagnosticOnUnimplementedOnlyForTraits;
#[derive(Diagnostic)]
#[diag("`#[diagnostic::on_const]` can only be applied to trait impls")]
struct DiagnosticOnConstOnlyForTraitImpls {
#[label("not a trait impl")]
item_span: Span,
}
#[derive(Diagnostic)]
#[diag("`#[diagnostic::on_const]` can only be applied to non-const trait impls")]
#[diag("`#[diagnostic::on_const]` can only be applied to non-const trait implementations")]
struct DiagnosticOnConstOnlyForNonConstTraitImpls {
#[label("this is a const trait impl")]
item_span: Span,
}
#[derive(Diagnostic)]
#[diag("`#[diagnostic::on_move]` can only be applied to enums, structs or unions")]
struct DiagnosticOnMoveOnlyForAdt;
#[derive(Diagnostic)]
#[diag("`#[diagnostic::on_unknown]` can only be applied to `use` statements")]
struct DiagnosticOnUnknownOnlyForImports {
#[label("not an import")]
#[label("this is a const trait implementation")]
item_span: Span,
}
@@ -221,12 +199,10 @@ fn check_attributes(
Attribute::Parsed(AttributeKind::RustcMustImplementOneOf { attr_span, fn_names }) => {
self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id,target)
},
Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)},
Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())},
Attribute::Parsed(AttributeKind::OnUnknown { span, .. }) => { self.check_diagnostic_on_unknown(*span, hir_id, target) },
Attribute::Parsed(AttributeKind::OnUnimplemented{directive,..}) => {self.check_diagnostic_on_unimplemented(hir_id, directive.as_deref())},
Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)}
Attribute::Parsed(AttributeKind::OnMove { span, directive }) => {
self.check_diagnostic_on_move(*span, hir_id, target, directive.as_deref())
Attribute::Parsed(AttributeKind::OnMove { directive , .. }) => {
self.check_diagnostic_on_move(hir_id, directive.as_deref())
},
Attribute::Parsed(
// tidy-alphabetical-start
@@ -245,6 +221,7 @@ fn check_attributes(
| AttributeKind::CustomMir(..)
| AttributeKind::DebuggerVisualizer(..)
| AttributeKind::DefaultLibAllocator
| AttributeKind::DoNotRecommend {..}
// `#[doc]` is actually a lot more than just doc comments, so is checked below
| AttributeKind::DocComment {..}
| AttributeKind::EiiDeclaration { .. }
@@ -275,6 +252,7 @@ fn check_attributes(
| AttributeKind::NoMain
| AttributeKind::NoMangle(..)
| AttributeKind::NoStd { .. }
| AttributeKind::OnUnknown { .. }
| AttributeKind::Optimize(..)
| AttributeKind::PanicRuntime
| AttributeKind::PatchableFunctionEntry { .. }
@@ -512,47 +490,8 @@ fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) {
}
}
/// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl
fn check_do_not_recommend(
&self,
attr_span: Span,
hir_id: HirId,
target: Target,
item: Option<ItemLike<'_>>,
) {
if !matches!(target, Target::Impl { .. })
|| matches!(
item,
Some(ItemLike::Item(hir::Item { kind: hir::ItemKind::Impl(_impl),.. }))
if _impl.of_trait.is_none()
)
{
self.tcx.emit_node_span_lint(
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
errors::IncorrectDoNotRecommendLocation,
);
}
}
/// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition
fn check_diagnostic_on_unimplemented(
&self,
attr_span: Span,
hir_id: HirId,
target: Target,
directive: Option<&Directive>,
) {
if !matches!(target, Target::Trait) {
self.tcx.emit_node_span_lint(
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
DiagnosticOnUnimplementedOnlyForTraits,
);
}
/// Checks use of generic formatting parameters in `#[diagnostic::on_unimplemented]`
fn check_diagnostic_on_unimplemented(&self, hir_id: HirId, directive: Option<&Directive>) {
if let Some(directive) = directive {
if let Node::Item(Item {
kind: ItemKind::Trait(_, _, _, _, trait_name, generics, _, _),
@@ -587,7 +526,7 @@ fn check_diagnostic_on_unimplemented(
}
}
/// Checks if `#[diagnostic::on_const]` is applied to a trait impl
/// Checks if `#[diagnostic::on_const]` is applied to a on-const trait impl
fn check_diagnostic_on_const(
&self,
attr_span: Span,
@@ -595,6 +534,8 @@ fn check_diagnostic_on_const(
target: Target,
item: Option<ItemLike<'_>>,
) {
// We only check the non-constness here. A diagnostic for use
// on not-trait impl items is issued during attribute parsing.
if target == (Target::Impl { of_trait: true }) {
match item.unwrap() {
ItemLike::Item(it) => match it.expect_impl().constness {
@@ -613,35 +554,13 @@ fn check_diagnostic_on_const(
ItemLike::ForeignItem => {}
}
}
let item_span = self.tcx.hir_span(hir_id);
self.tcx.emit_node_span_lint(
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
DiagnosticOnConstOnlyForTraitImpls { item_span },
);
// We don't check the validity of generic args here...whose generics would that be, anyway?
// The traits' or the impls'?
// FIXME(#155570) Can we do something with generic args here?
// regardless, we don't check the validity of generic args here
// ...whose generics would that be, anyway? The traits' or the impls'?
}
/// Checks if `#[diagnostic::on_move]` is applied to an ADT definition
fn check_diagnostic_on_move(
&self,
attr_span: Span,
hir_id: HirId,
target: Target,
directive: Option<&Directive>,
) {
if !matches!(target, Target::Enum | Target::Struct | Target::Union) {
self.tcx.emit_node_span_lint(
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
DiagnosticOnMoveOnlyForAdt,
);
}
/// Checks use of generic formatting parameters in `#[diagnostic::on_move]`
fn check_diagnostic_on_move(&self, hir_id: HirId, directive: Option<&Directive>) {
if let Some(directive) = directive {
if let Node::Item(Item {
kind:
@@ -675,19 +594,6 @@ fn check_diagnostic_on_move(
}
}
/// Checks if `#[diagnostic::on_unknown]` is applied to a trait impl
fn check_diagnostic_on_unknown(&self, attr_span: Span, hir_id: HirId, target: Target) {
if !matches!(target, Target::Use) {
let item_span = self.tcx.hir_span(hir_id);
self.tcx.emit_node_span_lint(
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
DiagnosticOnUnknownOnlyForImports { item_span },
);
}
}
/// Checks if an `#[inline]` is applied to a function or a closure.
fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) {
match target {
+127 -35
View File
@@ -8,7 +8,7 @@
use hir::def_id::{LocalDefIdMap, LocalDefIdSet};
use rustc_abi::FieldIdx;
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_errors::{ErrorGuaranteed, MultiSpan};
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
@@ -75,11 +75,46 @@ enum ComesFromAllowExpect {
No,
}
/// Carries both the propagated `allow/expect` context and the current item's
/// own `allow/expect` status.
///
/// For example:
///
/// ```rust
/// #[expect(dead_code)]
/// fn root() { middle() }
///
/// fn middle() { leaf() }
///
/// #[expect(dead_code)]
/// fn leaf() {}
/// ```
///
/// The seed for `root` starts as `propagated = Yes, own = Yes`.
///
/// When `root` reaches `middle`, the propagated context stays `Yes`, but
/// `middle` itself does not have `#[allow(dead_code)]` or `#[expect(dead_code)]`,
/// so its work item becomes `propagated = Yes, own = No`.
///
/// When `middle` reaches `leaf`, that same propagated `Yes` context is preserved,
/// and since `leaf` itself has `#[expect(dead_code)]`, its work item becomes
/// `propagated = Yes, own = Yes`.
///
/// In general, `propagated` controls whether descendants are still explored
/// under an `allow/expect` context, while `own` controls whether the current
/// item itself should be excluded from `live_symbols`.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
struct WorkItem {
id: LocalDefId,
propagated: ComesFromAllowExpect,
own: ComesFromAllowExpect,
}
struct MarkSymbolVisitor<'tcx> {
worklist: Vec<(LocalDefId, ComesFromAllowExpect)>,
worklist: Vec<WorkItem>,
tcx: TyCtxt<'tcx>,
maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
scanned: LocalDefIdSet,
scanned: FxHashSet<(LocalDefId, ComesFromAllowExpect)>,
live_symbols: LocalDefIdSet,
repr_unconditionally_treats_fields_as_live: bool,
repr_has_repr_simd: bool,
@@ -89,6 +124,7 @@ struct MarkSymbolVisitor<'tcx> {
// and the span of their respective impl (i.e., part of the derive
// macro)
ignored_derived_traits: LocalDefIdMap<FxIndexSet<DefId>>,
propagated_comes_from_allow_expect: ComesFromAllowExpect,
}
impl<'tcx> MarkSymbolVisitor<'tcx> {
@@ -101,19 +137,45 @@ fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
.expect("`MarkSymbolVisitor::typeck_results` called outside of body")
}
/// Returns whether `def_id` itself should be treated as coming from
/// `#[allow(dead_code)]` or `#[expect(dead_code)]` in the current
/// propagated work-item context.
fn own_comes_from_allow_expect(&self, def_id: LocalDefId) -> ComesFromAllowExpect {
if self.propagated_comes_from_allow_expect == ComesFromAllowExpect::Yes
&& let Some(ComesFromAllowExpect::Yes) =
has_allow_dead_code_or_lang_attr(self.tcx, def_id)
{
ComesFromAllowExpect::Yes
} else {
ComesFromAllowExpect::No
}
}
fn check_def_id(&mut self, def_id: DefId) {
if let Some(def_id) = def_id.as_local() {
let own_comes_from_allow_expect = self.own_comes_from_allow_expect(def_id);
if should_explore(self.tcx, def_id) {
self.worklist.push((def_id, ComesFromAllowExpect::No));
self.worklist.push(WorkItem {
id: def_id,
propagated: self.propagated_comes_from_allow_expect,
own: own_comes_from_allow_expect,
});
}
if own_comes_from_allow_expect == ComesFromAllowExpect::No {
self.live_symbols.insert(def_id);
}
self.live_symbols.insert(def_id);
}
}
fn insert_def_id(&mut self, def_id: DefId) {
if let Some(def_id) = def_id.as_local() {
debug_assert!(!should_explore(self.tcx, def_id));
self.live_symbols.insert(def_id);
if self.own_comes_from_allow_expect(def_id) == ComesFromAllowExpect::No {
self.live_symbols.insert(def_id);
}
}
}
@@ -323,7 +385,8 @@ fn handle_offset_of(&mut self, expr: &'tcx hir::Expr<'tcx>) {
fn mark_live_symbols(&mut self) -> <MarkSymbolVisitor<'tcx> as Visitor<'tcx>>::Result {
while let Some(work) = self.worklist.pop() {
let (mut id, comes_from_allow_expect) = work;
let WorkItem { mut id, propagated, own } = work;
self.propagated_comes_from_allow_expect = propagated;
// in the case of tuple struct constructors we want to check the item,
// not the generated tuple struct constructor function
@@ -352,14 +415,14 @@ fn mark_live_symbols(&mut self) -> <MarkSymbolVisitor<'tcx> as Visitor<'tcx>>::R
// this "duplication" is essential as otherwise a function with `#[expect]`
// called from a `pub fn` may be falsely reported as not live, falsely
// triggering the `unfulfilled_lint_expectations` lint.
match comes_from_allow_expect {
match own {
ComesFromAllowExpect::Yes => {}
ComesFromAllowExpect::No => {
self.live_symbols.insert(id);
}
}
if !self.scanned.insert(id) {
if !self.scanned.insert((id, propagated)) {
continue;
}
@@ -694,7 +757,11 @@ fn visit_trait_ref(&mut self, t: &'tcx hir::TraitRef<'tcx>) -> Self::Result {
)
.and_then(|item| item.def_id.as_local())
{
self.worklist.push((local_def_id, ComesFromAllowExpect::No));
self.worklist.push(WorkItem {
id: local_def_id,
propagated: ComesFromAllowExpect::No,
own: ComesFromAllowExpect::No,
});
}
}
}
@@ -752,23 +819,27 @@ fn has_used_like_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
fn maybe_record_as_seed<'tcx>(
tcx: TyCtxt<'tcx>,
owner_id: hir::OwnerId,
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
worklist: &mut Vec<WorkItem>,
unsolved_items: &mut Vec<LocalDefId>,
) {
let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, owner_id.def_id);
if let Some(comes_from_allow) = allow_dead_code {
worklist.push((owner_id.def_id, comes_from_allow));
worklist.push(WorkItem {
id: owner_id.def_id,
propagated: comes_from_allow,
own: comes_from_allow,
});
}
match tcx.def_kind(owner_id) {
DefKind::Enum => {
if let Some(comes_from_allow) = allow_dead_code {
let adt = tcx.adt_def(owner_id);
worklist.extend(
adt.variants()
.iter()
.map(|variant| (variant.def_id.expect_local(), comes_from_allow)),
);
worklist.extend(adt.variants().iter().map(|variant| WorkItem {
id: variant.def_id.expect_local(),
propagated: comes_from_allow,
own: comes_from_allow,
}));
}
}
DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::AssocTy => {
@@ -783,7 +854,11 @@ fn maybe_record_as_seed<'tcx>(
&& let Some(comes_from_allow) =
has_allow_dead_code_or_lang_attr(tcx, trait_item_local_def_id)
{
worklist.push((owner_id.def_id, comes_from_allow));
worklist.push(WorkItem {
id: owner_id.def_id,
propagated: comes_from_allow,
own: comes_from_allow,
});
}
// We only care about associated items of traits,
@@ -804,7 +879,11 @@ fn maybe_record_as_seed<'tcx>(
&& let Some(comes_from_allow) =
has_allow_dead_code_or_lang_attr(tcx, trait_def_id)
{
worklist.push((owner_id.def_id, comes_from_allow));
worklist.push(WorkItem {
id: owner_id.def_id,
propagated: comes_from_allow,
own: comes_from_allow,
});
}
unsolved_items.push(owner_id.def_id);
@@ -812,38 +891,48 @@ fn maybe_record_as_seed<'tcx>(
}
DefKind::GlobalAsm => {
// global_asm! is always live.
worklist.push((owner_id.def_id, ComesFromAllowExpect::No));
worklist.push(WorkItem {
id: owner_id.def_id,
propagated: ComesFromAllowExpect::No,
own: ComesFromAllowExpect::No,
});
}
DefKind::Const { .. } => {
if tcx.item_name(owner_id.def_id) == kw::Underscore {
// `const _` is always live, as that syntax only exists for the side effects
// of type checking and evaluating the constant expression, and marking them
// as dead code would defeat that purpose.
worklist.push((owner_id.def_id, ComesFromAllowExpect::No));
worklist.push(WorkItem {
id: owner_id.def_id,
propagated: ComesFromAllowExpect::No,
own: ComesFromAllowExpect::No,
});
}
}
_ => {}
}
}
fn create_and_seed_worklist(
tcx: TyCtxt<'_>,
) -> (Vec<(LocalDefId, ComesFromAllowExpect)>, Vec<LocalDefId>) {
fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> (Vec<WorkItem>, Vec<LocalDefId>) {
let effective_visibilities = &tcx.effective_visibilities(());
let mut unsolved_impl_item = Vec::new();
let mut worklist = effective_visibilities
.iter()
.filter_map(|(&id, effective_vis)| {
effective_vis
.is_public_at_level(Level::Reachable)
.then_some(id)
.map(|id| (id, ComesFromAllowExpect::No))
effective_vis.is_public_at_level(Level::Reachable).then_some(id).map(|id| WorkItem {
id,
propagated: ComesFromAllowExpect::No,
own: ComesFromAllowExpect::No,
})
})
// Seed entry point
.chain(
tcx.entry_fn(())
.and_then(|(def_id, _)| def_id.as_local().map(|id| (id, ComesFromAllowExpect::No))),
)
.chain(tcx.entry_fn(()).and_then(|(def_id, _)| {
def_id.as_local().map(|id| WorkItem {
id,
propagated: ComesFromAllowExpect::No,
own: ComesFromAllowExpect::No,
})
}))
.collect::<Vec<_>>();
let crate_items = tcx.hir_crate_items(());
@@ -870,6 +959,7 @@ fn live_symbols_and_ignored_derived_traits(
in_pat: false,
ignore_variant_stack: vec![],
ignored_derived_traits: Default::default(),
propagated_comes_from_allow_expect: ComesFromAllowExpect::No,
};
if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() {
return Err(guar);
@@ -884,9 +974,11 @@ fn live_symbols_and_ignored_derived_traits(
.collect();
while !items_to_check.is_empty() {
symbol_visitor
.worklist
.extend(items_to_check.drain(..).map(|id| (id, ComesFromAllowExpect::No)));
symbol_visitor.worklist.extend(items_to_check.drain(..).map(|id| WorkItem {
id,
propagated: ComesFromAllowExpect::No,
own: ComesFromAllowExpect::No,
}));
if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() {
return Err(guar);
}
-4
View File
@@ -13,10 +13,6 @@
use crate::check_attr::ProcMacroKind;
use crate::lang_items::Duplicate;
#[derive(Diagnostic)]
#[diag("`#[diagnostic::do_not_recommend]` can only be placed on trait implementations")]
pub(crate) struct IncorrectDoNotRecommendLocation;
#[derive(Diagnostic)]
#[diag("`#[loop_match]` should be applied to a loop")]
pub(crate) struct LoopMatchAttr {
@@ -1043,17 +1043,25 @@ pub(super) fn suggest_fn_call(
&& let ty::Tuple(inputs) = *sig.tupled_inputs_ty.kind()
&& inputs.is_empty()
&& self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Future)
&& let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()
&& let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) =
self.tcx.hir_node(*arg_hir_id)
&& let Some(hir::Node::Expr(hir::Expr {
kind:
hir::ExprKind::Closure(hir::Closure {
kind: hir::ClosureKind::CoroutineClosure(CoroutineDesugaring::Async),
fn_arg_span: Some(arg_span),
..
}),
..
kind: hir::ExprKind::Closure(closure), ..
})) = self.tcx.hir_get_if_local(def_id)
&& obligation.cause.span.contains(*arg_span)
&& let hir::ClosureKind::CoroutineClosure(CoroutineDesugaring::Async) = closure.kind
&& let Some(arg_span) = closure.fn_arg_span
&& obligation.cause.span.contains(arg_span)
{
let mut body = self.tcx.hir_body(closure.body).value;
let peeled = body.peel_blocks().peel_drop_temps();
if let hir::ExprKind::Closure(inner) = peeled.kind {
body = self.tcx.hir_body(inner.body).value;
}
if !matches!(body.peel_blocks().peel_drop_temps().kind, hir::ExprKind::Block(..)) {
return false;
}
let sm = self.tcx.sess.source_map();
let removal_span = if let Ok(snippet) =
sm.span_to_snippet(arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1)))
@@ -1062,7 +1070,7 @@ pub(super) fn suggest_fn_call(
// There's a space after `||`, include it in the removal
arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1))
} else {
*arg_span
arg_span
};
err.span_suggestion_verbose(
removal_span,
@@ -1102,23 +1110,63 @@ pub(super) fn suggest_fn_call(
.collect::<Vec<_>>()
.join(", ");
if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArg { .. })
if let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()
&& obligation.cause.span.can_be_used_for_suggestions()
{
let (span, sugg) = if let Some(snippet) =
self.tcx.sess.source_map().span_to_snippet(obligation.cause.span).ok()
&& snippet.starts_with("|")
{
(obligation.cause.span, format!("({snippet})({args})"))
} else {
(obligation.cause.span.shrink_to_hi(), format!("({args})"))
let span = obligation.cause.span;
let arg_expr = match self.tcx.hir_node(*arg_hir_id) {
hir::Node::Expr(expr) => Some(expr),
_ => None,
};
// When the obligation error has been ensured to have been caused by
// an argument, the `obligation.cause.span` points at the expression
// of the argument, so we can provide a suggestion. Otherwise, we give
// a more general note.
err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
let is_closure_expr =
arg_expr.is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Closure(..)));
// If the user wrote `|| {}()`, suggesting to call the closure would produce `(|| {}())()`,
// which doesn't help and is often outright wrong.
if args.is_empty()
&& let Some(expr) = arg_expr
&& let hir::ExprKind::Closure(closure) = expr.kind
{
let mut body = self.tcx.hir_body(closure.body).value;
// Async closures desugar to a closure returning a coroutine
if let hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) =
closure.kind
{
let peeled = body.peel_blocks().peel_drop_temps();
if let hir::ExprKind::Closure(inner) = peeled.kind {
body = self.tcx.hir_body(inner.body).value;
}
}
let peeled_body = body.peel_blocks().peel_drop_temps();
if let hir::ExprKind::Call(callee, call_args) = peeled_body.kind
&& call_args.is_empty()
&& let hir::ExprKind::Block(..) = callee.peel_blocks().peel_drop_temps().kind
{
return false;
}
}
if is_closure_expr {
err.multipart_suggestions(
msg,
vec![vec![
(span.shrink_to_lo(), "(".to_string()),
(span.shrink_to_hi(), format!(")({args})")),
]],
Applicability::HasPlaceholders,
);
} else {
err.span_suggestion_verbose(
span.shrink_to_hi(),
msg,
format!("({args})"),
Applicability::HasPlaceholders,
);
}
} else if let DefIdOrName::DefId(def_id) = def_id_or_name {
let name = match self.tcx.hir_get_if_local(def_id) {
Some(hir::Node::Expr(hir::Expr {
+15
View File
@@ -1588,6 +1588,21 @@ Tests on various well-formedness checks, e.g. [Type-checking normal functions](h
Tests on `where` clauses. See [Where clauses | Reference](https://doc.rust-lang.org/reference/items/generics.html#where-clauses).
## `tests/ui/whitespace/`
Tests for whitespace handling in the Rust lexer. The Rust language
defines whitespace as Unicode Pattern_White_Space, which is not the
same as what the standard library gives you:
- `is_ascii_whitespace` follows the WhatWG Infra Standard and skips
vertical tab (`\x0B`)
- `is_whitespace` matches Unicode White_Space, which is a broader set
These tests make that gap visible and check that the lexer accepts
all 11 Pattern_White_Space characters correctly.
See: https://github.com/rustfoundation/interop-initiative/issues/53
## `tests/ui/windows-subsystem/`: `#![windows_subsystem = ""]`
See [the `windows_subsystem` attribute](https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute).
@@ -66,8 +66,11 @@ LL | fn takes_future(_fut: impl Future<Output = ()>) {}
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future`
help: use parentheses to call this closure
|
LL | }(/* i32 */));
| +++++++++++
LL ~ takes_future((async |x: i32| {
LL |
LL | println!("{x}");
LL ~ })(/* i32 */));
|
error: aborting due to 3 previous errors
@@ -32,9 +32,8 @@ LL | fn check(_: impl std::marker::ConstParamTy_) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check`
help: use parentheses to call this closure
|
LL - check(|| {});
LL + check((|| {})());
|
LL | check((|| {})());
| + +++
error[E0277]: `fn()` can't be used as a const parameter type
--> $DIR/const_param_ty_bad.rs:9:11
@@ -3,6 +3,9 @@ warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implement
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | const CONST: () = ();
| --------------------- not a trait implementation
|
= note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
@@ -11,48 +14,72 @@ warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implement
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | static STATIC: () = ();
| ----------------------- not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:15:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | type Type = ();
| --------------- not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:19:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | enum Enum {}
| ------------ not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:23:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | impl Enum {}
| ------------ not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:27:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | extern "C" {}
| ------------- not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:31:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | fn fun() {}
| ----------- not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:35:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | struct Struct {}
| ---------------- not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:39:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | trait Trait {}
| -------------- not a trait implementation
warning: 9 warnings emitted
@@ -3,6 +3,9 @@ warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implement
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | const CONST: () = ();
| --------------------- not a trait implementation
|
= note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
@@ -11,48 +14,72 @@ warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implement
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | static STATIC: () = ();
| ----------------------- not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:15:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | type Type = ();
| --------------- not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:19:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | enum Enum {}
| ------------ not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:23:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | impl Enum {}
| ------------ not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:27:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | extern "C" {}
| ------------- not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:31:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | fn fun() {}
| ----------- not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:35:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | struct Struct {}
| ---------------- not a trait implementation
warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
--> $DIR/incorrect-locations.rs:39:1
|
LL | #[diagnostic::do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | trait Trait {}
| -------------- not a trait implementation
warning: 9 warnings emitted
@@ -2,11 +2,11 @@
#![deny(misplaced_diagnostic_attributes)]
#[diagnostic::on_const(message = "tadaa", note = "boing")]
//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls
//~^ ERROR: `#[diagnostic::on_const]` can only be applied to non-const trait implementations
pub struct Foo;
#[diagnostic::on_const(message = "tadaa", note = "boing")]
//~^ ERROR: `#[diagnostic::on_const]` can only be applied to non-const trait impls
//~^ ERROR: `#[diagnostic::on_const]` can only be applied to non-const trait implementations
impl const PartialEq for Foo {
fn eq(&self, _other: &Foo) -> bool {
true
@@ -14,7 +14,7 @@ fn eq(&self, _other: &Foo) -> bool {
}
#[diagnostic::on_const(message = "tadaa", note = "boing")]
//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls
//~^ ERROR: `#[diagnostic::on_const]` can only be applied to non-const trait implementations
impl Foo {
fn eq(&self, _other: &Foo) -> bool {
true
@@ -23,7 +23,7 @@ fn eq(&self, _other: &Foo) -> bool {
impl PartialOrd for Foo {
#[diagnostic::on_const(message = "tadaa", note = "boing")]
//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls
//~^ ERROR: `#[diagnostic::on_const]` can only be applied to non-const trait implementations
fn partial_cmp(&self, other: &Foo) -> Option<std::cmp::Ordering> {
None
}
@@ -1,11 +1,11 @@
error: `#[diagnostic::on_const]` can only be applied to trait impls
--> $DIR/misplaced_attr.rs:4:1
error: `#[diagnostic::on_const]` can only be applied to non-const trait implementations
--> $DIR/misplaced_attr.rs:8:1
|
LL | #[diagnostic::on_const(message = "tadaa", note = "boing")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | pub struct Foo;
| -------------- not a trait impl
LL | impl const PartialEq for Foo {
| ---------------------------- this is a const trait implementation
|
note: the lint level is defined here
--> $DIR/misplaced_attr.rs:2:9
@@ -13,32 +13,38 @@ note: the lint level is defined here
LL | #![deny(misplaced_diagnostic_attributes)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `#[diagnostic::on_const]` can only be applied to non-const trait impls
--> $DIR/misplaced_attr.rs:8:1
error: `#[diagnostic::on_const]` can only be applied to non-const trait implementations
--> $DIR/misplaced_attr.rs:4:1
|
LL | #[diagnostic::on_const(message = "tadaa", note = "boing")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | impl const PartialEq for Foo {
| ---------------------------- this is a const trait impl
LL | pub struct Foo;
| --------------- not a trait implementation
error: `#[diagnostic::on_const]` can only be applied to trait impls
error: `#[diagnostic::on_const]` can only be applied to non-const trait implementations
--> $DIR/misplaced_attr.rs:16:1
|
LL | #[diagnostic::on_const(message = "tadaa", note = "boing")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[diagnostic::on_const(message = "tadaa", note = "boing")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | impl Foo {
| -------- not a trait impl
LL | / impl Foo {
LL | | fn eq(&self, _other: &Foo) -> bool {
LL | | true
LL | | }
LL | | }
| |_- not a trait implementation
error: `#[diagnostic::on_const]` can only be applied to trait impls
error: `#[diagnostic::on_const]` can only be applied to non-const trait implementations
--> $DIR/misplaced_attr.rs:25:5
|
LL | #[diagnostic::on_const(message = "tadaa", note = "boing")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[diagnostic::on_const(message = "tadaa", note = "boing")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | fn partial_cmp(&self, other: &Foo) -> Option<std::cmp::Ordering> {
| ---------------------------------------------------------------- not a trait impl
LL | / fn partial_cmp(&self, other: &Foo) -> Option<std::cmp::Ordering> {
LL | | None
LL | | }
| |_____- not a trait implementation
error: aborting due to 4 previous errors
@@ -1,11 +1,3 @@
warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:22:1
|
LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: there is no parameter `from_desugaring` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:17
|
@@ -152,6 +144,14 @@ LL | #[diagnostic::on_unimplemented = "Message"]
|
= help: only `message`, `note` and `label` are allowed as options
warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:22:1
|
LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
error[E0277]: trait has `()` and `i32` as params
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:53:15
|
@@ -1,11 +1,3 @@
warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:7:1
|
LL | #[diagnostic::on_unimplemented(message = "Baz")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: there is no parameter `DoesNotExist` on trait `Test`
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:44
|
@@ -24,6 +16,14 @@ LL | #[diagnostic::on_unimplemented(unsupported = "foo")]
= help: only `message`, `note` and `label` are allowed as options
= note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:7:1
|
LL | #[diagnostic::on_unimplemented(message = "Baz")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: malformed `diagnostic::on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:11:50
|
@@ -5,7 +5,7 @@ LL | #[diagnostic::on_unknown(message = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | extern crate std as other_std;
| ----------------------------- not an import
| ------------------------------ not an import
|
= note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
@@ -16,7 +16,7 @@ LL | #[diagnostic::on_unknown(message = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | const CONST: () = ();
| --------------- not an import
| --------------------- not an import
warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements
--> $DIR/incorrect-locations.rs:13:1
@@ -25,7 +25,7 @@ LL | #[diagnostic::on_unknown(message = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | static STATIC: () = ();
| ----------------- not an import
| ----------------------- not an import
warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements
--> $DIR/incorrect-locations.rs:17:1
@@ -34,7 +34,7 @@ LL | #[diagnostic::on_unknown(message = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | type Type = ();
| --------- not an import
| --------------- not an import
warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements
--> $DIR/incorrect-locations.rs:21:1
@@ -43,7 +43,7 @@ LL | #[diagnostic::on_unknown(message = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | enum Enum {}
| --------- not an import
| ------------ not an import
warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements
--> $DIR/incorrect-locations.rs:25:1
@@ -52,7 +52,7 @@ LL | #[diagnostic::on_unknown(message = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | impl Enum {}
| --------- not an import
| ------------ not an import
warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements
--> $DIR/incorrect-locations.rs:29:1
@@ -70,7 +70,7 @@ LL | #[diagnostic::on_unknown(message = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | fn fun() {}
| -------- not an import
| ----------- not an import
warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements
--> $DIR/incorrect-locations.rs:37:1
@@ -79,7 +79,7 @@ LL | #[diagnostic::on_unknown(message = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | struct Struct {}
| ------------- not an import
| ---------------- not an import
warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements
--> $DIR/incorrect-locations.rs:41:1
@@ -88,7 +88,7 @@ LL | #[diagnostic::on_unknown(message = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | trait Trait {}
| ----------- not an import
| -------------- not an import
warning: `#[diagnostic::on_unknown]` can only be applied to `use` statements
--> $DIR/incorrect-locations.rs:45:1
@@ -97,7 +97,7 @@ LL | #[diagnostic::on_unknown(message = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | impl Trait for i32 {}
| ------------------ not an import
| --------------------- not an import
warning: 11 warnings emitted
@@ -0,0 +1,13 @@
//@ check-pass
#[expect(unused)]
trait UnusedTrait {}
struct UsedStruct(u32);
impl UnusedTrait for UsedStruct {}
fn main() {
let x = UsedStruct(12);
println!("Hello World! {}", x.0);
}
@@ -0,0 +1,9 @@
//@ check-pass
#![deny(dead_code, unfulfilled_lint_expectations, reason = "example")]
#![expect(dead_code, reason = "example")]
struct Foo;
impl Foo {}
fn main() {}
@@ -0,0 +1,19 @@
//@ check-pass
#![deny(unfulfilled_lint_expectations)]
#![warn(dead_code)]
struct Foo {
#[expect(dead_code)]
value: usize,
}
#[expect(dead_code)]
fn dead_reads_field() {
let foo = Foo { value: 0 };
let _ = foo.value;
}
fn main() {
let _ = Foo { value: 0 };
}
@@ -0,0 +1,18 @@
//@ check-pass
#![deny(unfulfilled_lint_expectations)]
#![warn(dead_code)]
#[expect(dead_code)]
fn root() {
middle();
}
fn middle() {
leaf();
}
#[expect(dead_code)]
fn leaf() {}
fn main() {}
@@ -0,0 +1,15 @@
// Regression test for #150701
//@ run-rustfix
//@ edition: 2024
use std::future::Future;
fn f(_c: impl Future<Output = ()>) {}
fn main() {
f((async || {})()); //~ ERROR: expected function, found `()`
//~^ ERROR: is not a future
f(async {});
//~^ ERROR: is not a future
}
@@ -0,0 +1,15 @@
// Regression test for #150701
//@ run-rustfix
//@ edition: 2024
use std::future::Future;
fn f(_c: impl Future<Output = ()>) {}
fn main() {
f(async || {}()); //~ ERROR: expected function, found `()`
//~^ ERROR: is not a future
f(async || {});
//~^ ERROR: is not a future
}
@@ -0,0 +1,52 @@
error[E0618]: expected function, found `()`
--> $DIR/suggest-create-closure-issue-150701.rs:11:16
|
LL | f(async || {}());
| ^^--
| |
| call expression requires function
|
help: if you meant to create this closure and immediately call it, surround the closure with parentheses
|
LL | f((async || {})());
| + +
error[E0277]: `{async closure@$DIR/suggest-create-closure-issue-150701.rs:11:7: 11:15}` is not a future
--> $DIR/suggest-create-closure-issue-150701.rs:11:7
|
LL | f(async || {}());
| - ^^^^^^^^^^^^^ `{async closure@$DIR/suggest-create-closure-issue-150701.rs:11:7: 11:15}` is not a future
| |
| required by a bound introduced by this call
|
= help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-create-closure-issue-150701.rs:11:7: 11:15}`
note: required by a bound in `f`
--> $DIR/suggest-create-closure-issue-150701.rs:8:15
|
LL | fn f(_c: impl Future<Output = ()>) {}
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `f`
error[E0277]: `{async closure@$DIR/suggest-create-closure-issue-150701.rs:13:7: 13:15}` is not a future
--> $DIR/suggest-create-closure-issue-150701.rs:13:7
|
LL | f(async || {});
| - ^^^^^^^^^^^ `{async closure@$DIR/suggest-create-closure-issue-150701.rs:13:7: 13:15}` is not a future
| |
| required by a bound introduced by this call
|
= help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-create-closure-issue-150701.rs:13:7: 13:15}`
note: required by a bound in `f`
--> $DIR/suggest-create-closure-issue-150701.rs:8:15
|
LL | fn f(_c: impl Future<Output = ()>) {}
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `f`
help: use `async {}` instead of `async || {}` to introduce an async block
|
LL - f(async || {});
LL + f(async {});
|
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0277, E0618.
For more information about an error, try `rustc --explain E0277`.
@@ -14,9 +14,8 @@ LL | fn call(&self, _: impl Display) {}
| ^^^^^^^ required by this bound in `S::call`
help: use parentheses to call this closure
|
LL - S.call(|| "hello");
LL + S.call((|| "hello")());
|
LL | S.call((|| "hello")());
| + +++
error: aborting due to 1 previous error
@@ -0,0 +1,22 @@
//@ run-pass
// This test checks that split_ascii_whitespace does NOT split on
// vertical tab (\x0B), because the standard library uses the WhatWG
// Infra Standard definition of ASCII whitespace, which excludes
// vertical tab.
//
// See: https://github.com/rust-lang/rust-project-goals/issues/53
fn main() {
let s = "a\x0Bb";
let parts: Vec<&str> = s.split_ascii_whitespace().collect();
assert_eq!(parts.len(), 1,
"vertical tab should not be treated as ASCII whitespace");
let s2 = "a b";
let parts2: Vec<&str> = s2.split_ascii_whitespace().collect();
assert_eq!(parts2.len(), 2,
"regular space should split correctly");
}
+13
View File
@@ -0,0 +1,13 @@
// This test ensures that the Rust lexer rejects invalid whitespace
// characters such as ZERO WIDTH SPACE.
//@ check-fail
fn main() {
let x = 5;
let y = 10;
let a=x + y;
//~^ ERROR unknown start of token
//~| HELP invisible characters like
}
@@ -0,0 +1,10 @@
error: unknown start of token: \u{200b}
--> $DIR/invalid_whitespace.rs:10:11
|
LL | let a=x + y;
| ^
|
= help: invisible characters like '\u{200b}' are not usually visible in text editors
error: aborting due to 1 previous error
+58
View File
@@ -0,0 +1,58 @@
//@ run-pass
// ignore-tidy-tab
//
// Tests that the Rust lexer accepts Unicode Pattern_White_Space characters.
//
// Worth noting: the Rust reference defines whitespace as Pattern_White_Space,
// which is not the same as what is_ascii_whitespace or is_whitespace give you.
//
// is_ascii_whitespace follows WhatWG and skips vertical tab (\x0B).
// is_whitespace uses Unicode White_Space, which is a broader set.
//
// The 11 characters that actually count as whitespace in Rust source:
// \x09 \x0A \x0B \x0C \x0D \x20 \u{85} \u{200E} \u{200F} \u{2028} \u{2029}
//
// Ref: https://github.com/rustfoundation/interop-initiative/issues/53
#[rustfmt::skip]
fn main() {
// tab (\x09) between let and the name
let _ws1 = 1_i32;
// vertical tab (\x0B) between let and the name
// this is the one is_ascii_whitespace gets wrong
let _ws2 = 2_i32;
// form feed (\x0C) between let and the name
let _ws3 = 3_i32;
// plain space (\x20), here just so every character is represented
let _ws4 = 4_i32;
// NEL (\u{85}) between let and the name
let…_ws5 = 5_i32;
// left-to-right mark (\u{200E}) between let and the name
let_ws6 = 6_i32;
// right-to-left mark (\u{200F}) between let and the name
let_ws7 = 7_i32;
// \x0A, \x0D, \u{2028}, \u{2029} are also Pattern_White_Space but they
// act as line endings, so you can't stick them in the middle of a statement.
// The lexer still handles them correctly at line boundaries.
// These are Unicode White_Space but NOT Pattern_White_Space:
// \u{A0} no-break space \u{1680} ogham space mark
// \u{2000} en quad \u{2001} em quad
// \u{2002} en space \u{2003} em space
// \u{2004} three-per-em space \u{2005} four-per-em space
// \u{2006} six-per-em space \u{2007} figure space
// \u{2008} punctuation space \u{2009} thin space
// \u{200A} hair space \u{202F} narrow no-break space
// \u{205F} medium math space \u{3000} ideographic space
// add them up so the compiler doesn't complain about unused variables
let _sum = _ws1 + _ws2 + _ws3 + _ws4 + _ws5 + _ws6 + _ws7;
println!("{}", _sum);
}