Skip to content

Commit 10bb5ed

Browse files
committed
Add multispan support to proc-macro diagnostics.
Also updates the issue number for 'proc_macro_diagnostic'.
1 parent 50d8693 commit 10bb5ed

File tree

5 files changed

+244
-41
lines changed

5 files changed

+244
-41
lines changed

src/libproc_macro/diagnostic.rs

+72-39
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111
use Span;
1212

1313
use rustc_errors as errors;
14-
use syntax_pos::MultiSpan;
1514

1615
/// An enum representing a diagnostic level.
17-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
16+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
1817
#[derive(Copy, Clone, Debug)]
1918
#[non_exhaustive]
2019
pub enum Level {
@@ -28,43 +27,74 @@ pub enum Level {
2827
Help,
2928
}
3029

30+
/// Trait implemented by types that can be converted into a set of `Span`s.
31+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
32+
pub trait MultiSpan {
33+
/// Converts `self` into a `Vec<Span>`.
34+
fn into_spans(self) -> Vec<Span>;
35+
}
36+
37+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
38+
impl MultiSpan for Span {
39+
fn into_spans(self) -> Vec<Span> {
40+
vec![self]
41+
}
42+
}
43+
44+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
45+
impl MultiSpan for Vec<Span> {
46+
fn into_spans(self) -> Vec<Span> {
47+
self
48+
}
49+
}
50+
51+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
52+
impl<'a> MultiSpan for &'a [Span] {
53+
fn into_spans(self) -> Vec<Span> {
54+
self.to_vec()
55+
}
56+
}
57+
3158
/// A structure representing a diagnostic message and associated children
3259
/// messages.
33-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
60+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
3461
#[derive(Clone, Debug)]
3562
pub struct Diagnostic {
3663
level: Level,
3764
message: String,
38-
span: Option<Span>,
65+
spans: Vec<Span>,
3966
children: Vec<Diagnostic>
4067
}
4168

4269
macro_rules! diagnostic_child_methods {
4370
($spanned:ident, $regular:ident, $level:expr) => (
4471
/// Add a new child diagnostic message to `self` with the level
45-
/// identified by this methods name with the given `span` and `message`.
46-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
47-
pub fn $spanned<T: Into<String>>(mut self, span: Span, message: T) -> Diagnostic {
48-
self.children.push(Diagnostic::spanned(span, $level, message));
72+
/// identified by this method's name with the given `spans` and
73+
/// `message`.
74+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
75+
pub fn $spanned<S, T>(mut self, spans: S, message: T) -> Diagnostic
76+
where S: MultiSpan, T: Into<String>
77+
{
78+
self.children.push(Diagnostic::spanned(spans, $level, message));
4979
self
5080
}
5181

5282
/// Add a new child diagnostic message to `self` with the level
5383
/// identified by this method's name with the given `message`.
54-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
84+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
5585
pub fn $regular<T: Into<String>>(mut self, message: T) -> Diagnostic {
5686
self.children.push(Diagnostic::new($level, message));
5787
self
5888
}
5989
)
6090
}
6191

62-
#[derive(Debug, Clone)]
63-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
6492
/// Iterator over the children diagnostics of a `Diagnostic`.
93+
#[derive(Debug, Clone)]
94+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
6595
pub struct Children<'a>(::std::slice::Iter<'a, Diagnostic>);
6696

67-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
97+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
6898
impl<'a> Iterator for Children<'a> {
6999
type Item = &'a Diagnostic;
70100

@@ -73,27 +103,29 @@ impl<'a> Iterator for Children<'a> {
73103
}
74104
}
75105

76-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
106+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
77107
impl Diagnostic {
78108
/// Create a new diagnostic with the given `level` and `message`.
79-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
109+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
80110
pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
81111
Diagnostic {
82112
level: level,
83113
message: message.into(),
84-
span: None,
114+
spans: vec![],
85115
children: vec![]
86116
}
87117
}
88118

89119
/// Create a new diagnostic with the given `level` and `message` pointing to
90-
/// the given `span`.
91-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
92-
pub fn spanned<T: Into<String>>(span: Span, level: Level, message: T) -> Diagnostic {
120+
/// the given set of `spans`.
121+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
122+
pub fn spanned<S, T>(spans: S, level: Level, message: T) -> Diagnostic
123+
where S: MultiSpan, T: Into<String>
124+
{
93125
Diagnostic {
94126
level: level,
95127
message: message.into(),
96-
span: Some(span),
128+
spans: spans.into_spans(),
97129
children: vec![]
98130
}
99131
}
@@ -104,61 +136,62 @@ impl Diagnostic {
104136
diagnostic_child_methods!(span_help, help, Level::Help);
105137

106138
/// Returns the diagnostic `level` for `self`.
107-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
139+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
108140
pub fn level(&self) -> Level {
109141
self.level
110142
}
111143

112144
/// Sets the level in `self` to `level`.
113-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
145+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
114146
pub fn set_level(&mut self, level: Level) {
115147
self.level = level;
116148
}
117149

118150
/// Returns the message in `self`.
119-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
151+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
120152
pub fn message(&self) -> &str {
121153
&self.message
122154
}
123155

124156
/// Sets the message in `self` to `message`.
125-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
157+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
126158
pub fn set_message<T: Into<String>>(&mut self, message: T) {
127159
self.message = message.into();
128160
}
129161

130-
/// Returns the `Span` in `self`.
131-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
132-
pub fn span(&self) -> Option<Span> {
133-
self.span
162+
/// Returns the `Span`s in `self`.
163+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
164+
pub fn spans(&self) -> &[Span] {
165+
&self.spans
134166
}
135167

136-
/// Sets the `Span` in `self` to `span`.
137-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
138-
pub fn set_span(&mut self, span: Span) {
139-
self.span = Some(span);
168+
/// Sets the `Span`s in `self` to `spans`.
169+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
170+
pub fn set_spans<S: MultiSpan>(&mut self, spans: S) {
171+
self.spans = spans.into_spans();
140172
}
141173

142174
/// Returns an iterator over the children diagnostics of `self`.
143-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
175+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
144176
pub fn children(&self) -> Children {
145177
Children(self.children.iter())
146178
}
147179

148180
/// Emit the diagnostic.
149-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
181+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
150182
pub fn emit(self) {
183+
fn to_internal(spans: Vec<Span>) -> ::syntax_pos::MultiSpan {
184+
let spans: Vec<_> = spans.into_iter().map(|s| s.0).collect();
185+
::syntax_pos::MultiSpan::from_spans(spans)
186+
}
187+
151188
let level = self.level.to_internal();
152189
let mut diag = errors::Diagnostic::new(level, &*self.message);
153-
154-
if let Some(span) = self.span {
155-
diag.set_span(span.0);
156-
}
190+
diag.set_span(to_internal(self.spans));
157191

158192
for child in self.children {
159-
let span = child.span.map_or(MultiSpan::new(), |s| s.0.into());
160193
let level = child.level.to_internal();
161-
diag.sub(level, &*child.message, span, None);
194+
diag.sub(level, &*child.message, to_internal(child.spans), None);
162195
}
163196

164197
::__internal::with_sess(move |sess, _| {

src/libproc_macro/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub mod rustc;
4747

4848
mod diagnostic;
4949

50-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
50+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
5151
pub use diagnostic::{Diagnostic, Level};
5252

5353
use std::{ascii, fmt, iter};
@@ -274,7 +274,7 @@ macro_rules! diagnostic_method {
274274
($name:ident, $level:expr) => (
275275
/// Create a new `Diagnostic` with the given `message` at the span
276276
/// `self`.
277-
#[unstable(feature = "proc_macro_diagnostic", issue = "38356")]
277+
#[unstable(feature = "proc_macro_diagnostic", issue = "54140")]
278278
pub fn $name<T: Into<String>>(self, message: T) -> Diagnostic {
279279
Diagnostic::spanned(self, $level, message)
280280
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2017 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+
// no-prefer-dynamic
12+
13+
#![crate_type = "proc-macro"]
14+
#![feature(proc_macro_diagnostic, proc_macro_span)]
15+
16+
extern crate proc_macro;
17+
18+
use proc_macro::{TokenStream, TokenTree, Span, Diagnostic};
19+
20+
fn parse(input: TokenStream) -> Result<(), Diagnostic> {
21+
let mut hi_spans = vec![];
22+
for tree in input {
23+
if let TokenTree::Ident(ref ident) = tree {
24+
if ident.to_string() == "hi" {
25+
hi_spans.push(ident.span());
26+
}
27+
}
28+
}
29+
30+
if !hi_spans.is_empty() {
31+
return Err(Span::def_site()
32+
.error("hello to you, too!")
33+
.span_note(hi_spans, "found these 'hi's"));
34+
}
35+
36+
Ok(())
37+
}
38+
39+
#[proc_macro]
40+
pub fn hello(input: TokenStream) -> TokenStream {
41+
if let Err(diag) = parse(input) {
42+
diag.emit();
43+
}
44+
45+
TokenStream::new()
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2016 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+
// aux-build:multispan.rs
12+
// ignore-stage1
13+
14+
#![feature(proc_macro_non_items)]
15+
16+
extern crate multispan;
17+
18+
use multispan::hello;
19+
20+
fn main() {
21+
// This one emits no error.
22+
hello!();
23+
24+
// Exactly one 'hi'.
25+
hello!(hi); //~ ERROR hello to you, too!
26+
27+
// Now two, back to back.
28+
hello!(hi hi); //~ ERROR hello to you, too!
29+
30+
// Now three, back to back.
31+
hello!(hi hi hi); //~ ERROR hello to you, too!
32+
33+
// Now several, with spacing.
34+
hello!(hi hey hi yo hi beep beep hi hi); //~ ERROR hello to you, too!
35+
hello!(hi there, hi how are you? hi... hi.); //~ ERROR hello to you, too!
36+
hello!(whoah. hi di hi di ho); //~ ERROR hello to you, too!
37+
hello!(hi good hi and good bye); //~ ERROR hello to you, too!
38+
}

0 commit comments

Comments
 (0)