1 use std::collections::BTreeMap;
4 use crate::ast::{self, Ident, Name};
6 use crate::ext::base::{ExtCtxt, MacEager, MacResult};
7 use crate::ext::build::AstBuilder;
8 use crate::parse::token::{self, Token};
10 use crate::symbol::kw;
11 use crate::tokenstream::{TokenTree};
13 use smallvec::smallvec;
16 use crate::diagnostics::metadata::output_metadata;
20 // Maximum width of any line in an extended error description (inclusive).
21 const MAX_DESCRIPTION_WIDTH: usize = 80;
23 /// Error information type.
24 pub struct ErrorInfo {
25 pub description: Option<Name>,
26 pub use_site: Option<Span>
29 /// Mapping from error codes to metadata.
30 pub type ErrorMap = BTreeMap<Name, ErrorInfo>;
32 pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt<'_>,
34 token_tree: &[TokenTree])
35 -> Box<dyn MacResult+'cx> {
36 let code = match token_tree {
38 TokenTree::Token(Token { kind: token::Ident(code, _), .. })
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")
53 Some(ref mut info) => {
54 info.use_site = Some(span);
56 // Unregistered errors.
58 ecx.span_err(span, &format!(
59 "used diagnostic code {} not registered", code
64 MacEager::expr(ecx.expr_tuple(span, Vec::new()))
67 pub fn expand_register_diagnostic<'cx>(ecx: &'cx mut ExtCtxt<'_>,
69 token_tree: &[TokenTree])
70 -> Box<dyn MacResult+'cx> {
71 let (code, description) = match token_tree {
73 TokenTree::Token(Token { kind: token::Ident(code, _), .. })
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, .. }), ..})
82 (*code, Some(*symbol))
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",
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");
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
110 // Add the error to the map.
111 ecx.parse_sess.registered_diagnostics.with_lock(|diagnostics| {
112 let info = ErrorInfo {
116 if diagnostics.insert(code, info).is_some() {
117 ecx.span_err(span, &format!(
118 "diagnostic code {} already registered", code
123 MacEager::items(smallvec![])
127 pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt<'_>,
129 token_tree: &[TokenTree])
130 -> Box<dyn MacResult+'cx> {
131 assert_eq!(token_tree.len(), 3);
132 let (crate_name, ident) = match (&token_tree[0], &token_tree[2]) {
135 &TokenTree::Token(Token { kind: token::Ident(crate_name, _), .. }),
136 // DIAGNOSTICS ident.
137 &TokenTree::Token(Token { kind: token::Ident(name, _), span })
138 ) => (crate_name, Ident::new(name, span)),
142 // Output error metadata to `tmp/extended-errors/<target arch>/<crate name>.json`
143 if let Ok(target_triple) = env::var("CFG_COMPILER_HOST_TRIPLE") {
144 ecx.parse_sess.registered_diagnostics.with_lock(|diagnostics| {
145 if let Err(e) = output_metadata(ecx,
147 &crate_name.as_str(),
149 ecx.span_bug(span, &format!(
150 "error writing metadata for triple `{}` and crate `{}`, error: {}, \
152 target_triple, crate_name, e.description(), e.cause()
157 ecx.span_err(span, &format!(
158 "failed to write metadata for crate `{}` because $CFG_COMPILER_HOST_TRIPLE is not set",
162 // Construct the output expression.
164 ecx.parse_sess.registered_diagnostics.with_lock(|diagnostics| {
165 let descriptions: Vec<P<ast::Expr>> =
166 diagnostics.iter().filter_map(|(&code, info)| {
167 info.description.map(|description| {
168 ecx.expr_tuple(span, vec![
169 ecx.expr_str(span, code),
170 ecx.expr_str(span, description)
174 (descriptions.len(), ecx.expr_vec(span, descriptions))
177 let static_ = ecx.lifetime(span, Ident::with_empty_ctxt(kw::StaticLifetime));
178 let ty_str = ecx.ty_rptr(
180 ecx.ty_ident(span, ecx.ident_of("str")),
182 ast::Mutability::Immutable,
190 ast::TyKind::Tup(vec![ty_str.clone(), ty_str])
193 id: ast::DUMMY_NODE_ID,
194 value: ecx.expr_usize(span, count),
199 MacEager::items(smallvec![
203 id: ast::DUMMY_NODE_ID,
204 node: ast::ItemKind::Const(
208 vis: source_map::respan(span.shrink_to_lo(), ast::VisibilityKind::Public),