internal: Report macro definition errors on the definition

This commit is contained in:
Lukas Wirth 2023-04-16 14:15:59 +02:00
parent 0bb9a17312
commit a5558cdfe5
10 changed files with 115 additions and 14 deletions

View File

@ -190,8 +190,9 @@ fn enter_expand_inner(
let file_id = call_id.as_file(); let file_id = call_id.as_file();
let raw_node = match db.parse_or_expand(file_id) { let raw_node = match db.parse_or_expand_with_err(file_id) {
Some(it) => it, // FIXME: report parse errors
Some(it) => it.syntax_node(),
None => { None => {
// Only `None` if the macro expansion produced no usable AST. // Only `None` if the macro expansion produced no usable AST.
if err.is_none() { if err.is_none() {

View File

@ -641,7 +641,12 @@ fn collect(&mut self, item_tree: &ItemTree, tree_id: TreeId, assoc_items: &[Asso
self.items.push((item.name.clone(), def.into())); self.items.push((item.name.clone(), def.into()));
} }
AssocItem::MacroCall(call) => { AssocItem::MacroCall(call) => {
if let Some(root) = self.db.parse_or_expand(self.expander.current_file_id()) { if let Some(root) =
self.db.parse_or_expand_with_err(self.expander.current_file_id())
{
// FIXME: report parse errors
let root = root.syntax_node();
let call = &item_tree[call]; let call = &item_tree[call];
let ast_id_map = self.db.ast_id_map(self.expander.current_file_id()); let ast_id_map = self.db.ast_id_map(self.expander.current_file_id());

View File

@ -1374,6 +1374,8 @@ fn collect_macro_expansion(
// Then, fetch and process the item tree. This will reuse the expansion result from above. // Then, fetch and process the item tree. This will reuse the expansion result from above.
let item_tree = self.db.file_item_tree(file_id); let item_tree = self.db.file_item_tree(file_id);
// FIXME: report parse errors for the macro expansion here
let mod_dir = self.mod_dirs[&module_id].clone(); let mod_dir = self.mod_dirs[&module_id].clone();
ModCollector { ModCollector {
def_collector: &mut *self, def_collector: &mut *self,

View File

@ -34,6 +34,8 @@ pub enum DefDiagnosticKind {
InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize }, InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
MalformedDerive { ast: AstId<ast::Adt>, id: usize }, MalformedDerive { ast: AstId<ast::Adt>, id: usize },
MacroDefError { ast: AstId<ast::Macro>, message: String },
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]

View File

@ -99,6 +99,8 @@ pub trait ExpandDatabase: SourceDatabase {
/// file or a macro expansion. /// file or a macro expansion.
#[salsa::transparent] #[salsa::transparent]
fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
#[salsa::transparent]
fn parse_or_expand_with_err(&self, file_id: HirFileId) -> Option<Parse<SyntaxNode>>;
/// Implementation for the macro case. /// Implementation for the macro case.
fn parse_macro_expansion( fn parse_macro_expansion(
&self, &self,
@ -252,13 +254,23 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option<Syntax
match file_id.repr() { match file_id.repr() {
HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
HirFileIdRepr::MacroFile(macro_file) => { HirFileIdRepr::MacroFile(macro_file) => {
// FIXME: Note how we convert from `Parse` to `SyntaxNode` here,
// forgetting about parse errors.
db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node()) db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node())
} }
} }
} }
fn parse_or_expand_with_err(
db: &dyn ExpandDatabase,
file_id: HirFileId,
) -> Option<Parse<SyntaxNode>> {
match file_id.repr() {
HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).to_syntax()),
HirFileIdRepr::MacroFile(macro_file) => {
db.parse_macro_expansion(macro_file).value.map(|(parse, _)| parse)
}
}
}
fn parse_macro_expansion( fn parse_macro_expansion(
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
macro_file: MacroFile, macro_file: MacroFile,

View File

@ -187,7 +187,10 @@ fn lazy_expand(
); );
let err = db.macro_expand_error(id); let err = db.macro_expand_error(id);
let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); let value =
db.parse_or_expand_with_err(id.as_file()).map(|node| InFile::new(id.as_file(), node));
// FIXME: report parse errors
let value = value.map(|it| it.map(|it| it.syntax_node()));
ExpandResult { value, err } ExpandResult { value, err }
} }

View File

@ -39,6 +39,7 @@ fn from(d: $diag) -> AnyDiagnostic {
InvalidDeriveTarget, InvalidDeriveTarget,
IncoherentImpl, IncoherentImpl,
MacroError, MacroError,
MacroDefError,
MalformedDerive, MalformedDerive,
MismatchedArgCount, MismatchedArgCount,
MissingFields, MissingFields,
@ -131,6 +132,13 @@ pub struct MacroError {
pub message: String, pub message: String,
} }
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MacroDefError {
pub node: InFile<AstPtr<ast::Macro>>,
pub message: String,
pub name: Option<TextRange>,
}
#[derive(Debug)] #[derive(Debug)]
pub struct UnimplementedBuiltinMacro { pub struct UnimplementedBuiltinMacro {
pub node: InFile<SyntaxNodePtr>, pub node: InFile<SyntaxNodePtr>,

View File

@ -46,6 +46,7 @@
item_tree::ItemTreeNode, item_tree::ItemTreeNode,
lang_item::{LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
layout::ReprOptions, layout::ReprOptions,
macro_id_to_def_id,
nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin}, nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin},
per_ns::PerNs, per_ns::PerNs,
resolver::{HasResolver, Resolver}, resolver::{HasResolver, Resolver},
@ -86,12 +87,12 @@
attrs::{HasAttrs, Namespace}, attrs::{HasAttrs, Namespace},
diagnostics::{ diagnostics::{
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl, AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MalformedDerive,
MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField,
PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel, PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch,
UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate,
UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedField, UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall,
UnresolvedProcMacro, UnusedMut, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
}, },
has_source::HasSource, has_source::HasSource,
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@ -563,6 +564,7 @@ pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
} }
emit_def_diagnostic(db, acc, diag); emit_def_diagnostic(db, acc, diag);
} }
for decl in self.declarations(db) { for decl in self.declarations(db) {
match decl { match decl {
ModuleDef::Module(m) => { ModuleDef::Module(m) => {
@ -601,9 +603,11 @@ pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
} }
acc.extend(decl.diagnostics(db)) acc.extend(decl.diagnostics(db))
} }
ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m),
_ => acc.extend(decl.diagnostics(db)), _ => acc.extend(decl.diagnostics(db)),
} }
} }
self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m));
let inherent_impls = db.inherent_impls_in_crate(self.id.krate()); let inherent_impls = db.inherent_impls_in_crate(self.id.krate());
@ -685,8 +689,31 @@ pub fn find_use_path_prefixed(
} }
} }
fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, m: Macro) {
let id = macro_id_to_def_id(db.upcast(), m.id);
if let Err(e) = db.macro_def(id) {
let Some(ast) = id.ast_id().left() else {
never!("MacroDefError for proc-macro: {:?}", e);
return;
};
emit_def_diagnostic_(
db,
acc,
&DefDiagnosticKind::MacroDefError { ast, message: e.to_string() },
);
}
}
fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: &DefDiagnostic) { fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: &DefDiagnostic) {
match &diag.kind { emit_def_diagnostic_(db, acc, &diag.kind)
}
fn emit_def_diagnostic_(
db: &dyn HirDatabase,
acc: &mut Vec<AnyDiagnostic>,
diag: &DefDiagnosticKind,
) {
match diag {
DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => { DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => {
let decl = declaration.to_node(db.upcast()); let decl = declaration.to_node(db.upcast());
acc.push( acc.push(
@ -794,6 +821,17 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
None => stdx::never!("derive diagnostic on item without derive attribute"), None => stdx::never!("derive diagnostic on item without derive attribute"),
} }
} }
DefDiagnosticKind::MacroDefError { ast, message } => {
let node = ast.to_node(db.upcast());
acc.push(
MacroDefError {
node: InFile::new(ast.file_id, AstPtr::new(&node)),
name: node.name().map(|it| it.syntax().text_range()),
message: message.clone(),
}
.into(),
);
}
} }
} }

