Skip to content

Commit 91d55fc

Browse files
committed
Move to UseSite for build_error span recovery
1 parent f4323d1 commit 91d55fc

5 files changed

Lines changed: 126 additions & 87 deletions

File tree

‎src/binary_analysis/build_error.rs‎

Lines changed: 82 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use object::{File, Object, ObjectSection, ObjectSymbol, RelocationTarget};
22
use rustc_middle::mir::mono::MonoItem;
3-
use rustc_middle::ty::{Instance, TyCtxt};
3+
use rustc_middle::ty::{Instance, TypingEnv};
44
use rustc_span::Span;
55

6+
use crate::ctxt::AnalysisCtxt;
7+
use crate::diagnostic::use_stack::{UseSite, UseSiteKind};
8+
69
#[derive(Diagnostic)]
710
#[diag(klint_build_error_referenced_without_symbol)]
811
struct BuildErrorReferencedWithoutSymbol;
@@ -14,15 +17,20 @@ struct BuildErrorReferencedWithoutInstance<'a> {
1417
}
1518

1619
#[derive(Diagnostic)]
17-
#[diag(klint_build_error_referenced)]
18-
struct BuildErrorReferenced<'tcx> {
20+
#[diag(klint_build_error_referenced_without_debug)]
21+
struct BuildErrorReferencedWithoutDebug<'tcx> {
1922
#[primary_span]
2023
pub span: Span,
2124
pub kind: &'static str,
2225
pub instance: Instance<'tcx>,
26+
pub err: String,
2327
}
2428

