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