]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/diagnostics/plugin.rs
Auto merge of #30641 - tsion:match-range, r=eddyb
[rust.git] / src / libsyntax / diagnostics / plugin.rs
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;
12 use std::collections::BTreeMap;
13 use std::env;
14
15 use ast;
16 use ast::{Ident, Name, TokenTree};
17 use codemap::Span;
18 use ext::base::{ExtCtxt, MacEager, MacResult};
19 use ext::build::AstBuilder;
20 use parse::token;
21 use ptr::P;
22 use util::small_vector::SmallVector;
23
24 use diagnostics::metadata::output_metadata;
25
26 // Maximum width of any line in an extended error description (inclusive).
27 const MAX_DESCRIPTION_WIDTH: usize = 80;
28
29 thread_local! {
30     static REGISTERED_DIAGNOSTICS: RefCell<ErrorMap> = {
31         RefCell::new(BTreeMap::new())
32     }
33 }
34
35 /// Error information type.
36 pub struct ErrorInfo {
37     pub description: Option<Name>,
38     pub use_site: Option<Span>
39 }
40
41 /// Mapping from error codes to metadata.
42 pub type ErrorMap = BTreeMap<Name, ErrorInfo>;
43
44 fn with_registered_diagnostics<T, F>(f: F) -> T where
45     F: FnOnce(&mut ErrorMap) -> T,
46 {
47     REGISTERED_DIAGNOSTICS.with(move |slot| {
48         f(&mut *slot.borrow_mut())
49     })
50 }
51
52 pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt,
53                                    span: Span,
54                                    token_tree: &[TokenTree])
55                                    -> Box<MacResult+'cx> {
56     let code = match (token_tree.len(), token_tree.get(0)) {
57         (1, Some(&TokenTree::Token(_, token::Ident(code, _)))) => code,
58         _ => unreachable!()
59     };
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) }) => {
65                 ecx.struct_span_warn(span, &format!(
66                     "diagnostic code {} already used", code
67                 )).span_note(previous_span, "previous invocation")
68                   .emit();
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!(
77                     "used diagnostic code {} not registered", code
78                 ));
79             }
80         }
81     });
82     MacEager::expr(ecx.expr_tuple(span, Vec::new()))
83 }
84
85 pub fn expand_register_diagnostic<'cx>(ecx: &'cx mut ExtCtxt,
86                                        span: Span,
87                                        token_tree: &[TokenTree])
88                                        -> Box<MacResult+'cx> {
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(&TokenTree::Token(_, token::Ident(ref code, _))), None, None) => {
96             (code, None)
97         },
98         (3, Some(&TokenTree::Token(_, token::Ident(ref code, _))),
99             Some(&TokenTree::Token(_, token::Comma)),
100             Some(&TokenTree::Token(_, token::Literal(token::StrRaw(description, _), None)))) => {
101             (code, Some(description))
102         }
103         _ => unreachable!()
104     };
105
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",
113                 code
114             ));
115         }
116
117         // URLs can be unavoidably longer than the line limit, so we allow them.
118         // Allowed format is: `[name]: https://www.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)) {
122             ecx.span_err(span, &format!(
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.",
125                 code, MAX_DESCRIPTION_WIDTH
126             ));
127         }
128     });
129     // Add the error to the map.
130     with_registered_diagnostics(|diagnostics| {
131         let info = ErrorInfo {
132             description: description,
133             use_site: None
134         };
135         if diagnostics.insert(code.name, info).is_some() {
136             ecx.span_err(span, &format!(
137                 "diagnostic code {} already registered", code
138             ));
139         }
140     });
141     let sym = Ident::with_empty_ctxt(token::gensym(&format!(
142         "__register_diagnostic_{}", code
143     )));
144     MacEager::items(SmallVector::many(vec![
145         ecx.item_mod(
146             span,
147             span,
148             sym,
149             Vec::new(),
150             Vec::new()
151         )
152     ]))
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> {
159     assert_eq!(token_tree.len(), 3);
160     let (crate_name, name) = match (&token_tree[0], &token_tree[2]) {
161         (
162             // Crate name.
163             &TokenTree::Token(_, token::Ident(ref crate_name, _)),
164             // DIAGNOSTICS ident.
165             &TokenTree::Token(_, token::Ident(ref name, _))
166         ) => (*&crate_name, name),
167         _ => unreachable!()
168     };
169
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
174     with_registered_diagnostics(|diagnostics| {
175         if let Err(e) = output_metadata(ecx,
176                                         &target_triple,
177                                         &crate_name.name.as_str(),
178                                         &diagnostics) {
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             ));
183         }
184     });
185
186     // Construct the output expression.
187     let (count, expr) =
188         with_registered_diagnostics(|diagnostics| {
189             let descriptions: Vec<P<ast::Expr>> =
190                 diagnostics.iter().filter_map(|(code, info)| {
191                     info.description.map(|description| {
192                         ecx.expr_tuple(span, vec![
193                             ecx.expr_str(span, code.as_str()),
194                             ecx.expr_str(span, description.as_str())
195                         ])
196                     })
197                 }).collect();
198             (descriptions.len(), ecx.expr_vec(span, descriptions))
199         });
200
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,
225             node: ast::ItemConst(
226                 ty,
227                 expr,
228             ),
229             vis: ast::Public,
230             span: span,
231         })
232     ]))
233 }