25-
pub fn build_error_detection<'tcx, 'obj>(tcx: TyCtxt<'tcx>, file: &File<'obj>) {
29+
#[derive(Diagnostic)]
30+
#[diag(klint_build_error_referenced)]
31+
struct BuildErrorReferenced;
32+
33+
pub fn build_error_detection<'tcx, 'obj>(cx: &AnalysisCtxt<'tcx>, file: &File<'obj>) {
2634
let Some(build_error_symbol) = file.symbol_by_name("rust_build_error") else {
2735
// This object file contains no reference to `build_error`, all good!
2836
return;
@@ -41,55 +49,42 @@ pub fn build_error_detection<'tcx, 'obj>(tcx: TyCtxt<'tcx>, file: &File<'obj>) {
4149

4250
// Collect all mono items, which we will use to find out which symbol is problematic.
4351
let mono_items = crate::monomorphize_collector::collect_crate_mono_items(
44-
tcx,
52+
cx.tcx,
4553
crate::monomorphize_collector::MonoItemCollectionStrategy::Lazy,
4654
)
4755
.0;
4856

4957
for section in file.sections() {
5058
for (offset, relocation) in section.relocations() {
5159
if relocation.target() == relo_target_needle {
52-
// Found a relocation that points to `build_error`. Emit and error.
60+
// Found a relocation that points to `build_error`. Emit an error.
5361
let Some((symbol, _)) =
5462
super::find_symbol_from_section_offset(file, &section, offset)
5563
else {
56-
tcx.dcx().emit_err(BuildErrorReferencedWithoutSymbol);
64+
cx.dcx().emit_err(BuildErrorReferencedWithoutSymbol);
5765
continue;
5866
};
5967

6068
let Some(mono) = mono_items
6169
.iter()
62-
.find(|item| item.symbol_name(tcx).name == symbol)
70+
.find(|item| item.symbol_name(cx.tcx).name == symbol)
6371
else {
64-
tcx.dcx()
72+
cx.dcx()
6573
.emit_err(BuildErrorReferencedWithoutInstance { symbol });
6674
continue;
6775
};
6876

69-
let mut diag = tcx.dcx().create_err(match mono {
70-
MonoItem::Fn(instance) => BuildErrorReferenced {
71-
span: tcx.def_span(instance.def_id()),
72-
kind: "fn",
73-
instance: *instance,
74-
},
75-
MonoItem::Static(def_id) => BuildErrorReferenced {
76-
span: tcx.def_span(def_id),
77-
kind: "static",
78-
instance: Instance::mono(tcx, *def_id),
79-
},
80-
MonoItem::GlobalAsm(_) => {
81-
// We're not going to be covered by symbols inside global asm.
82-
bug!();
83-
}
84-
});
85-
8677
let loader = super::dwarf::DwarfLoader::new(file)
8778
.expect("DWARF loader creation should not fail");
79+
80+
let mut diag = cx.dcx().create_err(BuildErrorReferenced);
8881
let mut frame = match mono {
89-
MonoItem::Fn(instance) => Some(*instance),
90-
_ => None,
82+
MonoItem::Fn(instance) => *instance,
83+
MonoItem::Static(def_id) => Instance::mono(cx.tcx, *def_id),
84+
MonoItem::GlobalAsm(_) => bug!(),
9185
};
9286

87+
let mut recovered_call_stack = Vec::new();
9388
let result: Result<_, super::dwarf::Error> = try {
9489
let call_stack = loader.inline_info(section.index(), offset)?;
9590
if let Some(first) = call_stack.first() {
@@ -100,16 +95,17 @@ pub fn build_error_detection<'tcx, 'obj>(tcx: TyCtxt<'tcx>, file: &File<'obj>) {
10095
}
10196
}
10297
for call in call_stack {
103-
if let Some(caller) = frame.take() {
104-
if let Some((callee, span)) = super::reconstruct::recover_fn_call_span(
105-
tcx,
106-
caller,
107-
&call.callee,
108-
call.location.as_ref(),
109-
) {
110-
frame = Some(callee);
111-
diag.span_note(span, format!("which calls `{callee}`"));
112-
}
98+
if let Some((callee, site)) = super::reconstruct::recover_fn_call_span(
99+
cx.tcx,
100+
frame,
101+
&call.callee,
102+
call.location.as_ref(),
103+
) {
104+
recovered_call_stack.push(UseSite {
105+
instance: TypingEnv::fully_monomorphized().as_query_input(frame),
106+
kind: site,
107+
});
108+
frame = callee;
113109
}
114110
}
115111
};
@@ -124,34 +120,60 @@ pub fn build_error_detection<'tcx, 'obj>(tcx: TyCtxt<'tcx>, file: &File<'obj>) {
124120
super::dwarf::Error::UnexpectedDwarf("cannot find line number info"),
125121
)?;
126122

127-
if let Some(frame) = frame
128-
&& let Some((_, span)) = super::reconstruct::recover_fn_call_span(
129-
tcx,
130-
frame,
131-
"rust_build_error",
132-
Some(&loc),
133-
)
134-
{
135-
diag.span_note(
136-
span,
137-
"which contains a `build_error` call that is not optimized out",
138-
);
123+
if let Some((_, site)) = super::reconstruct::recover_fn_call_span(
124+
cx.tcx,
125+
frame,
126+
"rust_build_error",
127+
Some(&loc),
128+
) {
129+
recovered_call_stack.push(UseSite {
130+
instance: TypingEnv::fully_monomorphized().as_query_input(frame),
131+
kind: site,
132+
});
139133
} else {
140-
let span = super::reconstruct::recover_span_from_line_no(tcx, &loc).ok_or(
141-
super::dwarf::Error::Other("cannot find file in compiler session"),
142-
)?;
143-
diag.span_note(
144-
span,
145-
"which contains a `build_error` reference that is not optimized out",
146-
);
134+
let span = super::reconstruct::recover_span_from_line_no(cx.tcx, &loc)
135+
.ok_or(super::dwarf::Error::Other(
136+
"cannot find file in compiler session",
137+
))?;
138+
recovered_call_stack.push(UseSite {
139+
instance: TypingEnv::fully_monomorphized().as_query_input(frame),
140+
kind: UseSiteKind::Other(
141+
span,
142+
"which is referenced by this function".to_string(),
143+
),
144+
})
147145
}
148146
};
149147
if let Err(err) = result {
150-
diag.note(format!(
151-
"attempt to reconstruct line information from DWARF failed: {err}"
152-
));
148+
diag.cancel();
149+
150+
// If even line number cannot be recovered, emit a different diagnostic.
151+
cx.dcx().emit_err(match mono {
152+
MonoItem::Fn(instance) => BuildErrorReferencedWithoutDebug {
153+
span: cx.def_span(instance.def_id()),
154+
kind: "fn",
155+
instance: *instance,
156+
err: err.to_string(),
157+
},
158+
MonoItem::Static(def_id) => BuildErrorReferencedWithoutDebug {
159+
span: cx.def_span(def_id),
160+
kind: "static",
161+
instance: Instance::mono(cx.tcx, *def_id),
162+
err: err.to_string(),
163+
},
164+
MonoItem::GlobalAsm(_) => {
165+
// We're not going to be covered by symbols inside global asm.
166+
bug!();
167+
}
168+
});
169+
continue;
153170
}
154171

172+
cx.note_use_stack(&mut diag, &recovered_call_stack);
173+
diag.span_note(
174+
cx.def_span(mono.def_id()),
175+
format!("reference contained in `{}`", mono),
176+
);
155177
diag.emit();
156178
}
157179
}

‎src/binary_analysis/mod.rs‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@ use std::fs::File;
22
use std::path::Path;
33

44
use object::{File as ObjectFile, Object, ObjectSection, ObjectSymbol, Section, SymbolSection};
5-
use rustc_middle::ty::TyCtxt;
5+
6+
use crate::ctxt::AnalysisCtxt;
67

78
mod build_error;
89
mod dwarf;
910
mod reconstruct;
1011

11-
pub fn binary_analysis<'tcx>(tcx: TyCtxt<'tcx>, path: &Path) {
12+
pub fn binary_analysis<'tcx>(cx: &AnalysisCtxt<'tcx>, path: &Path) {
1213
let file = File::open(path).unwrap();
1314
let mmap = unsafe { rustc_data_structures::memmap::Mmap::map(file) }.unwrap();
1415
let object = ObjectFile::parse(&*mmap).unwrap();
1516

16-
build_error::build_error_detection(tcx, &object);
17+
build_error::build_error_detection(cx, &object);
1718
}
1819

1920
fn find_symbol_from_section_offset<'obj>(

‎src/binary_analysis/reconstruct.rs‎

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use rustc_middle::ty::{Instance, TyCtxt};
22
use rustc_middle::{mir, ty};
33
use rustc_span::{BytePos, DUMMY_SP, FileName, Span};
44

5+
use crate::diagnostic::use_stack::UseSiteKind;
6+
57
pub fn recover_span_from_line_no<'tcx>(
68
tcx: TyCtxt<'tcx>,
79
location: &super::dwarf::Location,
@@ -45,11 +47,11 @@ pub fn recover_fn_call_span<'tcx>(
4547
caller: Instance<'tcx>,
4648
callee: &str,
4749
location: Option<&super::dwarf::Location>,
48-
) -> Option<(Instance<'tcx>, Span)> {
50+
) -> Option<(Instance<'tcx>, UseSiteKind)> {
4951
let mir = tcx.instance_mir(caller.def);
5052

5153
let mut callee_instance = None;
52-
let mut spans = Vec::new();
54+
let mut sites = Vec::new();
5355

5456
for block in mir.basic_blocks.iter() {
5557
let terminator = block.terminator();
@@ -62,7 +64,7 @@ pub fn recover_fn_call_span<'tcx>(
6264
continue;
6365
}
6466

65-
let instance = match terminator.kind {
67+
match terminator.kind {
6668
mir::TerminatorKind::Call { ref func, .. }
6769
| mir::TerminatorKind::TailCall { ref func, .. } => {
6870
let callee_ty = func.ty(mir, tcx);
@@ -75,13 +77,20 @@ pub fn recover_fn_call_span<'tcx>(
7577
let ty::FnDef(def_id, args) = *callee_ty.kind() else {
7678
continue;
7779
};
78-
ty::Instance::expect_resolve(
80+
81+
let instance = ty::Instance::expect_resolve(
7982
tcx,
8083
ty::TypingEnv::fully_monomorphized(),
8184
def_id,
8285
args,
8386
terminator.source_info.span,
84-
)
87+
);
88+
if tcx.symbol_name(instance).name != callee {
89+
continue;
90+
}
91+
92+
callee_instance = Some(instance);
93+
sites.push(UseSiteKind::Call(terminator.source_info.span));
8594
}
8695
mir::TerminatorKind::Drop { ref place, .. } => {
8796
let ty = place.ty(mir, tcx).ty;
@@ -90,18 +99,21 @@ pub fn recover_fn_call_span<'tcx>(
9099
ty::TypingEnv::fully_monomorphized(),
91100
ty::EarlyBinder::bind(ty),
92101
);
93-
Instance::resolve_drop_in_place(tcx, ty)
102+
103+
let instance = Instance::resolve_drop_in_place(tcx, ty);
104+
if tcx.symbol_name(instance).name != callee {
105+
continue;
106+
}
107+
108+
callee_instance = Some(instance);
109+
sites.push(UseSiteKind::Drop {
110+
drop_span: terminator.source_info.span,
111+
place_span: mir.local_decls[place.local].source_info.span,
112+
});
94113
}
95114

96115
_ => continue,
97116
};
98-
99-
if tcx.symbol_name(instance).name != callee {
100-
continue;
101-
}
102-
103-
callee_instance = Some(instance);
104-
spans.push(terminator.source_info.span);
105117
}
106118

107119
// In addition to direct function calls, we should also inspect inlined functions.
@@ -114,7 +126,7 @@ pub fn recover_fn_call_span<'tcx>(
114126
}
115127

116128
callee_instance = Some(instance);
117-
spans.push(span);
129+
sites.push(UseSiteKind::Call(span));
118130
}
119131
}
120132

@@ -124,8 +136,8 @@ pub fn recover_fn_call_span<'tcx>(
124136
};
125137

