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}
;
24 use util
::small_vector
::SmallVector
;
26 use diagnostics
::metadata
::output_metadata
;
30 // Maximum width of any line in an extended error description (inclusive).
31 const MAX_DESCRIPTION_WIDTH
: usize = 80;
33 /// Error information type.
34 pub struct ErrorInfo
{
35 pub description
: Option
<Name
>,
36 pub use_site
: Option
<Span
>
39 /// Mapping from error codes to metadata.
40 pub type ErrorMap
= BTreeMap
<Name
, ErrorInfo
>;
42 pub fn expand_diagnostic_used
<'cx
>(ecx
: &'cx
mut ExtCtxt
,
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
,
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")
61 Some(ref mut info
) => {
62 info
.use_site
= Some(span
);
64 // Unregistered errors.
66 ecx
.span_err(span
, &format
!(
67 "used diagnostic code {} not registered", code
72 MacEager
::expr(ecx
.expr_tuple(span
, Vec
::new()))
75 pub fn expand_register_diagnostic
<'cx
>(ecx
: &'cx
mut ExtCtxt
,
77 token_tree
: &[TokenTree
])
78 -> Box
<MacResult
+'cx
> {
79 let (code
, description
) = match (
85 (1, Some(&TokenTree
::Token(_
, token
::Ident(ref code
, _
))), None
, None
) => {
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
))
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",
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");
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
119 // Add the error to the map.
120 ecx
.parse_sess
.registered_diagnostics
.with_lock(|diagnostics
| {
121 let info
= ErrorInfo
{
125 if diagnostics
.insert(code
.name
, info
).is_some() {
126 ecx
.span_err(span
, &format
!(
127 "diagnostic code {} already registered", code
131 let sym
= Ident
::with_empty_ctxt(Symbol
::gensym(&format
!(
132 "__register_diagnostic_{}", code
134 MacEager
::items(SmallVector
::many(vec
![
145 pub fn expand_build_diagnostic_array
<'cx
>(ecx
: &'cx
mut ExtCtxt
,
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]) {
153 &TokenTree
::Token(_
, token
::Ident(ref crate_name
, _
)),
154 // DIAGNOSTICS ident.
155 &TokenTree
::Token(_
, token
::Ident(ref name
, _
))
156 ) => (*&crate_name
, name
),
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
,
165 &crate_name
.as_str(),
167 ecx
.span_bug(span
, &format
!(
168 "error writing metadata for triple `{}` and crate `{}`, error: {}, \
170 target_triple
, crate_name
, e
.description(), e
.cause()
175 ecx
.span_err(span
, &format
!(
176 "failed to write metadata for crate `{}` because $CFG_COMPILER_HOST_TRIPLE is not set",
180 // Construct the output expression.
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
)
192 (descriptions
.len(), ecx
.expr_vec(span
, descriptions
))
195 let static_
= ecx
.lifetime(span
, keywords
::StaticLifetime
.ident());
196 let ty_str
= ecx
.ty_rptr(
198 ecx
.ty_ident(span
, ecx
.ident_of("str")),
200 ast
::Mutability
::Immutable
,
208 ast
::TyKind
::Tup(vec
![ty_str
.clone(), ty_str
])
211 id
: ast
::DUMMY_NODE_ID
,
212 value
: ecx
.expr_usize(span
, count
),
217 MacEager
::items(SmallVector
::many(vec
![
221 id
: ast
::DUMMY_NODE_ID
,
222 node
: ast
::ItemKind
::Const(
226 vis
: codemap
::respan(span
.shrink_to_lo(), ast
::VisibilityKind
::Public
),