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
;
10 use crate::symbol
::{keywords, Symbol}
;
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
.len(), token_tree
.get(0)) {
37 (1, Some(&TokenTree
::Token(_
, token
::Ident(code
, _
)))) => code
,
41 ecx
.parse_sess
.registered_diagnostics
.with_lock(|diagnostics
| {
42 match diagnostics
.get_mut(&code
.name
) {
43 // Previously used errors.
44 Some(&mut ErrorInfo { description: _, use_site: Some(previous_span) }
) => {
45 ecx
.struct_span_warn(span
, &format
!(
46 "diagnostic code {} already used", code
47 )).span_note(previous_span
, "previous invocation")
51 Some(ref mut info
) => {
52 info
.use_site
= Some(span
);
54 // Unregistered errors.
56 ecx
.span_err(span
, &format
!(
57 "used diagnostic code {} not registered", code
62 MacEager
::expr(ecx
.expr_tuple(span
, Vec
::new()))
65 pub fn expand_register_diagnostic
<'cx
>(ecx
: &'cx
mut ExtCtxt
<'_
>,
67 token_tree
: &[TokenTree
])
68 -> Box
<dyn MacResult
+'cx
> {
69 let (code
, description
) = match (
75 (1, Some(&TokenTree
::Token(_
, token
::Ident(ref code
, _
))), None
, None
) => {
78 (3, Some(&TokenTree
::Token(_
, token
::Ident(ref code
, _
))),
79 Some(&TokenTree
::Token(_
, token
::Comma
)),
80 Some(&TokenTree
::Token(_
, token
::Literal(token
::StrRaw(description
, _
), None
)))) => {
81 (code
, Some(description
))
86 // Check that the description starts and ends with a newline and doesn't
87 // overflow the maximum line width.
88 description
.map(|raw_msg
| {
89 let msg
= raw_msg
.as_str();
90 if !msg
.starts_with("\n") || !msg
.ends_with("\n") {
91 ecx
.span_err(span
, &format
!(
92 "description for error code {} doesn't start and end with a newline",
97 // URLs can be unavoidably longer than the line limit, so we allow them.
98 // Allowed format is: `[name]: https://www.rust-lang.org/`
99 let is_url
= |l
: &str| l
.starts_with("[") && l
.contains("]:") && l
.contains("http");
101 if msg
.lines().any(|line
| line
.len() > MAX_DESCRIPTION_WIDTH
&& !is_url(line
)) {
102 ecx
.span_err(span
, &format
!(
103 "description for error code {} contains a line longer than {} characters.\n\
104 if you're inserting a long URL use the footnote style to bypass this check.",
105 code
, MAX_DESCRIPTION_WIDTH
109 // Add the error to the map.
110 ecx
.parse_sess
.registered_diagnostics
.with_lock(|diagnostics
| {
111 let info
= ErrorInfo
{
115 if diagnostics
.insert(code
.name
, info
).is_some() {
116 ecx
.span_err(span
, &format
!(
117 "diagnostic code {} already registered", code
122 let span
= span
.apply_mark(ecx
.current_expansion
.mark
);
124 let sym
= Ident
::new(Symbol
::gensym(&format
!("__register_diagnostic_{}", code
)), span
);
126 MacEager
::items(smallvec
![
138 pub fn expand_build_diagnostic_array
<'cx
>(ecx
: &'cx
mut ExtCtxt
<'_
>,
140 token_tree
: &[TokenTree
])
141 -> Box
<dyn MacResult
+'cx
> {
142 assert_eq
!(token_tree
.len(), 3);
143 let (crate_name
, name
) = match (&token_tree
[0], &token_tree
[2]) {
146 &TokenTree
::Token(_
, token
::Ident(ref crate_name
, _
)),
147 // DIAGNOSTICS ident.
148 &TokenTree
::Token(_
, token
::Ident(ref name
, _
))
149 ) => (*&crate_name
, name
),
153 // Output error metadata to `tmp/extended-errors/<target arch>/<crate name>.json`
154 if let Ok(target_triple
) = env
::var("CFG_COMPILER_HOST_TRIPLE") {
155 ecx
.parse_sess
.registered_diagnostics
.with_lock(|diagnostics
| {
156 if let Err(e
) = output_metadata(ecx
,
158 &crate_name
.as_str(),
160 ecx
.span_bug(span
, &format
!(
161 "error writing metadata for triple `{}` and crate `{}`, error: {}, \
163 target_triple
, crate_name
, e
.description(), e
.cause()
168 ecx
.span_err(span
, &format
!(
169 "failed to write metadata for crate `{}` because $CFG_COMPILER_HOST_TRIPLE is not set",
173 // Construct the output expression.
175 ecx
.parse_sess
.registered_diagnostics
.with_lock(|diagnostics
| {
176 let descriptions
: Vec
<P
<ast
::Expr
>> =
177 diagnostics
.iter().filter_map(|(&code
, info
)| {
178 info
.description
.map(|description
| {
179 ecx
.expr_tuple(span
, vec
![
180 ecx
.expr_str(span
, code
),
181 ecx
.expr_str(span
, description
)
185 (descriptions
.len(), ecx
.expr_vec(span
, descriptions
))
188 let static_
= ecx
.lifetime(span
, keywords
::StaticLifetime
.ident());
189 let ty_str
= ecx
.ty_rptr(
191 ecx
.ty_ident(span
, ecx
.ident_of("str")),
193 ast
::Mutability
::Immutable
,
201 ast
::TyKind
::Tup(vec
![ty_str
.clone(), ty_str
])
204 id
: ast
::DUMMY_NODE_ID
,
205 value
: ecx
.expr_usize(span
, count
),
210 MacEager
::items(smallvec
![
214 id
: ast
::DUMMY_NODE_ID
,
215 node
: ast
::ItemKind
::Const(
219 vis
: source_map
::respan(span
.shrink_to_lo(), ast
::VisibilityKind
::Public
),