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.
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.
11 use std
::collections
::BTreeMap
;
15 use ast
::{Ident, Name}
;
18 use ext
::base
::{ExtCtxt, MacEager, MacResult}
;
19 use ext
::build
::AstBuilder
;
22 use symbol
::{keywords, Symbol}
;
23 use tokenstream
::{TokenTree}
;
25 use diagnostics
::metadata
::output_metadata
;
29 // Maximum width of any line in an extended error description (inclusive).
30 const MAX_DESCRIPTION_WIDTH
: usize = 80;
32 /// Error information type.
33 pub struct ErrorInfo
{
34 pub description
: Option
<Name
>,
35 pub use_site
: Option
<Span
>
38 /// Mapping from error codes to metadata.
39 pub type ErrorMap
= BTreeMap
<Name
, ErrorInfo
>;
41 pub fn expand_diagnostic_used
<'cx
>(ecx
: &'cx
mut ExtCtxt
,
43 token_tree
: &[TokenTree
])
44 -> Box
<dyn MacResult
+'cx
> {
45 let code
= match (token_tree
.len(), token_tree
.get(0)) {
46 (1, Some(&TokenTree
::Token(_
, token
::Ident(code
, _
)))) => code
,
50 ecx
.parse_sess
.registered_diagnostics
.with_lock(|diagnostics
| {
51 match diagnostics
.get_mut(&code
.name
) {
52 // Previously used errors.
53 Some(&mut ErrorInfo { description: _, use_site: Some(previous_span) }
) => {
54 ecx
.struct_span_warn(span
, &format
!(
55 "diagnostic code {} already used", code
56 )).span_note(previous_span
, "previous invocation")
60 Some(ref mut info
) => {
61 info
.use_site
= Some(span
);
63 // Unregistered errors.
65 ecx
.span_err(span
, &format
!(
66 "used diagnostic code {} not registered", code
71 MacEager
::expr(ecx
.expr_tuple(span
, Vec
::new()))
74 pub fn expand_register_diagnostic
<'cx
>(ecx
: &'cx
mut ExtCtxt
,
76 token_tree
: &[TokenTree
])
77 -> Box
<dyn MacResult
+'cx
> {
78 let (code
, description
) = match (
84 (1, Some(&TokenTree
::Token(_
, token
::Ident(ref code
, _
))), None
, None
) => {
87 (3, Some(&TokenTree
::Token(_
, token
::Ident(ref code
, _
))),
88 Some(&TokenTree
::Token(_
, token
::Comma
)),
89 Some(&TokenTree
::Token(_
, token
::Literal(token
::StrRaw(description
, _
), None
)))) => {
90 (code
, Some(description
))
95 // Check that the description starts and ends with a newline and doesn't
96 // overflow the maximum line width.
97 description
.map(|raw_msg
| {
98 let msg
= raw_msg
.as_str();
99 if !msg
.starts_with("\n") || !msg
.ends_with("\n") {
100 ecx
.span_err(span
, &format
!(
101 "description for error code {} doesn't start and end with a newline",
106 // URLs can be unavoidably longer than the line limit, so we allow them.
107 // Allowed format is: `[name]: https://www.rust-lang.org/`
108 let is_url
= |l
: &str| l
.starts_with("[") && l
.contains("]:") && l
.contains("http");
110 if msg
.lines().any(|line
| line
.len() > MAX_DESCRIPTION_WIDTH
&& !is_url(line
)) {
111 ecx
.span_err(span
, &format
!(
112 "description for error code {} contains a line longer than {} characters.\n\
113 if you're inserting a long URL use the footnote style to bypass this check.",
114 code
, MAX_DESCRIPTION_WIDTH
118 // Add the error to the map.
119 ecx
.parse_sess
.registered_diagnostics
.with_lock(|diagnostics
| {
120 let info
= ErrorInfo
{
124 if diagnostics
.insert(code
.name
, info
).is_some() {
125 ecx
.span_err(span
, &format
!(
126 "diagnostic code {} already registered", code
130 let sym
= Ident
::with_empty_ctxt(Symbol
::gensym(&format
!(
131 "__register_diagnostic_{}", code
133 MacEager
::items(smallvec
![
144 pub fn expand_build_diagnostic_array
<'cx
>(ecx
: &'cx
mut ExtCtxt
,
146 token_tree
: &[TokenTree
])
147 -> Box
<dyn MacResult
+'cx
> {
148 assert_eq
!(token_tree
.len(), 3);
149 let (crate_name
, name
) = match (&token_tree
[0], &token_tree
[2]) {
152 &TokenTree
::Token(_
, token
::Ident(ref crate_name
, _
)),
153 // DIAGNOSTICS ident.
154 &TokenTree
::Token(_
, token
::Ident(ref name
, _
))
155 ) => (*&crate_name
, name
),
159 // Output error metadata to `tmp/extended-errors/<target arch>/<crate name>.json`
160 if let Ok(target_triple
) = env
::var("CFG_COMPILER_HOST_TRIPLE") {
161 ecx
.parse_sess
.registered_diagnostics
.with_lock(|diagnostics
| {
162 if let Err(e
) = output_metadata(ecx
,
164 &crate_name
.as_str(),
166 ecx
.span_bug(span
, &format
!(
167 "error writing metadata for triple `{}` and crate `{}`, error: {}, \
169 target_triple
, crate_name
, e
.description(), e
.cause()
174 ecx
.span_err(span
, &format
!(
175 "failed to write metadata for crate `{}` because $CFG_COMPILER_HOST_TRIPLE is not set",
179 // Construct the output expression.
181 ecx
.parse_sess
.registered_diagnostics
.with_lock(|diagnostics
| {
182 let descriptions
: Vec
<P
<ast
::Expr
>> =
183 diagnostics
.iter().filter_map(|(&code
, info
)| {
184 info
.description
.map(|description
| {
185 ecx
.expr_tuple(span
, vec
![
186 ecx
.expr_str(span
, code
),
187 ecx
.expr_str(span
, description
)
191 (descriptions
.len(), ecx
.expr_vec(span
, descriptions
))
194 let static_
= ecx
.lifetime(span
, keywords
::StaticLifetime
.ident());
195 let ty_str
= ecx
.ty_rptr(
197 ecx
.ty_ident(span
, ecx
.ident_of("str")),
199 ast
::Mutability
::Immutable
,
207 ast
::TyKind
::Tup(vec
![ty_str
.clone(), ty_str
])
210 id
: ast
::DUMMY_NODE_ID
,
211 value
: ecx
.expr_usize(span
, count
),
216 MacEager
::items(smallvec
![
220 id
: ast
::DUMMY_NODE_ID
,
221 node
: ast
::ItemKind
::Const(
225 vis
: source_map
::respan(span
.shrink_to_lo(), ast
::VisibilityKind
::Public
),