Skip to content

Commit cf4ff0e

Browse files
committed
Resolve paths in diagnostic::on_unimplemented
1 parent f732632 commit cf4ff0e

14 files changed

Lines changed: 435 additions & 97 deletions

File tree

compiler/rustc_ast/src/ast.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3796,7 +3796,6 @@ pub struct Trait {
37963796
pub bounds: GenericBounds,
37973797
#[visitable(extra = AssocCtxt::Trait)]
37983798
pub items: ThinVec<Box<AssocItem>>,
3799-
#[visitable(ignore)]
38003799
pub on_unimplemented: Option<Directive>,
38013800
}
38023801

compiler/rustc_ast/src/attr/diagnostic.rs

Lines changed: 131 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
//! Contains the data structures used by the diagnostic attribute family.
2-
use std::fmt;
3-
use std::fmt::Debug;
2+
use std::fmt::{self, Debug};
43

5-
use rustc_macros::{Decodable, Encodable, StableHash};
4+
use rustc_macros::{Decodable, Encodable, StableHash, Walkable};
5+
use rustc_span::def_id::DefId;
66
use rustc_span::{DesugaringKind, Span, Symbol, kw};
77
use thin_vec::ThinVec;
88
use tracing::debug;
99

10-
#[derive(Clone, Default, Debug, StableHash, Encodable, Decodable)]
10+
use crate::{NodeId, Path};
11+
12+
#[derive(Clone, Default, Debug, StableHash, Encodable, Decodable, Walkable)]
1113
pub struct Directive {
1214
pub is_rustc_attr: bool,
1315
/// This is never nested more than once, i.e. the directives in this
1416
/// thinvec have no filters of their own.
1517
pub filters: ThinVec<(Filter, Directive)>,
18+
#[visitable(ignore)]
1619
pub message: Option<(Span, FormatString)>,
20+
#[visitable(ignore)]
1721
pub label: Option<(Span, FormatString)>,
22+
#[visitable(ignore)]
1823
pub notes: ThinVec<FormatString>,
24+
#[visitable(ignore)]
1925
pub parent_label: Option<FormatString>,
2026
}
2127

@@ -47,6 +53,16 @@ impl Directive {
4753
}
4854
}
4955

