Skip to content

Commit 2fafe0e

Browse files
bors[bot]Veykril
andauthored
Merge #10804
10804: fix: Diagnose using `derive` on non-adt items r=Veykril a=Veykril Co-authored-by: Lukas Wirth <[email protected]>
2 parents 2507442 + 6757910 commit 2fafe0e

File tree

6 files changed

+99
-27
lines changed

6 files changed

+99
-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, id } => {
658+
let node = ast.to_node(db.upcast());
659+
let derive = node.attrs().nth(*id as usize);
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: 19 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,19 @@ 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+
attr.id,
1133+
);
1134+
self.def_map.diagnostics.push(diag);
11381135
res = ReachedFixedPoint::No;
11391136
return false;
11401137
}
@@ -1194,7 +1191,6 @@ impl DefCollector<'_> {
11941191
),
11951192
);
11961193

1197-
let file_id = ast_id.ast_id.file_id;
11981194
let item_tree = self.db.file_item_tree(file_id);
11991195
return recollect_without(self, &item_tree);
12001196
}

crates/hir_def/src/nameres/diagnostics.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use la_arena::Idx;
66
use syntax::ast;
77

88
use crate::{
9+
attr::AttrId,
910
item_tree::{self, ItemTreeId},
1011
nameres::LocalModuleId,
1112
path::ModPath,
@@ -29,6 +30,8 @@ pub enum DefDiagnosticKind {
2930
MacroError { ast: MacroCallKind, message: String },
3031

3132
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
33+
34+
InvalidDeriveTarget { ast: AstId<ast::Item>, id: u32 },
3235
}
3336

3437
#[derive(Debug, PartialEq, Eq)]
@@ -102,4 +105,15 @@ impl DefDiagnostic {
102105
) -> Self {
103106
Self { in_module: container, kind: DefDiagnosticKind::UnimplementedBuiltinMacro { ast } }
104107
}
108+
109+
pub(super) fn invalid_derive_target(
110+
container: LocalModuleId,
111+
ast: AstId<ast::Item>,
112+
id: AttrId,
113+
) -> Self {
114+
Self {
115+
in_module: container,
116+
kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id: id.ast_index },
117+
}
118+
}
105119
}
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)