name = "proc_macro"
version = "0.0.0"
dependencies = [
+ "rustc_errors 0.0.0",
"syntax 0.0.0",
"syntax_pos 0.0.0",
]
[dependencies]
syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }
+rustc_errors = { path = "../librustc_errors" }
--- /dev/null
+// 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")
+ }
+ }
+}
#[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;
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, (), ..]`).
/// Convenience function for internal use, clients should use one of the
/// public methods above.
- fn sub(&mut self,
+ pub(crate) fn sub(&mut self,
level: Level,
message: &str,
span: MultiSpan,
// }
}
+ /// Convenience function for internal use, clients should use one of the
+ /// span_* methods instead.
+ pub fn sub<S: Into<MultiSpan>>(
+ &mut self,
+ level: Level,
+ message: &str,
+ span: Option<S>,
+ ) -> &mut Self {
+ let span = span.map(|s| s.into()).unwrap_or(MultiSpan::new());
+ self.diagnostic.sub(level, message, span, None);
+ self
+ }
+
/// Delay emission of this diagnostic as a bug.
///
/// This can be useful in contexts where an error indicates a bug but
--- /dev/null
+// 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.
+
+// no-prefer-dynamic
+#![feature(proc_macro)]
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{TokenStream, TokenNode, Span, Diagnostic};
+
+fn parse(input: TokenStream) -> Result<(), Diagnostic> {
+ let mut count = 0;
+ let mut last_span = Span::default();
+ for tree in input {
+ let span = tree.span;
+ if count >= 3 {
+ return Err(span.error(format!("expected EOF, found `{}`.", tree))
+ .span_note(last_span, "last good input was here")
+ .help("input must be: `===`"))
+ }
+
+ if let TokenNode::Op('=', _) = tree.kind {
+ count += 1;
+ } else {
+ return Err(span.error(format!("expected `=`, found `{}`.", tree)));
+ }
+
+ last_span = span;
+ }
+
+ if count < 3 {
+ return Err(Span::default()
+ .error(format!("found {} equal signs, need exactly 3", count))
+ .help("input must be: `===`"))
+ }
+
+ Ok(())
+}
+
+#[proc_macro]
+pub fn three_equals(input: TokenStream) -> TokenStream {
+ if let Err(diag) = parse(input) {
+ diag.emit();
+ return TokenStream::empty();
+ }
+
+ "3".parse().unwrap()
+}
--- /dev/null
+// Copyright 2016 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.
+
+// aux-build:three-equals.rs
+// ignore-stage1
+
+#![feature(proc_macro)]
+
+extern crate three_equals;
+
+use three_equals::three_equals;
+
+fn main() {
+ // This one is okay.
+ three_equals!(===);
+
+ // Need exactly three equals.
+ three_equals!(==);
+
+ // Need exactly three equals.
+ three_equals!(=====);
+
+ // Only equals accepted.
+ three_equals!(abc);
+
+ // Only equals accepted.
+ three_equals!(!!);
+
+ // Only three characters expected.
+ three_equals!(===a);
+}
--- /dev/null
+error: found 2 equal signs, need exactly 3
+ --> $DIR/three-equals.rs:25:5
+ |
+25 | three_equals!(==);
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: input must be: `===`
+
+error: expected EOF, found `=`.
+ --> $DIR/three-equals.rs:28:21
+ |
+28 | three_equals!(=====);
+ | ^^
+ |
+note: last good input was here
+ --> $DIR/three-equals.rs:28:21
+ |
+28 | three_equals!(=====);
+ | ^^
+ = help: input must be: `===`
+
+error: expected `=`, found `abc`.
+ --> $DIR/three-equals.rs:31:19
+ |
+31 | three_equals!(abc);
+ | ^^^
+
+error: expected `=`, found `!`.
+ --> $DIR/three-equals.rs:34:19
+ |
+34 | three_equals!(!!);
+ | ^
+
+error: expected EOF, found `a`.
+ --> $DIR/three-equals.rs:37:22
+ |
+37 | three_equals!(===a);
+ | ^
+ |
+note: last good input was here
+ --> $DIR/three-equals.rs:37:21
+ |
+37 | three_equals!(===a);
+ | ^
+ = help: input must be: `===`
+
+error: aborting due to 5 previous errors
+