]>
Commit | Line | Data |
---|---|---|
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 | ||
11 | use std::cell::RefCell; | |
85aaf69f | 12 | use std::collections::BTreeMap; |
62682a34 | 13 | use std::env; |
85aaf69f | 14 | |
1a4d82fc JJ |
15 | use ast; |
16 | use ast::{Ident, Name, TokenTree}; | |
17 | use codemap::Span; | |
c34b1796 | 18 | use ext::base::{ExtCtxt, MacEager, MacResult}; |
1a4d82fc JJ |
19 | use ext::build::AstBuilder; |
20 | use parse::token; | |
21 | use ptr::P; | |
c34b1796 | 22 | use util::small_vector::SmallVector; |
1a4d82fc | 23 | |
62682a34 SL |
24 | use diagnostics::metadata::output_metadata; |
25 | ||
d9579d0f AL |
26 | // Maximum width of any line in an extended error description (inclusive). |
27 | const MAX_DESCRIPTION_WIDTH: usize = 80; | |
28 | ||
1a4d82fc | 29 | thread_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. | |
36 | pub 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. |
42 | pub type ErrorMap = BTreeMap<Name, ErrorInfo>; | |
43 | ||
1a4d82fc | 44 | fn 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 |
52 | pub 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 | ||
85 | pub 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 | ||
155 | pub 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 | } |