about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/diagnostics.rs6
-rw-r--r--crates/hir/src/lib.rs24
-rw-r--r--crates/hir_def/src/nameres/collector.rs41
-rw-r--r--crates/hir_def/src/nameres/diagnostics.rs13
-rw-r--r--crates/ide_diagnostics/src/handlers/invalid_derive_target.rs1
-rw-r--r--crates/ide_diagnostics/src/handlers/malformed_derive.rs37
-rw-r--r--crates/ide_diagnostics/src/lib.rs2
7 files changed, 100 insertions, 24 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 5c4104baaef..83c6f2c3b8a 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -34,6 +34,7 @@ diagnostics![
     IncorrectCase,
     InvalidDeriveTarget,
     MacroError,
+    MalformedDerive,
     MismatchedArgCount,
     MissingFields,
     MissingMatchArms,
@@ -105,6 +106,11 @@ pub struct InvalidDeriveTarget {
 }
 
 #[derive(Debug)]
+pub struct MalformedDerive {
+    pub node: InFile<SyntaxNodePtr>,
+}
+
+#[derive(Debug)]
 pub struct NoSuchField {
     pub field: InFile<AstPtr<ast::RecordExprField>>,
 }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 7a1b4fd31c8..baa58c08248 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -83,10 +83,11 @@ pub use crate::{
     attrs::{HasAttrs, Namespace},
     diagnostics::{
         AddReferenceHere, AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase,
-        InvalidDeriveTarget, MacroError, MismatchedArgCount, MissingFields, MissingMatchArms,
-        MissingOkOrSomeInTailExpr, MissingUnsafe, NoSuchField, RemoveThisSemicolon,
-        ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro, UnresolvedExternCrate,
-        UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
+        InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
+        MissingMatchArms, MissingOkOrSomeInTailExpr, MissingUnsafe, NoSuchField,
+        RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro,
+        UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
+        UnresolvedProcMacro,
     },
     has_source::HasSource,
     semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
@@ -669,6 +670,21 @@ impl Module {
                         None => stdx::never!("derive diagnostic on item without derive attribute"),
                     }
                 }
+                DefDiagnosticKind::MalformedDerive { ast, id } => {
+                    let node = ast.to_node(db.upcast());
+                    let derive = node.attrs().nth(*id as usize);
+                    match derive {
+                        Some(derive) => {
+                            acc.push(
+                                MalformedDerive {
+                                    node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&derive))),
+                                }
+                                .into(),
+                            );
+                        }
+                        None => stdx::never!("derive diagnostic on item without derive attribute"),
+                    }
+                }
             }
         }
         for decl in self.declarations(db) {
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 343e189046e..5673bef38bc 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -8,7 +8,6 @@ use std::iter;
 use base_db::{CrateId, Edition, FileId, ProcMacroId};
 use cfg::{CfgExpr, CfgOptions};
 use hir_expand::{
-    ast_id_map::FileAstId,
     builtin_attr_macro::find_builtin_attr,
     builtin_derive_macro::find_builtin_derive,
     builtin_fn_macro::find_builtin_macro,
@@ -1081,8 +1080,10 @@ impl DefCollector<'_> {
                         return false;
                     }
                 }