56+
pub fn resolve_predicates(&mut self, resolve: &mut impl FnMut(NodeId, &Path) -> Option<DefId>) {
57+
for (Filter { pred, .. }, nested_dir) in &mut self.filters {
58+
debug_assert!(
59+
nested_dir.filters.is_empty(),
60+
"can't have filters beyond the root directive"
61+
);
62+
pred.resolve_predicates(resolve);
63+
}
64+
}
65+
5066
pub fn eval(
5167
&self,
5268
filter_options: Option<&FilterOptions>,
@@ -234,19 +250,31 @@ pub enum FormatArg {
234250
}
235251

236252
/// Represents the `on` filter in `#[rustc_on_unimplemented]`.
237-
#[derive(Clone, Debug, StableHash, Encodable, Decodable)]
253+
#[derive(Clone, Debug, StableHash, Encodable, Decodable, Walkable)]
238254
pub struct Filter {
239255
pub span: Span,
240256
pub pred: Predicate,
241257
}
258+
242259
impl Filter {
243260
pub fn matches_predicate(&self, options: &FilterOptions) -> bool {
244261
self.pred.eval(&mut |p| match p {
245262
FlagOrNv::Flag(b) => options.has_flag(*b),
246-
FlagOrNv::NameValue(NameValue { name, value }) => {
263+
FlagOrNv::NameValue(NameValue::String { name, value }) => {
247264
let value = value.format(&options.generic_args);
248265
options.contains(*name, value)
249266
}
267+
FlagOrNv::NameValue(NameValue::Path { .. }) => {
268+
unreachable!("should have been removed during ast lowering")
269+
}
270+
FlagOrNv::NameValue(NameValue::DefId { name, def_id }) => {
271+
if let Some(def_id) = def_id {
272+
options.contains_defid(*name, *def_id)
273+
} else {
274+
// we've already errored during ast lowering
275+
false
276+
}
277+
}
250278
})
251279
}
252280

@@ -259,10 +287,10 @@ impl Filter {
259287
///
260288
/// It is similar to the predicate in the `cfg` attribute,
261289
/// and may contain nested predicates.
262-
#[derive(Clone, Debug, StableHash, Encodable, Decodable)]
290+
#[derive(Clone, Debug, StableHash, Encodable, Decodable, Walkable)]
263291
pub enum Predicate {
264292
/// A condition like `on(crate_local)`.
265-
Flag(Flag),
293+
Flag(#[visitable(ignore)] Flag),
266294
/// A match, like `on(Rhs = "Whatever")`.
267295
Match(NameValue),
268296
/// Negation, like `on(not($pred))`.
@@ -294,6 +322,41 @@ impl Predicate {
294322
}
295323
}
296324
}
325+
326+
pub fn visit_predicates(&self, visit: &mut impl FnMut(NodeId, &Path)) {
327+
match self {
328+
Predicate::Flag(_) | Predicate::Match(NameValue::String { .. }) => {}
329+
Predicate::Match(NameValue::Path { name: _, value, id }) => {
330+
visit(*id, value);
331+
}
332+
Predicate::Match(NameValue::DefId { .. }) => {
333+
unreachable!()
334+
}
335+
Predicate::Not(not) => not.visit_predicates(visit),
336+
Predicate::All(preds) | Predicate::Any(preds) => {
337+
preds.iter().for_each(|pred| pred.visit_predicates(visit))
338+
}
339+
}
340+
}
341+
342+
pub fn resolve_predicates(&mut self, resolve: &mut impl FnMut(NodeId, &Path) -> Option<DefId>) {
343+
match self {
344+
Predicate::Flag(_) | Predicate::Match(NameValue::String { .. }) => {}
345+
Predicate::Match(NameValue::Path { name, value, id }) => {
346+
let def_id = resolve(*id, value);
347+
*self = Predicate::Match(NameValue::DefId { name: *name, def_id });
348+
}
349+
Predicate::Match(NameValue::DefId { .. }) => {
350+
if cfg!(debug_assertions) {
351+
unreachable!("predicate was resolved twice")
352+
}
353+
}
354+
Predicate::Not(not) => not.resolve_predicates(resolve),
355+
Predicate::All(preds) | Predicate::Any(preds) => {
356+
preds.iter_mut().for_each(|pred| pred.resolve_predicates(resolve))
357+
}
358+
}
359+
}
297360
}
298361

299362
/// Represents a `MetaWord` in an `on`-filter.
@@ -312,21 +375,39 @@ pub enum Flag {
312375
/// A `MetaNameValueStr` in an `on`-filter.
313376
///
314377
/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`.
315-
#[derive(Clone, Debug, StableHash, Encodable, Decodable)]
316-
pub struct NameValue {
317-
pub name: Name,
378+
#[derive(Clone, Debug, StableHash, Encodable, Decodable, Walkable)]
379+
pub enum NameValue {
318380
/// Something like `"&str"` or `"alloc::string::String"`,
319381
/// in which case it just contains a single string piece.
320382
/// But if it is something like `"&[{A}]"` then it must be formatted later.
321-
pub value: FilterFormatString,
383+
String {
384+
#[visitable(ignore)]
385+
name: Name,
386+
#[visitable(ignore)]
387+
value: FilterFormatString,
388+
},
389+
Path {
390+
#[visitable(ignore)]
391+
name: Name,
392+
id: NodeId,
393+
value: Path,
394+
},
395+
DefId {
396+
#[visitable(ignore)]
397+
name: Name,
398+
#[visitable(ignore)]
399+
def_id: Option<DefId>,
400+
},
322401
}
323402

324403
impl NameValue {
325404
pub fn visit_params(&self, span: Span, visit: &mut impl FnMut(Symbol, Span)) {
326-
if let Name::GenericArg(arg) = self.name {
327-
visit(arg, span);
405+
if let NameValue::String { name, value } = self {
406+
if let Name::GenericArg(arg) = name {
407+
visit(*arg, span);
408+
}
409+
value.visit_params(span, visit);
328410
}
329-
self.value.visit_params(span, visit);
330411
}
331412
}
332413

@@ -357,14 +438,14 @@ pub struct FilterFormatString {
357438
}
358439

359440
impl FilterFormatString {
360-
fn format(&self, generic_args: &[(Symbol, String)]) -> String {
441+
fn format(&self, generic_args: &[(Symbol, String, Option<DefId>)]) -> String {
361442
let mut ret = String::new();
362443

363444
for piece in &self.pieces {
364445
match piece {
365446
LitOrArg::Lit(s) => ret.push_str(s.as_str()),
366-
LitOrArg::Arg(s) => match generic_args.iter().find(|(k, _)| k == s) {
367-
Some((_, val)) => ret.push_str(val),
447+
LitOrArg::Arg(s) => match generic_args.iter().find(|(k, _, _)| k == s) {
448+
Some((_, val, _)) => ret.push_str(val),
368449
None => {
369450
let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}"));
370451
}
@@ -416,21 +497,21 @@ pub enum LitOrArg {
416497
///
417498
/// ```rust,ignore (just an example)
418499
/// FilterOptions {
419-
/// self_types: ["u32", "{integral}"],
500+
/// self_types: [("u32", None), ("{integral}", None), ("Type", Some(<defid>))],
420501
/// from_desugaring: Some("QuestionMark"),
421502
/// cause: None,
422503
/// crate_local: false,
423504
/// direct: true,
424-
/// generic_args: [("Self","u32"),
425-
/// ("R", "core::option::Option<core::convert::Infallible>"),
426-
/// ("R", "core::option::Option<T>" ),
505+
/// generic_args: [("Self","u32", <defid>),
506+
/// ("R", "core::option::Option<core::convert::Infallible>", Some(<defid>)),
507+
/// ("R", "core::option::Option<T>", Some(<defid>)),
427508
/// ],
428509
/// }
429510
/// ```
430511
#[derive(Debug)]
431512
pub struct FilterOptions {
432513
/// All the self types that may apply.
433-
pub self_types: Vec<String>,
514+
pub self_types: Vec<(String, Option<DefId>)>,
434515
// The kind of compiler desugaring.
435516
pub from_desugaring: Option<DesugaringKind>,
436517
/// Match on a variant of rustc_infer's `ObligationCauseCode`.
@@ -439,23 +520,45 @@ pub struct FilterOptions {
439520
/// Is the obligation "directly" user-specified, rather than derived?
440521
pub direct: bool,
441522
// A list of the generic arguments and their reified types.
442-
pub generic_args: Vec<(Symbol, String)>,
523+
pub generic_args: Vec<(Symbol, String, Option<DefId>)>,
443524
}
444525

445526
impl FilterOptions {
446-
pub fn has_flag(&self, name: Flag) -> bool {
527+
fn has_flag(&self, name: Flag) -> bool {
447528
match name {
448529
Flag::CrateLocal => self.crate_local,
449530
Flag::Direct => self.direct,
450531
Flag::FromDesugaring => self.from_desugaring.is_some(),
451532
}
452533
}
453-
pub fn contains(&self, name: Name, value: String) -> bool {
534+
fn contains(&self, name: Name, value: String) -> bool {
454535
match name {
455-
Name::SelfUpper => self.self_types.contains(&value),
536+
Name::SelfUpper => {
537+
self.self_types.iter().any(|(type_name, _this_def_id)| *type_name == value)
538+
}
456539
Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)),
457540
Name::Cause => self.cause == Some(value),
458-
Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)),
541+
Name::GenericArg(generic) => self
542+
.generic_args
543+
.iter()
544+
.any(|(this_name, typename, _)| *this_name == generic && *typename == value),
545+
}
546+
}
547+
548+
fn contains_defid(&self, name: Name, def_id: DefId) -> bool {
549+
match name {
550+
Name::SelfUpper => {
551+
self.self_types.iter().any(|(_type_name, this_def_id)| *this_def_id == Some(def_id))
552+
}
553+
554+
Name::GenericArg(generic) => {
555+
self.generic_args.iter().any(|(this_name, _type_name, this_def_id)| {
556+
*this_name == generic && *this_def_id == Some(def_id)
557+
})
558+
}
559+
Name::FromDesugaring | Name::Cause => {
560+
unreachable!("these can only refer to strings and are never resolved")
561+
}
459562
}
460563
}
461564
}

compiler/rustc_ast/src/mut_visit.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use smallvec::{SmallVec, smallvec};
1616
use thin_vec::ThinVec;
1717

1818
use crate::ast::*;
19+
use crate::attr::diagnostic::{Directive, Filter, NameValue, Predicate};
1920
use crate::tokenstream::*;
2021
use crate::visit::{AssocCtxt, BoundKind, FnCtxt, LifetimeCtxt, VisitorResult, try_visit};
2122

compiler/rustc_ast/src/visit.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc_span::{Ident, Span, Spanned, Symbol};
1919
use thin_vec::ThinVec;
2020

2121
use crate::ast::*;
22+
use crate::attr::diagnostic::{Directive, Filter, NameValue, Predicate};
2223
use crate::tokenstream::DelimSpan;
2324

2425
#[derive(Copy, Clone, Debug, PartialEq)]
@@ -394,6 +395,8 @@ macro_rules! common_visitor_and_walkers {
394395
ThinVec<Box<Ty>>,
395396
ThinVec<TyPat>,
396397
ThinVec<EiiImpl>,
398+
ThinVec<Predicate>,
399+
ThinVec<(Filter, Directive)>,
397400
);
398401

399402
// This macro generates `impl Visitable` and `impl MutVisitable` that forward to `Walkable`
@@ -494,6 +497,10 @@ macro_rules! common_visitor_and_walkers {
494497
YieldKind,
495498
EiiDecl,
496499
EiiImpl,
500+
Directive,
501+
Filter,
502+
Predicate,
503+
NameValue,
497504
);
498505

499506
/// Each method of this trait is a hook to be potentially

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
use std::convert::identity;
2+
13
use rustc_abi::ExternAbi;
24
use rustc_ast::visit::AssocCtxt;
35
use rustc_ast::*;
46
use rustc_data_structures::fx::FxIndexMap;
57
use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err};
8+
use rustc_hir::attrs::diagnostic::Directive;
69
use rustc_hir::attrs::{AttributeKind, EiiImplResolution};
710
use rustc_hir::def::{DefKind, PerNS, Res};
811
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
912
use rustc_hir::{
10-
self as hir, HirId, ImplItemImplKind, LifetimeSource, PredicateOrigin, Target, find_attr,
13+
self as hir, AttrPath, HirId, ImplItemImplKind, LifetimeSource, PredicateOrigin, Target,
14+
find_attr,
1115
};
1216
use rustc_index::{IndexSlice, IndexVec};
1317
use rustc_middle::span_bug;
@@ -205,6 +209,38 @@ impl<'hir> LoweringContext<'_, 'hir> {
205209
}
206210
}
207211

