]> git.proxmox.com Git - rustc.git/blame - src/libsyntax/diagnostics/plugin.rs
Imported Upstream version 1.3.0+dfsg1
[rustc.git] / src / libsyntax / diagnostics / plugin.rs
CommitLineData
1a4d82fc
JJ
1// Copyright 2014 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
11use std::cell::RefCell;
85aaf69f 12use std::collections::BTreeMap;
62682a34 13use std::env;
85aaf69f 14
1a4d82fc
JJ
15use ast;
16use ast::{Ident, Name, TokenTree};
17use codemap::Span;
c34b1796 18use ext::base::{ExtCtxt, MacEager, MacResult};
1a4d82fc
JJ
19use ext::build::AstBuilder;
20use parse::token;
21use ptr::P;
c34b1796 22use util::small_vector::SmallVector;
1a4d82fc 23
62682a34
SL
24use diagnostics::metadata::output_metadata;
25
d9579d0f
AL
26// Maximum width of any line in an extended error description (inclusive).
27const MAX_DESCRIPTION_WIDTH: usize = 80;
28
1a4d82fc 29thread_local! {
d9579d0f 30 static REGISTERED_DIAGNOSTICS: RefCell<ErrorMap> = {
85aaf69f 31 RefCell::new(BTreeMap::new())
1a4d82fc
JJ
32 }
33}
d9579d0f
AL
34
35/// Error information type.
36pub struct ErrorInfo {
37 pub description: Option<Name>,
38 pub use_site: Option<Span>
1a4d82fc
JJ
39}
40
d9579d0f
AL
41/// Mapping from error codes to metadata.
42pub type ErrorMap = BTreeMap<Name, ErrorInfo>;
43
1a4d82fc 44fn with_registered_diagnostics<T, F>(f: F) -> T where
d9579d0f 45 F: FnOnce(&mut ErrorMap) -> T,
1a4d82fc
JJ
46{
47 REGISTERED_DIAGNOSTICS.with(move |slot| {
48 f(&mut *slot.borrow_mut())
49 })
50}
51
1a4d82fc
JJ
52pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt,
53 span: Span,
54 token_tree: &[TokenTree])
55 -> Box<MacResult+'cx> {
d9579d0f
AL
56 let code = match (token_tree.len(), token_tree.get(0)) {
57 (1, Some(&ast::TtToken(_, token::Ident(code, _)))) => code,
1a4d82fc
JJ
58 _ => unreachable!()
59 };
d9579d0f
AL
60
61 with_registered_diagnostics(|diagnostics| {
62 match diagnostics.get_mut(&code.name) {
63 // Previously used errors.
64 Some(&mut ErrorInfo { description: _, use_site: Some(previous_span) }) => {
1a4d82fc 65 ecx.span_warn(span, &format!(
c1a9b12d 66 "diagnostic code {} already used", code
85aaf69f 67 ));
1a4d82fc 68 ecx.span_note(previous_span, "previous invocation");
d9579d0f
AL
69 }
70 // Newly used errors.
71 Some(ref mut info) => {
72 info.use_site = Some(span);
73 }
74 // Unregistered errors.
75 None => {
76 ecx.span_err(span, &format!(
c1a9b12d 77 "used diagnostic code {} not registered", code
d9579d0f
AL
78 ));
79 }
85aaf69f
SL
80 }
81 });
d9579d0f 82 MacEager::expr(ecx.expr_tuple(span, Vec::new()))
1a4d82fc
JJ
83}
84
85pub fn expand_register_diagnostic<'cx>(ecx: &'cx mut ExtCtxt,
86 span: Span,
87 token_tree: &[TokenTree])
88 -> Box<MacResult+'cx> {
d9579d0f
AL
89 let (code, description) = match (
90 token_tree.len(),
91 token_tree.get(0),
92 token_tree.get(1),
93 token_tree.get(2)
94 ) {
95 (1, Some(&ast::TtToken(_, token::Ident(ref code, _))), None, None) => {
1a4d82fc
JJ
96 (code, None)
97 },
d9579d0f
AL
98 (3, Some(&ast::TtToken(_, token::Ident(ref code, _))),
99 Some(&ast::TtToken(_, token::Comma)),
100 Some(&ast::TtToken(_, token::Literal(token::StrRaw(description, _), None)))) => {
1a4d82fc
JJ
101 (code, Some(description))
102 }
103 _ => unreachable!()
104 };
62682a34 105
d9579d0f
AL
106 // Check that the description starts and ends with a newline and doesn't
107 // overflow the maximum line width.
108 description.map(|raw_msg| {
109 let msg = raw_msg.as_str();
110 if !msg.starts_with("\n") || !msg.ends_with("\n") {
111 ecx.span_err(span, &format!(
112 "description for error code {} doesn't start and end with a newline",
c1a9b12d 113 code
d9579d0f
AL
114 ));
115 }
62682a34
SL
116
117 // URLs can be unavoidably longer than the line limit, so we allow them.
118 // Allowed format is: `[name]: http://rust-lang.org/`
119 let is_url = |l: &str| l.starts_with('[') && l.contains("]:") && l.contains("http");
120
121 if msg.lines().any(|line| line.len() > MAX_DESCRIPTION_WIDTH && !is_url(line)) {
d9579d0f 122 ecx.span_err(span, &format!(
62682a34
SL
123 "description for error code {} contains a line longer than {} characters.\n\
124 if you're inserting a long URL use the footnote style to bypass this check.",
c1a9b12d 125 code, MAX_DESCRIPTION_WIDTH
d9579d0f
AL
126 ));
127 }
128 });
129 // Add the error to the map.
1a4d82fc 130 with_registered_diagnostics(|diagnostics| {
d9579d0f
AL
131 let info = ErrorInfo {
132 description: description,
133 use_site: None
134 };
135 if diagnostics.insert(code.name, info).is_some() {
1a4d82fc 136 ecx.span_err(span, &format!(
c1a9b12d 137 "diagnostic code {} already registered", code
85aaf69f 138 ));
1a4d82fc
JJ
139 }
140 });
c1a9b12d
SL
141 let sym = Ident::new(token::gensym(&format!(
142 "__register_diagnostic_{}", code
85aaf69f 143 )));
d9579d0f
AL
144 MacEager::items(SmallVector::many(vec![
145 ecx.item_mod(
146 span,
147 span,
148 sym,
149 Vec::new(),
150 Vec::new()
151 )
152 ]))
1a4d82fc
JJ
153}
154
155pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt,
156 span: Span,
157 token_tree: &[TokenTree])
158 -> Box<MacResult+'cx> {
d9579d0f
AL
159 assert_eq!(token_tree.len(), 3);
160 let (crate_name, name) = match (&token_tree[0], &token_tree[2]) {
161 (
162 // Crate name.
163 &ast::TtToken(_, token::Ident(ref crate_name, _)),
164 // DIAGNOSTICS ident.
165 &ast::TtToken(_, token::Ident(ref name, _))
c1a9b12d 166 ) => (*&crate_name, name),
1a4d82fc
JJ
167 _ => unreachable!()
168 };
169
62682a34
SL
170 // Output error metadata to `tmp/extended-errors/<target arch>/<crate name>.json`
171 let target_triple = env::var("CFG_COMPILER_HOST_TRIPLE")
172 .ok().expect("unable to determine target arch from $CFG_COMPILER_HOST_TRIPLE");
173
d9579d0f 174 with_registered_diagnostics(|diagnostics| {
c1a9b12d
SL
175 if let Err(e) = output_metadata(ecx,
176 &target_triple,
177 &crate_name.name.as_str(),
178 &diagnostics) {
62682a34
SL
179 ecx.span_bug(span, &format!(
180 "error writing metadata for triple `{}` and crate `{}`, error: {}, cause: {:?}",
181 target_triple, crate_name, e.description(), e.cause()
182 ));
d9579d0f 183 }
d9579d0f
AL
184 });
185
186 // Construct the output expression.
1a4d82fc
JJ
187 let (count, expr) =
188 with_registered_diagnostics(|diagnostics| {
189 let descriptions: Vec<P<ast::Expr>> =
d9579d0f
AL
190 diagnostics.iter().filter_map(|(code, info)| {
191 info.description.map(|description| {
1a4d82fc 192 ecx.expr_tuple(span, vec![
c1a9b12d
SL
193 ecx.expr_str(span, code.as_str()),
194 ecx.expr_str(span, description.as_str())
d9579d0f 195 ])
1a4d82fc
JJ
196 })
197 }).collect();
198 (descriptions.len(), ecx.expr_vec(span, descriptions))
199 });
200
d9579d0f
AL
201 let static_ = ecx.lifetime(span, ecx.name_of("'static"));
202 let ty_str = ecx.ty_rptr(
203 span,
204 ecx.ty_ident(span, ecx.ident_of("str")),
205 Some(static_),
206 ast::MutImmutable,
207 );
208
209 let ty = ecx.ty(
210 span,
211 ast::TyFixedLengthVec(
212 ecx.ty(
213 span,
214 ast::TyTup(vec![ty_str.clone(), ty_str])
215 ),
216 ecx.expr_usize(span, count),
217 ),
218 );
219
220 MacEager::items(SmallVector::many(vec![
221 P(ast::Item {
222 ident: name.clone(),
223 attrs: Vec::new(),
224 id: ast::DUMMY_NODE_ID,
62682a34 225 node: ast::ItemConst(
d9579d0f 226 ty,
d9579d0f
AL
227 expr,
228 ),
229 vis: ast::Public,
230 span: span,
231 })
232 ]))
1a4d82fc 233}