about summary refs log tree commit diff
path: root/src/libsyntax/diagnostics/plugin.rs
diff options
context:
space:
mode:
authorMichael Sproul <micsproul@gmail.com>2015-04-28 12:48:22 +1000
committerMichael Sproul <micsproul@gmail.com>2015-04-30 08:59:53 +1000
commitd27230bb6d45eeabf13c0d9e5605636840af01c2 (patch)
treecf74549e405d9d3c287e5305de7d036c25d438d6 /src/libsyntax/diagnostics/plugin.rs
parent54d65092a49eba959b5cd7a1c4b2e43b3fedba1f (diff)
downloadrust-d27230bb6d45eeabf13c0d9e5605636840af01c2.tar.gz
rust-d27230bb6d45eeabf13c0d9e5605636840af01c2.zip
Add metadata output to the diagnostics system.
Diagnostic errors are now checked for uniqueness across the compiler and
error metadata is written to JSON files.
Diffstat (limited to 'src/libsyntax/diagnostics/plugin.rs')
-rw-r--r--src/libsyntax/diagnostics/plugin.rs96
1 files changed, 62 insertions, 34 deletions
diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs
index 6de4edafa0b..16841bd9039 100644
--- a/src/libsyntax/diagnostics/plugin.rs
+++ b/src/libsyntax/diagnostics/plugin.rs
@@ -14,6 +14,7 @@ use std::collections::BTreeMap;
 use ast;
 use ast::{Ident, Name, TokenTree};
 use codemap::Span;
+use diagnostics::metadata::{check_uniqueness, output_metadata, Duplicate};
 use ext::base::{ExtCtxt, MacEager, MacResult};
 use ext::build::AstBuilder;
 use parse::token;
@@ -24,32 +25,28 @@ use util::small_vector::SmallVector;
 const MAX_DESCRIPTION_WIDTH: usize = 80;
 
 thread_local! {
-    static REGISTERED_DIAGNOSTICS: RefCell<BTreeMap<Name, Option<Name>>> = {
+    static REGISTERED_DIAGNOSTICS: RefCell<ErrorMap> = {
         RefCell::new(BTreeMap::new())
     }
 }
-thread_local! {
-    static USED_DIAGNOSTICS: RefCell<BTreeMap<Name, Span>> = {
-        RefCell::new(BTreeMap::new())
-    }
+
+/// Error information type.
+pub struct ErrorInfo {
+    pub description: Option<Name>,
+    pub use_site: Option<Span>
 }
 
+/// Mapping from error codes to metadata.
+pub type ErrorMap = BTreeMap<Name, ErrorInfo>;
+
 fn with_registered_diagnostics<T, F>(f: F) -> T where
-    F: FnOnce(&mut BTreeMap<Name, Option<Name>>) -> T,
+    F: FnOnce(&mut ErrorMap) -> T,
 {
     REGISTERED_DIAGNOSTICS.with(move |slot| {
         f(&mut *slot.borrow_mut())
     })
 }
 
-fn with_used_diagnostics<T, F>(f: F) -> T where
-    F: FnOnce(&mut BTreeMap<Name, Span>) -> T,
-{
-    USED_DIAGNOSTICS.with(move |slot| {
-        f(&mut *slot.borrow_mut())
-    })
-}
-
 pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt,
                                    span: Span,
                                    token_tree: &[TokenTree])
@@ -58,23 +55,26 @@ pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt,
         (1, Some(&ast::TtToken(_, token::Ident(code, _)))) => code,
         _ => unreachable!()
     };
-    with_used_diagnostics(|diagnostics| {
-        match diagnostics.insert(code.name, span) {
-            Some(previous_span) => {
+
+    with_registered_diagnostics(|diagnostics| {
+        match diagnostics.get_mut(&code.name) {
+            // Previously used errors.
+            Some(&mut ErrorInfo { description: _, use_site: Some(previous_span) }) => {
                 ecx.span_warn(span, &format!(
                     "diagnostic code {} already used", &token::get_ident(code)
                 ));
                 ecx.span_note(previous_span, "previous invocation");
-            },
-            None => ()
-        }
-        ()
-    });
-    with_registered_diagnostics(|diagnostics| {
-        if !diagnostics.contains_key(&code.name) {
-            ecx.span_err(span, &format!(
-                "used diagnostic code {} not registered", &token::get_ident(code)
-            ));
+            }
+            // Newly used errors.
+            Some(ref mut info) => {
+                info.use_site = Some(span);
+            }
+            // Unregistered errors.
+            None => {
+                ecx.span_err(span, &format!(
+                    "used diagnostic code {} not registered", &token::get_ident(code)
+                ));
+            }
         }
     });
     MacEager::expr(ecx.expr_tuple(span, Vec::new()))
@@ -116,10 +116,14 @@ pub fn expand_register_diagnostic<'cx>(ecx: &'cx mut ExtCtxt,
                 token::get_ident(*code), MAX_DESCRIPTION_WIDTH
             ));
         }
-        raw_msg
     });
+    // Add the error to the map.
     with_registered_diagnostics(|diagnostics| {
-        if diagnostics.insert(code.name, description).is_some() {
+        let info = ErrorInfo {
+            description: description,
+            use_site: None
+        };
+        if diagnostics.insert(code.name, info).is_some() {
             ecx.span_err(span, &format!(
                 "diagnostic code {} already registered", &token::get_ident(*code)
             ));
@@ -143,19 +147,43 @@ pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt,
                                           span: Span,
                                           token_tree: &[TokenTree])
                                           -> Box<MacResult+'cx> {
-    let name = match (token_tree.len(), token_tree.get(0)) {
-        (1, Some(&ast::TtToken(_, token::Ident(ref name, _)))) => name,
+    assert_eq!(token_tree.len(), 3);
+    let (crate_name, name) = match (&token_tree[0], &token_tree[2]) {
+        (
+            // Crate name.
+            &ast::TtToken(_, token::Ident(ref crate_name, _)),
+            // DIAGNOSTICS ident.
+            &ast::TtToken(_, token::Ident(ref name, _))
+        ) => (crate_name.as_str(), name),
         _ => unreachable!()
     };
 
+    // Check uniqueness of errors and output metadata.
+    with_registered_diagnostics(|diagnostics| {
+        match check_uniqueness(crate_name, &*diagnostics) {
+            Ok(Duplicate(err, location)) => {
+                ecx.span_err(span, &format!(
+                    "error {} from `{}' also found in `{}'",
+                    err, crate_name, location
+                ));
+            },
+            Ok(_) => (),
+            Err(e) => panic!("{}", e.description())
+        }
+
+        output_metadata(&*ecx, crate_name, &*diagnostics).ok().expect("metadata output error");
+    });
+
+    // Construct the output expression.
     let (count, expr) =
         with_registered_diagnostics(|diagnostics| {
             let descriptions: Vec<P<ast::Expr>> =
-                diagnostics.iter().filter_map(|(code, description)| {
-                    description.map(|description| {
+                diagnostics.iter().filter_map(|(code, info)| {
+                    info.description.map(|description| {
                         ecx.expr_tuple(span, vec![
                             ecx.expr_str(span, token::get_name(*code)),
-                            ecx.expr_str(span, token::get_name(description))])
+                            ecx.expr_str(span, token::get_name(description))
+                        ])
                     })
                 }).collect();
             (descriptions.len(), ecx.expr_vec(span, descriptions))