-                MacroDirectiveKind::Attr { ast_id, mod_item, attr } => {
-                    let file_id = ast_id.ast_id.file_id;
+                MacroDirectiveKind::Attr { ast_id: file_ast_id, mod_item, attr } => {
+                    let &AstIdWithPath { ast_id, ref path } = file_ast_id;
+                    let file_id = ast_id.file_id;
+
                     let mut recollect_without = |collector: &mut Self, item_tree| {
                         // Remove the original directive since we resolved it.
                         let mod_dir = collector.mod_dirs[&directive.module_id].clone();
@@ -1100,8 +1101,8 @@ impl DefCollector<'_> {
                         false
                     };
 
-                    if let Some(ident) = ast_id.path.as_ident() {
-                        if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id.ast_id) {
+                    if let Some(ident) = path.as_ident() {
+                        if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id) {
                             if helpers.contains(ident) {
                                 cov_mark::hit!(resolved_derive_helper);
                                 // Resolved to derive helper. Collect the item's attributes again,
@@ -1112,7 +1113,7 @@ impl DefCollector<'_> {
                         }
                     }
 
-                    let def = resolver(ast_id.path.clone()).filter(MacroDefId::is_attribute);
+                    let def = resolver(path.clone()).filter(MacroDefId::is_attribute);
                     if matches!(
                         def,
                         Some(MacroDefId {  kind:MacroDefKind::BuiltInAttr(expander, _),.. })
@@ -1121,26 +1122,23 @@ impl DefCollector<'_> {
                         // Resolved to `#[derive]`
                         let item_tree = self.db.file_item_tree(file_id);
 
-                        let ast_id: FileAstId<ast::Item> = match *mod_item {
-                            ModItem::Struct(it) => item_tree[it].ast_id.upcast(),
-                            ModItem::Union(it) => item_tree[it].ast_id.upcast(),
-                            ModItem::Enum(it) => item_tree[it].ast_id.upcast(),
+                        match mod_item {
+                            ModItem::Struct(_) | ModItem::Union(_) | ModItem::Enum(_) => (),
                             _ => {
                                 let diag = DefDiagnostic::invalid_derive_target(
                                     directive.module_id,
-                                    ast_id.ast_id,
+                                    ast_id,
                                     attr.id,
                                 );
                                 self.def_map.diagnostics.push(diag);
-                                res = ReachedFixedPoint::No;
-                                return false;
+                                return recollect_without(self, &item_tree);
                             }
-                        };
+                        }
 
                         match attr.parse_derive() {
                             Some(derive_macros) => {
                                 for path in derive_macros {
-                                    let ast_id = AstIdWithPath::new(file_id, ast_id, path);
+                                    let ast_id = AstIdWithPath::new(file_id, ast_id.value, path);
                                     self.unresolved_macros.push(MacroDirective {
                                         module_id: directive.module_id,
                                         depth: directive.depth + 1,
@@ -1152,8 +1150,12 @@ impl DefCollector<'_> {
                                 }
                             }
                             None => {
-                                // FIXME: diagnose
-                                tracing::debug!("malformed derive: {:?}", attr);
+                                let diag = DefDiagnostic::malformed_derive(
+                                    directive.module_id,
+                                    ast_id,
+                                    attr.id,
+                                );
+                                self.def_map.diagnostics.push(diag);
                             }
                         }
 
@@ -1165,7 +1167,8 @@ impl DefCollector<'_> {
                     }
 
                     // Not resolved to a derive helper or the derive attribute, so try to resolve as a normal attribute.
-                    match attr_macro_as_call_id(ast_id, attr, self.db, self.def_map.krate, def) {
+                    match attr_macro_as_call_id(file_ast_id, attr, self.db, self.def_map.krate, def)
+                    {
                         Ok(call_id) => {
                             let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
 
@@ -1198,7 +1201,7 @@ impl DefCollector<'_> {
 
                             self.def_map.modules[directive.module_id]
                                 .scope
-                                .add_attr_macro_invoc(ast_id.ast_id, call_id);
+                                .add_attr_macro_invoc(ast_id, call_id);
 
                             resolved.push((directive.module_id, call_id, directive.depth));
                             res = ReachedFixedPoint::No;
diff --git a/crates/hir_def/src/nameres/diagnostics.rs b/crates/hir_def/src/nameres/diagnostics.rs
index 1eb38447310..51e3f18c111 100644
--- a/crates/hir_def/src/nameres/diagnostics.rs
+++ b/crates/hir_def/src/nameres/diagnostics.rs
@@ -32,6 +32,8 @@ pub enum DefDiagnosticKind {
     UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
 
     InvalidDeriveTarget { ast: AstId<ast::Item>, id: u32 },
+
+    MalformedDerive { ast: AstId<ast::Item>, id: u32 },
 }
 
 #[derive(Debug, PartialEq, Eq)]
@@ -116,4 +118,15 @@ impl DefDiagnostic {
             kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id: id.ast_index },
         }
     }
+
+    pub(super) fn malformed_derive(
+        container: LocalModuleId,
+        ast: AstId<ast::Item>,
+        id: AttrId,
+    ) -> Self {
+        Self {
+            in_module: container,
+            kind: DefDiagnosticKind::MalformedDerive { ast, id: id.ast_index },
+        }
+    }
 }
diff --git a/crates/ide_diagnostics/src/handlers/invalid_derive_target.rs b/crates/ide_diagnostics/src/handlers/invalid_derive_target.rs
index 556d2dc0cbd..c779266bc97 100644
--- a/crates/ide_diagnostics/src/handlers/invalid_derive_target.rs
+++ b/crates/ide_diagnostics/src/handlers/invalid_derive_target.rs
@@ -8,7 +8,6 @@ pub(crate) fn invalid_derive_target(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::InvalidDeriveTarget,
 ) -> Diagnostic {
-    // Use more accurate position if available.
     let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
 
     Diagnostic::new(
diff --git a/crates/ide_diagnostics/src/handlers/malformed_derive.rs b/crates/ide_diagnostics/src/handlers/malformed_derive.rs
new file mode 100644
index 00000000000..cd48bdba07e
--- /dev/null
+++ b/crates/ide_diagnostics/src/handlers/malformed_derive.rs
@@ -0,0 +1,37 @@
+use crate::{Diagnostic, DiagnosticsContext, Severity};
+
+// Diagnostic: malformed-derive
+//
+// This diagnostic is shown when the derive attribute has invalid input.
+pub(crate) fn malformed_derive(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::MalformedDerive,
+) -> Diagnostic {
+    let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
+
+    Diagnostic::new(
+        "malformed-derive",
+        "malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`",
+        display_range,
+    )
+    .severity(Severity::Error)
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::check_diagnostics;
+
+    #[test]
+    fn invalid_input() {
+        check_diagnostics(
+            r#"
+//- minicore:derive
+mod __ {
+    #[derive = "aaaa"]
+  //^^^^^^^^^^^^^^^^^^ error: malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`
+    struct Foo;
+}
+            "#,
+        );
+    }
+}
diff --git a/crates/ide_diagnostics/src/lib.rs b/crates/ide_diagnostics/src/lib.rs
index 5070f240036..c921d989bd3 100644
--- a/crates/ide_diagnostics/src/lib.rs
+++ b/crates/ide_diagnostics/src/lib.rs
@@ -30,6 +30,7 @@ mod handlers {
     pub(crate) mod incorrect_case;
     pub(crate) mod invalid_derive_target;
     pub(crate) mod macro_error;
+    pub(crate) mod malformed_derive;
     pub(crate) mod mismatched_arg_count;
     pub(crate) mod missing_fields;
     pub(crate) mod missing_match_arms;
@@ -182,6 +183,7 @@ pub fn diagnostics(
             AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
             AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
             AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&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::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
             AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),