Skip to content

Commit 31b8068

Browse files
committed
fix: Diagnose using derive on non-adt items
1 parent 966cae3 commit 31b8068

File tree

6 files changed

+90
-27
lines changed

6 files changed

+90
-27
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ diagnostics![
3232
BreakOutsideOfLoop,
3333
InactiveCode,
3434
IncorrectCase,
35+
InvalidDeriveTarget,
3536
MacroError,
3637
MismatchedArgCount,
3738
MissingFields,
@@ -98,6 +99,11 @@ pub struct UnimplementedBuiltinMacro {
9899
pub node: InFile<SyntaxNodePtr>,
99100
}
100101

102+
#[derive(Debug)]
103+
pub struct InvalidDeriveTarget {
104+
pub node: InFile<SyntaxNodePtr>,
105+
}
106+
101107
#[derive(Debug)]
102108
pub struct NoSuchField {
103109
pub field: InFile<AstPtr<ast::RecordExprField>>,

crates/hir/src/lib.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ pub use crate::{
8383
attrs::{HasAttrs, Namespace},
8484
diagnostics::{
8585
AddReferenceHere, AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase,
86-
MacroError, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
87-
MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
88-
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
89-
UnresolvedModule, UnresolvedProcMacro,
86+
InvalidDeriveTarget, MacroError, MismatchedArgCount, MissingFields, MissingMatchArms,
87+
MissingOkOrSomeInTailExpr, MissingUnsafe, NoSuchField, RemoveThisSemicolon,
88+
ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro, UnresolvedExternCrate,
89+
UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
9090
},
9191
has_source::HasSource,
9292
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
@@ -654,6 +654,21 @@ impl Module {
654654
.into(),
655655
);
656656
}
657+
DefDiagnosticKind::InvalidDeriveTarget { ast } => {
658+
let node = ast.to_node(db.upcast());
659+
let derive = node.attrs().next();
660+
match derive {
661+
Some(derive) => {
662+
acc.push(
663+
InvalidDeriveTarget {
664+
node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&derive))),
665+
}
666+
.into(),
667+
);
668+
}
669+
None => stdx::never!("derive diagnostic on item without derive attribute"),
670+
}
671+
}
657672
}
658673
}
659674
for decl in self.declarations(db) {

crates/hir_def/src/nameres/collector.rs

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,13 +1055,10 @@ impl DefCollector<'_> {
10551055
&resolver,
10561056
&mut |_err| (),
10571057
);
1058-
match call_id {
1059-
Ok(Ok(call_id)) => {
1060-
resolved.push((directive.module_id, call_id, directive.depth));
1061-
res = ReachedFixedPoint::No;
1062-
return false;
1063-
}
1064-
Err(UnresolvedMacro { .. }) | Ok(Err(_)) => {}
1058+
if let Ok(Ok(call_id)) = call_id {
1059+
resolved.push((directive.module_id, call_id, directive.depth));
1060+
res = ReachedFixedPoint::No;
1061+
return false;
10651062
}
10661063
}
10671064
MacroDirectiveKind::Derive { ast_id, derive_attr } => {
@@ -1072,19 +1069,16 @@ impl DefCollector<'_> {
10721069
self.def_map.krate,
10731070
&resolver,
10741071
);
1075-
match call_id {
1076-
Ok(call_id) => {
1077-
self.def_map.modules[directive.module_id].scope.add_derive_macro_invoc(
1078-
ast_id.ast_id,
1079-
call_id,
1080-
*derive_attr,
1081-
);
1072+
if let Ok(call_id) = call_id {
1073+
self.def_map.modules[directive.module_id].scope.add_derive_macro_invoc(
1074+
ast_id.ast_id,
1075+
call_id,
1076+
*derive_attr,
1077+
);
10821078

1083-
resolved.push((directive.module_id, call_id, directive.depth));
1084-
res = ReachedFixedPoint::No;
1085-
return false;
1086-
}
1087-
Err(UnresolvedMacro { .. }) => {}
1079+
resolved.push((directive.module_id, call_id, directive.depth));
1080+
res = ReachedFixedPoint::No;
1081+
return false;
10881082
}
10891083
}
10901084
MacroDirectiveKind::Attr { ast_id, mod_item, attr } => {
@@ -1125,16 +1119,18 @@ impl DefCollector<'_> {
11251119
if expander.is_derive()
11261120
) {
11271121
// Resolved to `#[derive]`
1128-
let file_id = ast_id.ast_id.file_id;
11291122
let item_tree = self.db.file_item_tree(file_id);
11301123

11311124
let ast_id: FileAstId<ast::Item> = match *mod_item {
11321125
ModItem::Struct(it) => item_tree[it].ast_id.upcast(),
11331126
ModItem::Union(it) => item_tree[it].ast_id.upcast(),
11341127
ModItem::Enum(it) => item_tree[it].ast_id.upcast(),
11351128
_ => {
1136-
// Cannot use derive on this item.
1137-
// FIXME: diagnose
1129+
let diag = DefDiagnostic::invalid_derive_target(
1130+
directive.module_id,
1131+
ast_id.ast_id,
1132+
);
1133+
self.def_map.diagnostics.push(diag);
11381134
res = ReachedFixedPoint::No;
11391135
return false;
11401136
}
@@ -1194,7 +1190,6 @@ impl DefCollector<'_> {
11941190
),
11951191
);
11961192