212+
fn lower_on_unimplemented(&mut self, directive: &Directive) -> Directive {
213+
let mut directive = directive.clone();
214+
directive.resolve_predicates(&mut |id, path| {
215+
let path = AttrPath::from_ast(path, identity);
216+
let res = self.get_partial_res(id);
217+
let Some(res) = res else {
218+
self.dcx().span_err(path.span, format!("cannot find type `{path}`"));
219+
return None;
220+
};
221+
let res = if let Some(res) = res.full_res()
222+
&& !matches!(res, Res::Err)
223+
{
224+
res
225+
} else {
226+
self.dcx().span_err(path.span, format!("cannot find type `{path}`"));
227+
return None;
228+
};
229+
let Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, def_id) = res else {
230+
let article = res.article();
231+
let descr = res.descr();
232+
self.dcx().span_err(
233+
path.span,
234+
format!("`{path}` refers to {article} {descr}, not a struct, enum or union"),
235+
);
236+
return None;
237+
};
238+
239+
Some(def_id)
240+
});
241+
directive
242+
}
243+
208244
fn generate_extra_attrs_for_item_kind(
209245
&mut self,
210246
id: NodeId,
@@ -226,9 +262,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
226262
.map(|decl| vec![hir::Attribute::Parsed(AttributeKind::EiiDeclaration(decl))])
227263
.unwrap_or_default(),
228264
ItemKind::Trait(Trait { on_unimplemented: Some(ou), .. }) => {
229-
// FIXME Lower here
265+
let new_ou = self.lower_on_unimplemented(ou);
230266
vec![hir::Attribute::Parsed(AttributeKind::OnUnimplemented {
231-
directive: Some(Box::new(ou.clone())),
267+
directive: Some(Box::new(new_ou)),
232268
})]
233269
}
234270
ItemKind::ExternCrate(..)

0 commit comments

Comments
 (0)