diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2017-08-30 11:11:12 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-08-30 11:11:12 -0500 |
| commit | fd4f362b304b0e93e7842830b1d3a0fee7d7c1bc (patch) | |
| tree | ca188916d8267f8a5728f0d8bc05f90c6bef38a9 /src/libproc_macro | |
| parent | bc6981ba55bc20fdd7513b0e1e89af5c7d547d34 (diff) | |
| parent | 8be132e9d76232feb2376de9edcbb34fe3ac99ac (diff) | |
| download | rust-fd4f362b304b0e93e7842830b1d3a0fee7d7c1bc.tar.gz rust-fd4f362b304b0e93e7842830b1d3a0fee7d7c1bc.zip | |
Rollup merge of #44125 - SergioBenitez:master, r=nrc
Initial diagnostic API for proc-macros.
This commit introduces the ability to create and emit `Diagnostic` structures from proc-macros, allowing for proc-macro authors to emit warning, error, note, and help messages just like the compiler does.
The API is somewhat based on the diagnostic API already present in `rustc` with several changes that improve usability. The entry point into the diagnostic API is a new `Diagnostic` type which is primarily created through new `error`, `warning`, `help`, and `note` methods on `Span`. The `Diagnostic` type records the diagnostic level, message, and optional `Span` for the top-level diagnostic and contains a `Vec` of all of the child diagnostics. Child diagnostics can be added through builder methods on `Diagnostic`.
A typical use of the API may look like:
```rust
let token = parse_token();
let val = parse_val();
val.span
.error(format!("expected A but found {}", val))
.span_note(token.span, "because of this token")
.help("consider using a different token")
.emit();
```
cc @jseyfried @nrc @dtolnay @alexcrichton
Diffstat (limited to 'src/libproc_macro')
| -rw-r--r-- | src/libproc_macro/Cargo.toml | 1 | ||||
| -rw-r--r-- | src/libproc_macro/diagnostic.rs | 134 | ||||
| -rw-r--r-- | src/libproc_macro/lib.rs | 22 |
3 files changed, 157 insertions, 0 deletions
diff --git a/src/libproc_macro/Cargo.toml b/src/libproc_macro/Cargo.toml index 1b5141773a9..cfd83e348a8 100644 --- a/src/libproc_macro/Cargo.toml +++ b/src/libproc_macro/Cargo.toml @@ -10,3 +10,4 @@ crate-type = ["dylib"] [dependencies] syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } +rustc_errors = { path = "../librustc_errors" } diff --git a/src/libproc_macro/diagnostic.rs b/src/libproc_macro/diagnostic.rs new file mode 100644 index 00000000000..c39aec896e6 --- /dev/null +++ b/src/libproc_macro/diagnostic.rs @@ -0,0 +1,134 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use Span; + +use rustc_errors as rustc; + +/// An enum representing a diagnostic level. +#[unstable(feature = "proc_macro", issue = "38356")] +#[derive(Copy, Clone, Debug)] +pub enum Level { + /// An error. + Error, + /// A warning. + Warning, + /// A note. + Note, + /// A help message. + Help, + #[doc(hidden)] + __Nonexhaustive, +} + +/// A structure representing a diagnostic message and associated children +/// messages. +#[unstable(feature = "proc_macro", issue = "38356")] +#[derive(Clone, Debug)] +pub struct Diagnostic { + level: Level, + message: String, + span: Option<Span>, + children: Vec<Diagnostic> +} + +macro_rules! diagnostic_child_methods { + ($spanned:ident, $regular:ident, $level:expr) => ( + /// Add a new child diagnostic message to `self` with the level + /// identified by this methods name with the given `span` and `message`. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn $spanned<T: Into<String>>(mut self, span: Span, message: T) -> Diagnostic { + self.children.push(Diagnostic::spanned(span, $level, message)); + self + } + + /// Add a new child diagnostic message to `self` with the level + /// identified by this method's name with the given `message`. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn $regular<T: Into<String>>(mut self, message: T) -> Diagnostic { + self.children.push(Diagnostic::new($level, message)); + self + } + ) +} + +impl Diagnostic { + /// Create a new diagnostic with the given `level` and `message`. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic { + Diagnostic { + level: level, + message: message.into(), + span: None, + children: vec![] + } + } + + /// Create a new diagnostic with the given `level` and `message` pointing to + /// the given `span`. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn spanned<T: Into<String>>(span: Span, level: Level, message: T) -> Diagnostic { + Diagnostic { + level: level, + message: message.into(), + span: Some(span), + children: vec![] + } + } + + diagnostic_child_methods!(span_error, error, Level::Error); + diagnostic_child_methods!(span_warning, warning, Level::Warning); + diagnostic_child_methods!(span_note, note, Level::Note); + diagnostic_child_methods!(span_help, help, Level::Help); + + /// Returns the diagnostic `level` for `self`. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn level(&self) -> Level { + self.level + } + + /// Emit the diagnostic. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn emit(self) { + ::__internal::with_sess(move |(sess, _)| { + let handler = &sess.span_diagnostic; + let level = __internal::level_to_internal_level(self.level); + let mut diag = rustc::DiagnosticBuilder::new(handler, level, &*self.message); + + if let Some(span) = self.span { + diag.set_span(span.0); + } + + for child in self.children { + let span = child.span.map(|s| s.0); + let level = __internal::level_to_internal_level(child.level); + diag.sub(level, &*child.message, span); + } + + diag.emit(); + }); + } +} + +#[unstable(feature = "proc_macro_internals", issue = "27812")] +#[doc(hidden)] +pub mod __internal { + use super::{Level, rustc}; + + pub fn level_to_internal_level(level: Level) -> rustc::Level { + match level { + Level::Error => rustc::Level::Error, + Level::Warning => rustc::Level::Warning, + Level::Note => rustc::Level::Note, + Level::Help => rustc::Level::Help, + Level::__Nonexhaustive => unreachable!("Level::__Nonexhaustive") + } + } +} diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index a86854cdbc7..e403e2a00c9 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -42,6 +42,12 @@ #[macro_use] extern crate syntax; extern crate syntax_pos; +extern crate rustc_errors; + +mod diagnostic; + +#[unstable(feature = "proc_macro", issue = "38356")] +pub use diagnostic::{Diagnostic, Level}; use std::{ascii, fmt, iter}; use std::str::FromStr; @@ -188,12 +194,28 @@ pub fn quote_span(span: Span) -> TokenStream { TokenStream(quote::Quote::quote(&span.0)) } +macro_rules! diagnostic_method { + ($name:ident, $level:expr) => ( + /// Create a new `Diagnostic` with the given `message` at the span + /// `self`. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn $name<T: Into<String>>(self, message: T) -> Diagnostic { + Diagnostic::spanned(self, $level, message) + } + ) +} + impl Span { /// The span of the invocation of the current procedural macro. #[unstable(feature = "proc_macro", issue = "38356")] pub fn call_site() -> Span { ::__internal::with_sess(|(_, mark)| Span(mark.expn_info().unwrap().call_site)) } + + diagnostic_method!(error, Level::Error); + diagnostic_method!(warning, Level::Warning); + diagnostic_method!(note, Level::Note); + diagnostic_method!(help, Level::Help); } /// A single token or a delimited sequence of token trees (e.g. `[1, (), ..]`). |