1197-
let file_id = ast_id.ast_id.file_id;
11981193
let item_tree = self.db.file_item_tree(file_id);
11991194
return recollect_without(self, &item_tree);
12001195
}

crates/hir_def/src/nameres/diagnostics.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ pub enum DefDiagnosticKind {
2929
MacroError { ast: MacroCallKind, message: String },
3030

3131
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
32+
33+
InvalidDeriveTarget { ast: AstId<ast::Item> },
3234
}
3335

3436
#[derive(Debug, PartialEq, Eq)]
@@ -102,4 +104,8 @@ impl DefDiagnostic {
102104
) -> Self {
103105
Self { in_module: container, kind: DefDiagnosticKind::UnimplementedBuiltinMacro { ast } }
104106
}
107+
108+
pub(super) fn invalid_derive_target(container: LocalModuleId, ast: AstId<ast::Item>) -> Self {
109+
Self { in_module: container, kind: DefDiagnosticKind::InvalidDeriveTarget { ast } }
110+
}
105111
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use crate::{Diagnostic, DiagnosticsContext, Severity};
2+
3+
// Diagnostic: invalid-derive-target
4+
//
5+
// This diagnostic is shown when the derive attribute is used on an item other than a `struct`,
6+
// `enum` or `union`.
7+
pub(crate) fn invalid_derive_target(
8+
ctx: &DiagnosticsContext<'_>,
9+
d: &hir::InvalidDeriveTarget,
10+
) -> Diagnostic {
11+
// Use more accurate position if available.
12+
let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
13+
14+
Diagnostic::new(
15+
"invalid-derive-target",
16+
"`derive` may only be applied to `struct`s, `enum`s and `union`s",
17+
display_range,
18+
)
19+
.severity(Severity::Error)
20+
}
21+
22+
#[cfg(test)]
23+
mod tests {
24+
use crate::tests::check_diagnostics;
25+
26+
#[test]
27+
fn fails_on_function() {
28+
check_diagnostics(
29+
r#"
30+
//- minicore:derive
31+
mod __ {
32+
#[derive()]
33+
//^^^^^^^^^^^ error: `derive` may only be applied to `struct`s, `enum`s and `union`s
34+
fn main() {}
35+
}
36+
"#,
37+
);
38+
}
39+
}

crates/ide_diagnostics/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ mod handlers {
2828
pub(crate) mod break_outside_of_loop;
2929
pub(crate) mod inactive_code;
3030
pub(crate) mod incorrect_case;
31+
pub(crate) mod invalid_derive_target;
3132
pub(crate) mod macro_error;
3233
pub(crate) mod mismatched_arg_count;
3334
pub(crate) mod missing_fields;
@@ -195,6 +196,7 @@ pub fn diagnostics(
195196
AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
196197
AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
197198
AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
199+
AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
198200

199201
AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
200202
Some(it) => it,

0 commit comments

Comments
 (0)