aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_expand/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-12-03 16:55:15 +0000
committerGitHub <[email protected]>2020-12-03 16:55:15 +0000
commitd46fce88f5af1a97888edf91df2cb51ff5bfd61c (patch)
tree1f0351e895fa8b24ae4fa5a1854fb6c4285efcd2 /crates/hir_expand/src
parent74de29b223c2e6f01d1ed0912d72787c202bb225 (diff)
parentbca1e5fcb825c6c4e09ec197513b5568fce3d985 (diff)
Merge #6700
6700: More macro diagnostics improvements r=jonas-schievink a=jonas-schievink This threads macro expansion errors through `eager.rs` and the `AsMacroCall` trait, improving macro diagnostics emitted during body lowering. Co-authored-by: Jonas Schievink <[email protected]>
Diffstat (limited to 'crates/hir_expand/src')
-rw-r--r--crates/hir_expand/src/builtin_macro.rs74
-rw-r--r--crates/hir_expand/src/db.rs1
-rw-r--r--crates/hir_expand/src/eager.rs116
3 files changed, 145 insertions, 46 deletions
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 7f4db106d..16c3c4d69 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -86,7 +86,6 @@ pub fn find_builtin_macro(
86register_builtin! { 86register_builtin! {
87 LAZY: 87 LAZY:
88 (column, Column) => column_expand, 88 (column, Column) => column_expand,
89 (compile_error, CompileError) => compile_error_expand,
90 (file, File) => file_expand, 89 (file, File) => file_expand,
91 (line, Line) => line_expand, 90 (line, Line) => line_expand,
92 (assert, Assert) => assert_expand, 91 (assert, Assert) => assert_expand,
@@ -97,6 +96,7 @@ register_builtin! {
97 (format_args_nl, FormatArgsNl) => format_args_expand, 96 (format_args_nl, FormatArgsNl) => format_args_expand,
98 97
99 EAGER: 98 EAGER:
99 (compile_error, CompileError) => compile_error_expand,
100 (concat, Concat) => concat_expand, 100 (concat, Concat) => concat_expand,
101 (include, Include) => include_expand, 101 (include, Include) => include_expand,
102 (include_bytes, IncludeBytes) => include_bytes_expand, 102 (include_bytes, IncludeBytes) => include_bytes_expand,
@@ -213,25 +213,6 @@ fn file_expand(
213 ExpandResult::ok(expanded) 213 ExpandResult::ok(expanded)
214} 214}
215 215
216fn compile_error_expand(
217 _db: &dyn AstDatabase,
218 _id: LazyMacroId,
219 tt: &tt::Subtree,
220) -> ExpandResult<tt::Subtree> {
221 if tt.count() == 1 {
222 if let tt::TokenTree::Leaf(tt::Leaf::Literal(it)) = &tt.token_trees[0] {
223 let s = it.text.as_str();
224 if s.contains('"') {
225 return ExpandResult::ok(quote! { loop { #it }});
226 }
227 };
228 }
229
230 ExpandResult::only_err(mbe::ExpandError::BindingError(
231 "`compile_error!` argument be a string".into(),
232 ))
233}
234
235fn format_args_expand( 216fn format_args_expand(
236 _db: &dyn AstDatabase, 217 _db: &dyn AstDatabase,
237 _id: LazyMacroId, 218 _id: LazyMacroId,
@@ -280,6 +261,30 @@ fn unquote_str(lit: &tt::Literal) -> Option<String> {
280 token.value().map(|it| it.into_owned()) 261 token.value().map(|it| it.into_owned())
281} 262}
282 263
264fn compile_error_expand(
265 _db: &dyn AstDatabase,
266 _id: EagerMacroId,
267 tt: &tt::Subtree,
268) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
269 let err = match &*tt.token_trees {
270 [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => {
271 let text = it.text.as_str();
272 if text.starts_with('"') && text.ends_with('"') {
273 // FIXME: does not handle raw strings
274 mbe::ExpandError::Other(format!(
275 "`compile_error!` called: {}",
276 &text[1..text.len() - 1]
277 ))
278 } else {
279 mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into())
280 }
281 }
282 _ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()),
283 };
284
285 ExpandResult { value: Some((quote! {}, FragmentKind::Items)), err: Some(err) }
286}
287
283fn concat_expand( 288fn concat_expand(
284 _db: &dyn AstDatabase, 289 _db: &dyn AstDatabase,
285 _arg_id: EagerMacroId, 290 _arg_id: EagerMacroId,
@@ -417,17 +422,25 @@ fn env_expand(
417 Err(e) => return ExpandResult::only_err(e), 422 Err(e) => return ExpandResult::only_err(e),
418 }; 423 };
419 424
420 // FIXME: 425 let mut err = None;
421 // If the environment variable is not defined int rustc, then a compilation error will be emitted. 426 let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| {
422 // We might do the same if we fully support all other stuffs. 427 // The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
423 // But for now on, we should return some dummy string for better type infer purpose. 428 // unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
424 // However, we cannot use an empty string here, because for 429 if key == "OUT_DIR" {
425 // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become 430 err = Some(mbe::ExpandError::Other(
426 // `include!("foo.rs"), which might go to infinite loop 431 r#"`OUT_DIR` not set, enable "load out dirs from check" to fix"#.into(),
427 let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string()); 432 ));
433 }
434
435 // If the variable is unset, still return a dummy string to help type inference along.
436 // We cannot use an empty string here, because for
437 // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
438 // `include!("foo.rs"), which might go to infinite loop
439 "__RA_UNIMPLEMENTED__".to_string()
440 });
428 let expanded = quote! { #s }; 441 let expanded = quote! { #s };
429 442
430 ExpandResult::ok(Some((expanded, FragmentKind::Expr))) 443 ExpandResult { value: Some((expanded, FragmentKind::Expr)), err }
431} 444}
432 445
433fn option_env_expand( 446fn option_env_expand(
@@ -638,7 +651,8 @@ mod tests {
638 "#, 651 "#,
639 ); 652 );
640 653
641 assert_eq!(expanded, r#"loop{"error!"}"#); 654 // This expands to nothing (since it's in item position), but emits an error.
655 assert_eq!(expanded, "");
642 } 656 }
643 657
644 #[test] 658 #[test]
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 4fd0ba290..842a177db 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -207,6 +207,7 @@ fn macro_expand_with_arg(
207 } else { 207 } else {
208 return ExpandResult { 208 return ExpandResult {
209 value: Some(db.lookup_intern_eager_expansion(id).subtree), 209 value: Some(db.lookup_intern_eager_expansion(id).subtree),
210 // FIXME: There could be errors here!
210 err: None, 211 err: None,
211 }; 212 };
212 } 213 }
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index ab6b4477c..0229a836e 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -26,19 +26,89 @@ use crate::{
26}; 26};
27 27
28use base_db::CrateId; 28use base_db::CrateId;
29use mbe::ExpandResult;
29use parser::FragmentKind; 30use parser::FragmentKind;
30use std::sync::Arc; 31use std::sync::Arc;
31use syntax::{algo::SyntaxRewriter, SyntaxNode}; 32use syntax::{algo::SyntaxRewriter, SyntaxNode};
32 33
34pub struct ErrorEmitted {
35 _private: (),
36}
37
38trait ErrorSink {
39 fn emit(&mut self, err: mbe::ExpandError);
40
41 fn option<T>(
42 &mut self,
43 opt: Option<T>,
44 error: impl FnOnce() -> mbe::ExpandError,
45 ) -> Result<T, ErrorEmitted> {
46 match opt {
47 Some(it) => Ok(it),
48 None => {
49 self.emit(error());
50 Err(ErrorEmitted { _private: () })
51 }
52 }
53 }
54
55 fn option_with<T>(
56 &mut self,
57 opt: impl FnOnce() -> Option<T>,
58 error: impl FnOnce() -> mbe::ExpandError,
59 ) -> Result<T, ErrorEmitted> {
60 self.option(opt(), error)
61 }
62
63 fn result<T>(&mut self, res: Result<T, mbe::ExpandError>) -> Result<T, ErrorEmitted> {
64 match res {
65 Ok(it) => Ok(it),
66 Err(e) => {
67 self.emit(e);
68 Err(ErrorEmitted { _private: () })
69 }
70 }
71 }
72
73 fn expand_result_option<T>(&mut self, res: ExpandResult<Option<T>>) -> Result<T, ErrorEmitted> {
74 match (res.value, res.err) {
75 (None, Some(err)) => {
76 self.emit(err);
77 Err(ErrorEmitted { _private: () })
78 }
79 (Some(value), opt_err) => {
80 if let Some(err) = opt_err {
81 self.emit(err);
82 }
83 Ok(value)
84 }
85 (None, None) => unreachable!("`ExpandResult` without value or error"),
86 }
87 }
88}
89
90impl ErrorSink for &'_ mut dyn FnMut(mbe::ExpandError) {
91 fn emit(&mut self, err: mbe::ExpandError) {
92 self(err);
93 }
94}
95
96fn err(msg: impl Into<String>) -> mbe::ExpandError {
97 mbe::ExpandError::Other(msg.into())
98}
99
33pub fn expand_eager_macro( 100pub fn expand_eager_macro(
34 db: &dyn AstDatabase, 101 db: &dyn AstDatabase,
35 krate: CrateId, 102 krate: CrateId,
36 macro_call: InFile<ast::MacroCall>, 103 macro_call: InFile<ast::MacroCall>,
37 def: MacroDefId, 104 def: MacroDefId,
38 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 105 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
39) -> Option<EagerMacroId> { 106 mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
40 let args = macro_call.value.token_tree()?; 107) -> Result<EagerMacroId, ErrorEmitted> {
41 let parsed_args = mbe::ast_to_token_tree(&args)?.0; 108 let parsed_args = diagnostic_sink.option_with(
109 || Some(mbe::ast_to_token_tree(&macro_call.value.token_tree()?)?.0),
110 || err("malformed macro invocation"),
111 )?;
42 112
43 // Note: 113 // Note:
44 // When `lazy_expand` is called, its *parent* file must be already exists. 114 // When `lazy_expand` is called, its *parent* file must be already exists.
@@ -55,17 +125,22 @@ pub fn expand_eager_macro(
55 }); 125 });
56 let arg_file_id: MacroCallId = arg_id.into(); 126 let arg_file_id: MacroCallId = arg_id.into();
57 127
58 let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0; 128 let parsed_args =
129 diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0;
59 let result = eager_macro_recur( 130 let result = eager_macro_recur(
60 db, 131 db,
61 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()), 132 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
62 krate, 133 krate,
63 resolver, 134 resolver,
135 diagnostic_sink,
64 )?; 136 )?;
65 let subtree = to_subtree(&result)?; 137 let subtree =
138 diagnostic_sink.option(to_subtree(&result), || err("failed to parse macro result"))?;
66 139
67 if let MacroDefKind::BuiltInEager(eager) = def.kind { 140 if let MacroDefKind::BuiltInEager(eager) = def.kind {
68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).value?; 141 let res = eager.expand(db, arg_id, &subtree);
142
143 let (subtree, fragment) = diagnostic_sink.expand_result_option(res)?;
69 let eager = EagerCallLoc { 144 let eager = EagerCallLoc {
70 def, 145 def,
71 fragment, 146 fragment,
@@ -74,9 +149,9 @@ pub fn expand_eager_macro(
74 file_id: macro_call.file_id, 149 file_id: macro_call.file_id,
75 }; 150 };
76 151
77 Some(db.intern_eager_expansion(eager)) 152 Ok(db.intern_eager_expansion(eager))
78 } else { 153 } else {
79 None 154 panic!("called `expand_eager_macro` on non-eager macro def {:?}", def);
80 } 155 }
81} 156}
82 157
@@ -91,13 +166,16 @@ fn lazy_expand(
91 def: &MacroDefId, 166 def: &MacroDefId,
92 macro_call: InFile<ast::MacroCall>, 167 macro_call: InFile<ast::MacroCall>,
93 krate: CrateId, 168 krate: CrateId,
94) -> Option<InFile<SyntaxNode>> { 169) -> ExpandResult<Option<InFile<SyntaxNode>>> {
95 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 170 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
96 171
97 let id: MacroCallId = 172 let id: MacroCallId =
98 def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); 173 def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
99 174
100 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) 175 let err = db.macro_expand_error(id);
176 let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node));
177
178 ExpandResult { value, err }
101} 179}
102 180
103fn eager_macro_recur( 181fn eager_macro_recur(
@@ -105,7 +183,8 @@ fn eager_macro_recur(
105 curr: InFile<SyntaxNode>, 183 curr: InFile<SyntaxNode>,
106 krate: CrateId, 184 krate: CrateId,
107 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 185 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
108) -> Option<SyntaxNode> { 186 mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
187) -> Result<SyntaxNode, ErrorEmitted> {
109 let original = curr.value.clone(); 188 let original = curr.value.clone();
110 189
111 let children = curr.value.descendants().filter_map(ast::MacroCall::cast); 190 let children = curr.value.descendants().filter_map(ast::MacroCall::cast);
@@ -113,7 +192,8 @@ fn eager_macro_recur(
113 192
114 // Collect replacement 193 // Collect replacement
115 for child in children { 194 for child in children {
116 let def: MacroDefId = macro_resolver(child.path()?)?; 195 let def = diagnostic_sink
196 .option_with(|| macro_resolver(child.path()?), || err("failed to resolve macro"))?;
117 let insert = match def.kind { 197 let insert = match def.kind {
118 MacroDefKind::BuiltInEager(_) => { 198 MacroDefKind::BuiltInEager(_) => {
119 let id: MacroCallId = expand_eager_macro( 199 let id: MacroCallId = expand_eager_macro(
@@ -122,17 +202,21 @@ fn eager_macro_recur(
122 curr.with_value(child.clone()), 202 curr.with_value(child.clone()),
123 def, 203 def,
124 macro_resolver, 204 macro_resolver,
205 diagnostic_sink,
125 )? 206 )?
126 .into(); 207 .into();
127 db.parse_or_expand(id.as_file())? 208 db.parse_or_expand(id.as_file())
209 .expect("successful macro expansion should be parseable")
128 } 210 }
129 MacroDefKind::Declarative 211 MacroDefKind::Declarative
130 | MacroDefKind::BuiltIn(_) 212 | MacroDefKind::BuiltIn(_)
131 | MacroDefKind::BuiltInDerive(_) 213 | MacroDefKind::BuiltInDerive(_)
132 | MacroDefKind::ProcMacro(_) => { 214 | MacroDefKind::ProcMacro(_) => {
133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?; 215 let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate);
216 let val = diagnostic_sink.expand_result_option(res)?;
217
134 // replace macro inside 218 // replace macro inside
135 eager_macro_recur(db, expanded, krate, macro_resolver)? 219 eager_macro_recur(db, val, krate, macro_resolver, diagnostic_sink)?
136 } 220 }
137 }; 221 };
138 222
@@ -140,5 +224,5 @@ fn eager_macro_recur(
140 } 224 }
141 225
142 let res = rewriter.rewrite(&original); 226 let res = rewriter.rewrite(&original);
143 Some(res) 227 Ok(res)
144} 228}