View File

@ -9,6 +9,16 @@ pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) ->
Diagnostic::new("macro-error", d.message.clone(), display_range).experimental() Diagnostic::new("macro-error", d.message.clone(), display_range).experimental()
} }
// Diagnostic: macro-error
//
// This diagnostic is shown for macro expansion errors.
pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefError) -> Diagnostic {
// Use more accurate position if available.
let display_range =
ctx.resolve_precise_location(&d.node.clone().map(|it| it.syntax_node_ptr()), d.name);
Diagnostic::new("macro-def-error", d.message.clone(), display_range).experimental()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
@ -188,6 +198,7 @@ fn f() {
"#, "#,
); );
} }
#[test] #[test]
fn dollar_crate_in_builtin_macro() { fn dollar_crate_in_builtin_macro() {
check_diagnostics( check_diagnostics(
@ -209,6 +220,24 @@ macro_rules! outer {
fn f() { fn f() {
outer!(); outer!();
} //^^^^^^^^ error: leftover tokens } //^^^^^^^^ error: leftover tokens
"#,
)
}
#[test]
fn def_diagnostic() {
check_diagnostics(
r#"
macro_rules! foo {
//^^^ error: expected subtree
f => {};
}
fn f() {
foo!();
//^^^ error: invalid macro definition: expected subtree
}
"#, "#,
) )
} }

View File

@ -249,7 +249,7 @@ pub fn diagnostics(
let mut diags = Vec::new(); let mut diags = Vec::new();
if let Some(m) = module { if let Some(m) = module {
m.diagnostics(db, &mut diags) m.diagnostics(db, &mut diags);
} }
for diag in diags { for diag in diags {
@ -263,6 +263,7 @@ pub fn diagnostics(
AnyDiagnostic::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d), AnyDiagnostic::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d),
AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d), AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d), AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d),
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d), AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),