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