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
::cell
::RefCell
;
12 use std
::collections
::BTreeMap
;
16 use ast
::{Ident, Name, TokenTree}
;
18 use ext
::base
::{ExtCtxt, MacEager, MacResult}
;
19 use ext
::build
::AstBuilder
;
22 use util
::small_vector
::SmallVector
;
24 use diagnostics
::metadata
::output_metadata
;
26 // Maximum width of any line in an extended error description (inclusive).
27 const MAX_DESCRIPTION_WIDTH
: usize = 80;
30 static REGISTERED_DIAGNOSTICS
: RefCell
<ErrorMap
> = {
31 RefCell
::new(BTreeMap
::new())
35 /// Error information type.
36 pub struct ErrorInfo
{
37 pub description
: Option
<Name
>,
38 pub use_site
: Option
<Span
>
41 /// Mapping from error codes to metadata.
42 pub type ErrorMap
= BTreeMap
<Name
, ErrorInfo
>;
44 fn with_registered_diagnostics
<T
, F
>(f
: F
) -> T
where
45 F
: FnOnce(&mut ErrorMap
) -> T
,
47 REGISTERED_DIAGNOSTICS
.with(move |slot
| {
48 f(&mut *slot
.borrow_mut())
52 pub fn expand_diagnostic_used
<'cx
>(ecx
: &'cx
mut ExtCtxt
,
54 token_tree
: &[TokenTree
])
55 -> Box
<MacResult
+'cx
> {
56 let code
= match (token_tree
.len(), token_tree
.get(0)) {
57 (1, Some(&ast
::TtToken(_
, token
::Ident(code
, _
)))) => code
,
61 with_registered_diagnostics(|diagnostics
| {
62 match diagnostics
.get_mut(&code
.name
) {
63 // Previously used errors.
64 Some(&mut ErrorInfo { description: _, use_site: Some(previous_span) }
) => {
65 ecx
.span_warn(span
, &format
!(
66 "diagnostic code {} already used", code
68 ecx
.span_note(previous_span
, "previous invocation");
71 Some(ref mut info
) => {
72 info
.use_site
= Some(span
);
74 // Unregistered errors.
76 ecx
.span_err(span
, &format
!(
77 "used diagnostic code {} not registered", code
82 MacEager
::expr(ecx
.expr_tuple(span
, Vec
::new()))
85 pub fn expand_register_diagnostic
<'cx
>(ecx
: &'cx
mut ExtCtxt
,
87 token_tree
: &[TokenTree
])
88 -> Box
<MacResult
+'cx
> {
89 let (code
, description
) = match (
95 (1, Some(&ast
::TtToken(_
, token
::Ident(ref code
, _
))), None
, None
) => {
98 (3, Some(&ast
::TtToken(_
, token
::Ident(ref code
, _
))),
99 Some(&ast
::TtToken(_
, token
::Comma
)),
100 Some(&ast
::TtToken(_
, token
::Literal(token
::StrRaw(description
, _
), None
)))) => {
101 (code
, Some(description
))
106 // Check that the description starts and ends with a newline and doesn't
107 // overflow the maximum line width.
108 description
.map(|raw_msg
| {
109 let msg
= raw_msg
.as_str();
110 if !msg
.starts_with("\n") || !msg
.ends_with("\n") {
111 ecx
.span_err(span
, &format
!(
112 "description for error code {} doesn't start and end with a newline",
117 // URLs can be unavoidably longer than the line limit, so we allow them.
118 // Allowed format is: `[name]: https://www.rust-lang.org/`
119 let is_url
= |l
: &str| l
.starts_with('
['
) && l
.contains("]:") && l
.contains("http");
121 if msg
.lines().any(|line
| line
.len() > MAX_DESCRIPTION_WIDTH
&& !is_url(line
)) {
122 ecx
.span_err(span
, &format
!(
123 "description for error code {} contains a line longer than {} characters.\n\
124 if you're inserting a long URL use the footnote style to bypass this check.",
125 code
, MAX_DESCRIPTION_WIDTH
129 // Add the error to the map.
130 with_registered_diagnostics(|diagnostics
| {
131 let info
= ErrorInfo
{
132 description
: description
,
135 if diagnostics
.insert(code
.name
, info
).is_some() {
136 ecx
.span_err(span
, &format
!(
137 "diagnostic code {} already registered", code
141 let sym
= Ident
::new(token
::gensym(&format
!(
142 "__register_diagnostic_{}", code
144 MacEager
::items(SmallVector
::many(vec
![
155 pub fn expand_build_diagnostic_array
<'cx
>(ecx
: &'cx
mut ExtCtxt
,
157 token_tree
: &[TokenTree
])
158 -> Box
<MacResult
+'cx
> {
159 assert_eq
!(token_tree
.len(), 3);
160 let (crate_name
, name
) = match (&token_tree
[0], &token_tree
[2]) {
163 &ast
::TtToken(_
, token
::Ident(ref crate_name
, _
)),
164 // DIAGNOSTICS ident.
165 &ast
::TtToken(_
, token
::Ident(ref name
, _
))
166 ) => (*&crate_name
, name
),
170 // Output error metadata to `tmp/extended-errors/<target arch>/<crate name>.json`
171 let target_triple
= env
::var("CFG_COMPILER_HOST_TRIPLE")
172 .ok().expect("unable to determine target arch from $CFG_COMPILER_HOST_TRIPLE");
174 with_registered_diagnostics(|diagnostics
| {
175 if let Err(e
) = output_metadata(ecx
,
177 &crate_name
.name
.as_str(),
179 ecx
.span_bug(span
, &format
!(
180 "error writing metadata for triple `{}` and crate `{}`, error: {}, cause: {:?}",
181 target_triple
, crate_name
, e
.description(), e
.cause()
186 // Construct the output expression.
188 with_registered_diagnostics(|diagnostics
| {
189 let descriptions
: Vec
<P
<ast
::Expr
>> =
190 diagnostics
.iter().filter_map(|(code
, info
)| {
191 info
.description
.map(|description
| {
192 ecx
.expr_tuple(span
, vec
![
193 ecx
.expr_str(span
, code
.as_str()),
194 ecx
.expr_str(span
, description
.as_str())
198 (descriptions
.len(), ecx
.expr_vec(span
, descriptions
))
201 let static_
= ecx
.lifetime(span
, ecx
.name_of("'static"));
202 let ty_str
= ecx
.ty_rptr(
204 ecx
.ty_ident(span
, ecx
.ident_of("str")),
211 ast
::TyFixedLengthVec(
214 ast
::TyTup(vec
![ty_str
.clone(), ty_str
])
216 ecx
.expr_usize(span
, count
),
220 MacEager
::items(SmallVector
::many(vec
![
224 id
: ast
::DUMMY_NODE_ID
,
225 node
: ast
::ItemConst(