Add support for embedding pretty printers via the #[debugger_visualizer]
attribute. Add tests for embedding pretty printers and update documentation.
Ensure all error checking for `#[debugger_visualizer]` is done up front and not when the `debugger_visualizer` query is run. Clean up potential ODR violations when embedding pretty printers into the `__rustc_debug_gdb_scripts_section__` section. Respond to PR comments and update documentation.
This commit is contained in:
parent
ee9726cb10
commit
60458b97e7
27 changed files with 455 additions and 200 deletions
|
@ -8,6 +8,7 @@ use rustc_ast::tokenstream::DelimSpan;
|
|||
use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MacArgs, MetaItemKind, NestedMetaItem};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
|
||||
use rustc_expand::base::resolve_path;
|
||||
use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
|
||||
|
@ -1982,49 +1983,64 @@ impl CheckAttrVisitor<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
let hints = match attr.meta_item_list() {
|
||||
Some(meta_item_list) => meta_item_list,
|
||||
None => {
|
||||
self.emit_debugger_visualizer_err(attr);
|
||||
return false;
|
||||
}
|
||||
let Some(hints) = attr.meta_item_list() else {
|
||||
self.emit_debugger_visualizer_err(attr.span);
|
||||
return false;
|
||||
};
|
||||
|
||||
let hint = match hints.len() {
|
||||
1 => &hints[0],
|
||||
_ => {
|
||||
self.emit_debugger_visualizer_err(attr);
|
||||
self.emit_debugger_visualizer_err(attr.span);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
if !hint.has_name(sym::natvis_file) {
|
||||
self.emit_debugger_visualizer_err(attr);
|
||||
let Some(meta_item) = hint.meta_item() else {
|
||||
self.emit_debugger_visualizer_err(attr.span);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let meta_item = match hint.meta_item() {
|
||||
Some(meta_item) => meta_item,
|
||||
None => {
|
||||
self.emit_debugger_visualizer_err(attr);
|
||||
let visualizer_path = match (meta_item.name_or_empty(), meta_item.value_str()) {
|
||||
(sym::natvis_file, Some(value)) => value,
|
||||
(sym::gdb_script_file, Some(value)) => value,
|
||||
(_, _) => {
|
||||
self.emit_debugger_visualizer_err(meta_item.span);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
match (meta_item.name_or_empty(), meta_item.value_str()) {
|
||||
(sym::natvis_file, Some(_)) => true,
|
||||
(_, _) => {
|
||||
self.emit_debugger_visualizer_err(attr);
|
||||
let file =
|
||||
match resolve_path(&self.tcx.sess.parse_sess, visualizer_path.as_str(), attr.span) {
|
||||
Ok(file) => file,
|
||||
Err(mut err) => {
|
||||
err.emit();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
match std::fs::File::open(&file) {
|
||||
Ok(_) => true,
|
||||
Err(err) => {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
meta_item.span,
|
||||
&format!("couldn't read {}: {}", file.display(), err),
|
||||
)
|
||||
.emit();
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_debugger_visualizer_err(&self, attr: &Attribute) {
|
||||
fn emit_debugger_visualizer_err(&self, span: Span) {
|
||||
self.tcx
|
||||
.sess
|
||||
.struct_span_err(attr.span, "invalid argument")
|
||||
.struct_span_err(span, "invalid argument")
|
||||
.note(r#"expected: `natvis_file = "..."`"#)
|
||||
.note(r#"OR"#)
|
||||
.note(r#"expected: `gdb_script_file = "..."`"#)
|
||||
.emit();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,8 @@ fn check_for_debugger_visualizer<'tcx>(
|
|||
let attrs = tcx.hir().attrs(hir_id);
|
||||
for attr in attrs {
|
||||
if attr.has_name(sym::debugger_visualizer) {
|
||||
let list = match attr.meta_item_list() {
|
||||
Some(list) => list,
|
||||
_ => continue,
|
||||
let Some(list) = attr.meta_item_list() else {
|
||||
continue
|
||||
};
|
||||
|
||||
let meta_item = match list.len() {
|
||||
|
@ -34,45 +33,28 @@ fn check_for_debugger_visualizer<'tcx>(
|
|||
_ => continue,
|
||||
};
|
||||
|
||||
let file = match (meta_item.name_or_empty(), meta_item.value_str()) {
|
||||
(sym::natvis_file, Some(value)) => {
|
||||
match resolve_path(&tcx.sess.parse_sess, value.as_str(), attr.span) {
|
||||
Ok(file) => file,
|
||||
Err(mut err) => {
|
||||
err.emit();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
(_, _) => continue,
|
||||
let visualizer_type = match meta_item.name_or_empty() {
|
||||
sym::natvis_file => DebuggerVisualizerType::Natvis,
|
||||
sym::gdb_script_file => DebuggerVisualizerType::GdbPrettyPrinter,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
if file.is_file() {
|
||||
let contents = match std::fs::read(&file) {
|
||||
Ok(contents) => contents,
|
||||
Err(err) => {
|
||||
tcx.sess
|
||||
.struct_span_err(
|
||||
attr.span,
|
||||
&format!(
|
||||
"Unable to read contents of file `{}`. {}",
|
||||
file.display(),
|
||||
err
|
||||
),
|
||||
)
|
||||
.emit();
|
||||
continue;
|
||||
let file = match meta_item.value_str() {
|
||||
Some(value) => {
|
||||
match resolve_path(&tcx.sess.parse_sess, value.as_str(), attr.span) {
|
||||
Ok(file) => file,
|
||||
_ => continue,
|
||||
}
|
||||
};
|
||||
}
|
||||
None => continue,
|
||||
};
|
||||
|
||||
debugger_visualizers.insert(DebuggerVisualizerFile::new(
|
||||
Arc::from(contents),
|
||||
DebuggerVisualizerType::Natvis,
|
||||
));
|
||||
} else {
|
||||
tcx.sess
|
||||
.struct_span_err(attr.span, &format!("{} is not a valid file", file.display()))
|
||||
.emit();
|
||||
match std::fs::read(&file) {
|
||||
Ok(contents) => {
|
||||
debugger_visualizers
|
||||
.insert(DebuggerVisualizerFile::new(Arc::from(contents), visualizer_type));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue