]> git.proxmox.com Git - rustc.git/blob - src/libsyntax/diagnostics/plugin.rs
New upstream version 1.34.2+dfsg1
[rustc.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;
9 use crate::ptr::P;
10 use crate::symbol::{keywords, Symbol};
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::Ident(code, _)))) => code,
38 _ => unreachable!()
39 };
40
41 ecx.parse_sess.registered_diagnostics.with_lock(|diagnostics| {
42 match diagnostics.get_mut(&code.name) {
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::Ident(ref code, _))), None, None) => {
76 (code, None)
77 },
78 (3, Some(&TokenTree::Token(_, token::Ident(ref code, _))),
79 Some(&TokenTree::Token(_, token::Comma)),
80 Some(&TokenTree::Token(_, token::Literal(token::StrRaw(description, _), None)))) => {
81 (code, Some(description))
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.name, 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 sym = Ident::new(Symbol::gensym(&format!("__register_diagnostic_{}", code)), span);
125
126 MacEager::items(smallvec![
127 ecx.item_mod(
128 span,
129 span,
130 sym,
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, name) = match (&token_tree[0], &token_tree[2]) {
144 (
145 // Crate name.
146 &TokenTree::Token(_, token::Ident(ref crate_name, _)),
147 // DIAGNOSTICS ident.
148 &TokenTree::Token(_, token::Ident(ref name, _))
149 ) => (*&crate_name, name),
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, keywords::StaticLifetime.ident());
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: *name,
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 }