Skip to content

Commit 19d2ee6

Browse files
committed
Include scope names in diagnostics
So far, rustc has only printed about filenames and line numbers for warnings and errors. I think it is rather missed, compared to `gcc` and other compiler, that useful context information such as function names and structs is not included. The changes in this pull request introduce a new line emission in diagnostics to implement this.
1 parent e315056 commit 19d2ee6

File tree

6 files changed

+198
-3
lines changed

6 files changed

+198
-3
lines changed

src/librustc_driver/driver.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ pub fn compile_input(
196196
)?
197197
};
198198

199+
// Let diagnostics access the AST for allowing to show function names in messages.
200+
let expanded_crate = Lrc::new(expanded_crate);
201+
sess.diagnostic().set_ast(
202+
Box::new(syntax::diagnostics::ast::ContextResolver::new(expanded_crate.clone())));
203+
199204
let output_paths = generated_output_paths(sess, &outputs, output.is_some(), &crate_name);
200205

201206
// Ensure the source file isn't accidentally overwritten during compilation.
@@ -257,7 +262,7 @@ pub fn compile_input(
257262
&hir_map,
258263
&analysis,
259264
&resolutions,
260-
&expanded_crate,
265+
&*expanded_crate,
261266
&hir_map.krate(),
262267
&outputs,
263268
&crate_name
@@ -267,8 +272,10 @@ pub fn compile_input(
267272
}
268273

269274
let opt_crate = if control.keep_ast {
270-
Some(&expanded_crate)
275+
Some(&*expanded_crate)
271276
} else {
277+
// AST will be kept for scope naming in diagnostic, should
278+
// depricate keep_ast?
272279
drop(expanded_crate);
273280
None
274281
};

src/librustc_errors/emitter.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ use std::cmp::min;
2626
use termcolor::{StandardStream, ColorChoice, ColorSpec, BufferWriter};
2727
use termcolor::{WriteColor, Color, Buffer};
2828
use unicode_width;
29+
use SpanContextResolver;
30+
use SpanContextKind;
31+
use SpanContext;
32+
use std::cell::RefCell;
2933

3034
const ANONYMIZED_LINE_NUM: &str = "LL";
3135

@@ -81,6 +85,7 @@ impl Emitter for EmitterWriter {
8185
self.emit_messages_default(&db.level,
8286
&db.styled_message(),
8387
&db.code,
88+
&db.handler.context_resolver,
8489
&primary_span,
8590
&children,
8691
&suggestions);
@@ -958,6 +963,7 @@ impl EmitterWriter {
958963
msp: &MultiSpan,
959964
msg: &Vec<(String, Style)>,
960965
code: &Option<DiagnosticId>,
966+
cr: &RefCell<Option<Box<SpanContextResolver>>>,
961967
level: &Level,
962968
max_line_num_len: usize,
963969
is_secondary: bool)
@@ -1042,6 +1048,18 @@ impl EmitterWriter {
10421048
cm.doctest_offset_line(loc.line),
10431049
loc.col.0 + 1),
10441050
Style::LineAndColumn);
1051+
if let Some(ref primary_span) = msp.primary_span().as_ref() {
1052+
if primary_span != &&DUMMY_SP {
1053+
if let &Some(ref rc) = &*cr.borrow() {
1054+
let context = rc.span_to_context(**primary_span);
1055+
if let Some(context) = context {
1056+
repr_styled_context(buffer_msg_line_offset,
1057+
&mut buffer, context);
1058+
}
1059+
}
1060+
}
1061+
}
1062+
10451063
for _ in 0..max_line_num_len {
10461064
buffer.prepend(buffer_msg_line_offset, " ", Style::NoStyle);
10471065
}
@@ -1282,6 +1300,7 @@ impl EmitterWriter {
12821300
level: &Level,
12831301
message: &Vec<(String, Style)>,
12841302
code: &Option<DiagnosticId>,
1303+
cr: &RefCell<Option<Box<SpanContextResolver>>>,
12851304
span: &MultiSpan,
12861305
children: &Vec<SubDiagnostic>,
12871306
suggestions: &[CodeSuggestion]) {
@@ -1294,6 +1313,7 @@ impl EmitterWriter {
12941313
match self.emit_message_default(span,
12951314
message,
12961315
code,
1316+
cr,
12971317
level,
12981318
max_line_num_len,
12991319
false) {
@@ -1315,6 +1335,7 @@ impl EmitterWriter {
13151335
match self.emit_message_default(&span,
13161336
&child.styled_message(),
13171337
&None,
1338+
cr,
13181339
&child.level,
13191340
max_line_num_len,
13201341
true) {
@@ -1396,6 +1417,23 @@ fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool {
13961417
num_overlap(a1.start_col, a1.end_col + padding, a2.start_col, a2.end_col, false)
13971418
}
13981419

1420+
fn repr_styled_context(line_offset: usize, buffer: &mut StyledBuffer, context: SpanContext)
1421+
{
1422+
let kind_str = match context.kind {
1423+
SpanContextKind::Enum => "enum",
1424+
SpanContextKind::Module => "mod",
1425+
SpanContextKind::Function => "fn",
1426+
SpanContextKind::Impl => "impl",
1427+
SpanContextKind::Method => "fn",
1428+
SpanContextKind::Struct => "struct",
1429+
SpanContextKind::Trait => "trait",
1430+
SpanContextKind::Union => "union",
1431+
};
1432+
1433+
buffer.append(line_offset, &format!(": in {}", kind_str), Style::NoStyle);
1434+
buffer.append(line_offset, &format!(" {}", context.path), Style::HeaderMsg);
1435+
}
1436+
13991437
fn emit_to_destination(rendered_buffer: &Vec<Vec<StyledString>>,
14001438
lvl: &Level,
14011439
dst: &mut Destination,

src/librustc_errors/lib.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use rustc_data_structures::fx::FxHashSet;
3838
use rustc_data_structures::stable_hasher::StableHasher;
3939

4040
use std::borrow::Cow;
41-
use std::cell::Cell;
41+
use std::cell::{Cell, RefCell};
4242
use std::{error, fmt};
4343
use std::sync::atomic::AtomicUsize;
4444
use std::sync::atomic::Ordering::SeqCst;
@@ -286,6 +286,9 @@ pub struct Handler {
286286
// this handler. These hashes is used to avoid emitting the same error
287287
// twice.
288288
emitted_diagnostics: Lock<FxHashSet<u128>>,
289+
290+
// Callback to the Span AST resolver
291+
context_resolver: RefCell<Option<Box<SpanContextResolver>>>,
289292
}
290293

291294
fn default_track_diagnostic(_: &Diagnostic) {}
@@ -300,6 +303,32 @@ pub struct HandlerFlags {
300303
pub external_macro_backtrace: bool,
301304
}
302305

306+
pub enum SpanContextKind {
307+
Enum,
308+
Function,
309+
Impl,
310+
Method,
311+
Struct,
312+
Trait,
313+
Union,
314+
Module,
315+
}
316+
317+
pub struct SpanContext {
318+
kind: SpanContextKind,
319+
path: String,
320+
}
321+
322+
impl SpanContext {
323+
pub fn new(kind: SpanContextKind, path: String) -> Self {
324+
Self { kind, path }
325+
}
326+
}
327+
328+
pub trait SpanContextResolver {
329+
fn span_to_context(&self, sp: Span) -> Option<SpanContext>;
330+
}
331+
303332
impl Handler {
304333
pub fn with_tty_emitter(color_config: ColorConfig,
305334
can_emit_warnings: bool,
@@ -347,9 +376,14 @@ impl Handler {
347376
taught_diagnostics: Lock::new(FxHashSet()),
348377
emitted_diagnostic_codes: Lock::new(FxHashSet()),
349378
emitted_diagnostics: Lock::new(FxHashSet()),
379+
context_resolver: RefCell::new(None),
350380
}
351381
}
352382

383+
pub fn set_ast(&self, scr: Box<SpanContextResolver>) {
384+
*self.context_resolver.borrow_mut() = Some(scr);
385+
}
386+
353387
pub fn set_continue_after_error(&self, continue_after_error: bool) {
354388
self.continue_after_error.set(continue_after_error);
355389
}

src/libsyntax/diagnostics/ast.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use ast::*;
12+
use errors::{SpanContextResolver, SpanContextKind, SpanContext};
13+
use rustc_data_structures::sync::Lrc;
14+
use syntax_pos::Span;
15+
use visit::{self, Visitor, FnKind};
16+
17+
pub struct ContextResolver {
18+
krate: Lrc<Crate>
19+
}
20+
21+
impl ContextResolver {
22+
pub fn new(krate: Lrc<Crate>) -> Self {
23+
Self { krate }
24+
}
25+
}
26+
27+
struct SpanResolver {
28+
idents: Vec<Ident>,
29+
kind: Option<SpanContextKind>,
30+
span: Span,
31+
}
32+
33+
impl<'ast> Visitor<'ast> for SpanResolver {
34+
fn visit_trait_item(&mut self, ti: &'ast TraitItem) {
35+
if ti.span.proper_contains(self.span) {
36+
self.idents.push(ti.ident);
37+
self.kind = Some(SpanContextKind::Trait);
38+
visit::walk_trait_item(self, ti)
39+
}
40+
}
41+
42+
fn visit_impl_item(&mut self, ii: &'ast ImplItem) {
43+
if ii.span.proper_contains(self.span) {
44+
self.idents.push(ii.ident);
45+
self.kind = Some(SpanContextKind::Impl);
46+
visit::walk_impl_item(self, ii)
47+
}
48+
}
49+
50+
fn visit_item(&mut self, i: &'ast Item) {
51+
if i.span.proper_contains(self.span) {
52+
let kind = match i.node {
53+
ItemKind::Enum(..) => Some(SpanContextKind::Enum),
54+
ItemKind::Struct(..) => Some(SpanContextKind::Struct),
55+
ItemKind::Union(..) => Some(SpanContextKind::Union),
56+
ItemKind::Trait(..) => Some(SpanContextKind::Trait),
57+
ItemKind::Mod(..) => Some(SpanContextKind::Module),
58+
_ => None,
59+
};
60+
61+
if kind.is_some() {
62+
self.idents.push(i.ident);
63+
self.kind = kind;
64+
}
65+
66+
visit::walk_item(self, i);
67+
}
68+
}
69+
70+
fn visit_fn(&mut self, fk: FnKind<'ast>, fd: &'ast FnDecl, s: Span, _: NodeId) {
71+
if s.proper_contains(self.span) {
72+
match fk {
73+
FnKind::ItemFn(ref ident, ..) => {
74+
self.idents.push(*ident);
75+
self.kind = Some(SpanContextKind::Function);
76+
}
77+
FnKind::Method(ref ident, ..) => {
78+
self.idents.push(*ident);
79+
self.kind = Some(SpanContextKind::Method);
80+
}
81+
_ => {}
82+
}
83+
84+
visit::walk_fn(self, fk, fd, s)
85+
}
86+
}
87+
}
88+
89+
impl SpanContextResolver for ContextResolver {
90+
fn span_to_context(&self, sp: Span) -> Option<SpanContext> {
91+
let mut sr = SpanResolver {
92+
idents: Vec::new(),
93+
span: sp,
94+
kind: None,
95+
};
96+
visit::walk_crate(&mut sr, &*self.krate);
97+
98+
let SpanResolver { kind, idents, .. } = sr;
99+
100+
match kind {
101+
None => None,
102+
Some(kind) => {
103+
let path = idents.iter().map(
104+
|x| x.to_string()).collect::<Vec<String>>().join("::");
105+
Some(SpanContext::new(kind, path))
106+
}
107+
}
108+
}
109+
}

src/libsyntax/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub mod diagnostics {
106106
pub mod macros;
107107
pub mod plugin;
108108
pub mod metadata;
109+
pub mod ast;
109110
}
110111

111112
// NB: This module needs to be declared first so diagnostics are

src/libsyntax_pos/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,12 @@ impl Span {
265265
span.lo <= other.lo && other.hi <= span.hi
266266
}
267267

268+
pub fn proper_contains(self, other: Span) -> bool {
269+
let span = self.data();
270+
let other = other.data();
271+
span.lo < other.lo && other.hi < span.hi
272+
}
273+
268274
/// Return true if the spans are equal with regards to the source text.
269275
///
270276
/// Use this instead of `==` when either span could be generated code,

0 commit comments

Comments
 (0)