126138
// If there's only a single span, then it has to be the correct span.
127-
if spans.len() == 1 {
128-
return Some((callee_instance, spans[0]));
139+
if sites.len() == 1 {
140+
return Some((callee_instance, sites.pop().unwrap()));
129141
}
130142

131143
// Otherwise, we need to use the DWARF location information to find the best related span.
@@ -135,7 +147,7 @@ pub fn recover_fn_call_span<'tcx>(
135147
caller,
136148
callee_instance
137149
);
138-
return Some((callee_instance, spans[0]));
150+
return Some((callee_instance, sites.pop().unwrap()));
139151
};
140152

141153
let Some(recovered_span) = recover_span_from_line_no(tcx, loc) else {
@@ -144,16 +156,16 @@ pub fn recover_fn_call_span<'tcx>(
144156
caller,
145157
callee_instance
146158
);
147-
return Some((callee_instance, spans[0]));
159+
return Some((callee_instance, sites.pop().unwrap()));
148160
};
149161

150162
// Now we have a recovered span. Use this span to match spans that we have.
151-
for span in spans {
152-
if recover_span(recovered_span, span) {
153-
return Some((callee_instance, span));
163+
for site in sites {
164+
if recover_span(recovered_span, site.span()) {
165+
return Some((callee_instance, site));
154166
}
155167
}
156168

157169
// No perfect match, just use the recovered span that we have.
158-
Some((callee_instance, recovered_span))
170+
Some((callee_instance, UseSiteKind::Call(recovered_span)))
159171
}

‎src/main.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ impl driver::CallbacksExt for MyCallbacks {
126126
fn after_codegen<'tcx>(&mut self, cx: &'tcx AnalysisCtxt<'tcx>) {
127127
let outputs = cx.output_filenames(());
128128
if outputs.outputs.contains_key(&OutputType::Object) {
129-
binary_analysis::binary_analysis(cx.tcx, outputs.path(OutputType::Object).as_path());
129+
binary_analysis::binary_analysis(cx, outputs.path(OutputType::Object).as_path());
130130
}
131131
}
132132
}

‎src/messages.ftl‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,9 @@ klint_build_error_referenced_without_symbol =
1414
klint_build_error_referenced_without_instance =
1515
symbol `{$symbol}` references `build_error` in the object file, but no associated instance is found
1616
17-
klint_build_error_referenced =
17+
klint_build_error_referenced_without_debug =
1818
`{$kind} {$instance}` contains reference to `build_error`
19+
.note = attempt to reconstruct line information from DWARF failed: {$err}
20+
21+
klint_build_error_referenced =
22+
this `build_error` reference is not optimized away

0 commit comments

Comments
 (0)