]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/diagnostics/plugin.rs
syntax: Remove duplicate span from `token::Ident`
[rust.git] / src / libsyntax / diagnostics / plugin.rs
1 use std::collections::BTreeMap;
2 use std::env;
3
4 use crate::ast::{self, Ident, Name};
5 use crate::source_map;
6 use crate::ext::base::{ExtCtxt, MacEager, MacResult};
7 use crate::ext::build::AstBuilder;
8 use crate::parse::token::{self, Token};
9 use crate::ptr::P;
10 use crate::symbol::kw;
11 use crate::tokenstream::{TokenTree};
12
13 use smallvec::smallvec;
14 use syntax_pos::Span;
15
16 use crate::diagnostics::metadata::output_metadata;
17
18 pub use errors::*;
19
20 // Maximum width of any line in an extended error description (inclusive).
21 const MAX_DESCRIPTION_WIDTH: usize = 80;
22
23 /// Error information type.
24 pub struct ErrorInfo {
25     pub description: Option<Name>,
26     pub use_site: Option<Span>
27 }
28
29 /// Mapping from error codes to metadata.
30 pub type ErrorMap = BTreeMap<Name, ErrorInfo>;
31
32 pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt<'_>,
33                                    span: Span,
34                                    token_tree: &[TokenTree])
35                                    -> Box<dyn MacResult+'cx> {
36     let code = match (token_tree.len(), token_tree.get(0)) {
37         (1, Some(&TokenTree::Token(Token { kind: token::Ident(code, _), .. }))) => code,
38         _ => unreachable!()
39     };
40
41     ecx.parse_sess.registered_diagnostics.with_lock(|diagnostics| {
42         match diagnostics.get_mut(&code) {
43             // Previously used errors.
44             Some(&mut ErrorInfo { description: _, use_site: Some(previous_span) }) => {
45                 ecx.struct_span_warn(span, &format!(
46                     "diagnostic code {} already used", code
47                 )).span_note(previous_span, "previous invocation")
48                   .emit();
49             }
50             // Newly used errors.
51             Some(ref mut info) => {
52                 info.use_site = Some(span);
53             }
54             // Unregistered errors.
55             None => {
56                 ecx.span_err(span, &format!(
57                     "used diagnostic code {} not registered", code
58                 ));
59             }
60         }
61     });
62     MacEager::expr(ecx.expr_tuple(span, Vec::new()))
63 }
64
65 pub fn expand_register_diagnostic<'cx>(ecx: &'cx mut ExtCtxt<'_>,
66                                        span: Span,
67                                        token_tree: &[TokenTree])
68                                        -> Box<dyn MacResult+'cx> {
69     let (code, description) = match (
70         token_tree.len(),
71         token_tree.get(0),
72         token_tree.get(1),
73         token_tree.get(2)
74     ) {
75         (1, Some(&TokenTree::Token(Token { kind: token::Ident(code, _), .. })), None, None) => {
76             (code, None)
77         },
78         (3, Some(&TokenTree::Token(Token { kind: token::Ident(code, _), .. })),
79             Some(&TokenTree::Token(Token { kind: token::Comma, .. })),
80             Some(&TokenTree::Token(Token { kind: token::Literal(token::Lit { symbol, .. }), .. }))) => {
81             (code, Some(symbol))
82         }
83         _ => unreachable!()
84     };
85
86     // Check that the description starts and ends with a newline and doesn't
87     // overflow the maximum line width.
88     description.map(|raw_msg| {
89         let msg = raw_msg.as_str();
90         if !msg.starts_with("\n") || !msg.ends_with("\n") {
91             ecx.span_err(span, &format!(
92                 "description for error code {} doesn't start and end with a newline",
93                 code
94             ));
95         }
96
97         // URLs can be unavoidably longer than the line limit, so we allow them.
98         // Allowed format is: `[name]: https://www.rust-lang.org/`
99         let is_url = |l: &str| l.starts_with("[") && l.contains("]:") && l.contains("http");
100
101         if msg.lines().any(|line| line.len() > MAX_DESCRIPTION_WIDTH && !is_url(line)) {
102             ecx.span_err(span, &format!(
103                 "description for error code {} contains a line longer than {} characters.\n\
104                  if you're inserting a long URL use the footnote style to bypass this check.",
105                 code, MAX_DESCRIPTION_WIDTH
106             ));
107         }
108     });
109     // Add the error to the map.
110     ecx.parse_sess.registered_diagnostics.with_lock(|diagnostics| {
111         let info = ErrorInfo {
112             description,
113             use_site: None
114         };
115         if diagnostics.insert(code, info).is_some() {
116             ecx.span_err(span, &format!(
117                 "diagnostic code {} already registered", code
118             ));
119         }
120     });
121
122     let span = span.apply_mark(ecx.current_expansion.mark);
123
124     let name = Ident::from_str_and_span(&format!("__register_diagnostic_{}", code), span).gensym();
125
126     MacEager::items(smallvec![
127         ecx.item_mod(
128             span,
129             span,
130             name,
131             vec![],
132             vec![],
133         )
134     ])
135 }
136
137 #[allow(deprecated)]
138 pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt<'_>,
139                                           span: Span,
140                                           token_tree: &[TokenTree])
141                                           -> Box<dyn MacResult+'cx> {
142     assert_eq!(token_tree.len(), 3);
143     let (crate_name, ident) = match (&token_tree[0], &token_tree[2]) {
144         (
145             // Crate name.
146             &TokenTree::Token(Token { kind: token::Ident(crate_name, _), .. }),
147             // DIAGNOSTICS ident.
148             &TokenTree::Token(Token { kind: token::Ident(name, _), span })
149         ) => (crate_name, Ident::new(name, span)),
150         _ => unreachable!()
151     };
152
153     // Output error metadata to `tmp/extended-errors/<target arch>/<crate name>.json`
154     if let Ok(target_triple) = env::var("CFG_COMPILER_HOST_TRIPLE") {
155         ecx.parse_sess.registered_diagnostics.with_lock(|diagnostics| {
156             if let Err(e) = output_metadata(ecx,
157                                             &target_triple,
158                                             &crate_name.as_str(),
159                                             diagnostics) {
160                 ecx.span_bug(span, &format!(
161                     "error writing metadata for triple `{}` and crate `{}`, error: {}, \
162                      cause: {:?}",
163                     target_triple, crate_name, e.description(), e.cause()
164                 ));
165             }
166         });
167     } else {
168         ecx.span_err(span, &format!(
169             "failed to write metadata for crate `{}` because $CFG_COMPILER_HOST_TRIPLE is not set",
170             crate_name));
171     }
172
173     // Construct the output expression.
174     let (count, expr) =
175         ecx.parse_sess.registered_diagnostics.with_lock(|diagnostics| {
176             let descriptions: Vec<P<ast::Expr>> =
177                 diagnostics.iter().filter_map(|(&code, info)| {
178                     info.description.map(|description| {
179                         ecx.expr_tuple(span, vec![
180                             ecx.expr_str(span, code),
181                             ecx.expr_str(span, description)
182                         ])
183                     })
184                 }).collect();
185             (descriptions.len(), ecx.expr_vec(span, descriptions))
186         });
187
188     let static_ = ecx.lifetime(span, Ident::with_empty_ctxt(kw::StaticLifetime));
189     let ty_str = ecx.ty_rptr(
190         span,
191         ecx.ty_ident(span, ecx.ident_of("str")),
192         Some(static_),
193         ast::Mutability::Immutable,
194     );
195
196     let ty = ecx.ty(
197         span,
198         ast::TyKind::Array(
199             ecx.ty(
200                 span,
201                 ast::TyKind::Tup(vec![ty_str.clone(), ty_str])
202             ),
203             ast::AnonConst {
204                 id: ast::DUMMY_NODE_ID,
205                 value: ecx.expr_usize(span, count),
206             },
207         ),
208     );
209
210     MacEager::items(smallvec![
211         P(ast::Item {
212             ident,
213             attrs: Vec::new(),
214             id: ast::DUMMY_NODE_ID,
215             node: ast::ItemKind::Const(
216                 ty,
217                 expr,
218             ),
219             vis: source_map::respan(span.shrink_to_lo(), ast::VisibilityKind::Public),
220             span,
221             tokens: None,
222         })
223     ])
224 }