mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
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:
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+8
-8
@@ -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
|
||||
|
|
||||
|
||||
+8
-8
@@ -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");
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user