Skip to content

Commit 4c20d68

Browse files
bors[bot]Veykril
andauthored
Merge #10807
10807: fix: Diagnose invalid derive attribute input r=Veykril a=Veykril Doesn't yet diagnose incorrect syntax between the `(`, `)` braces as we discard those problems currently. bors r+ Co-authored-by: Lukas Wirth <[email protected]>
2 parents a60e009 + ea03def commit 4c20d68

File tree

7 files changed

+100
-24
lines changed

7 files changed

+100
-24
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ diagnostics![
3434
IncorrectCase,
3535
InvalidDeriveTarget,
3636
MacroError,
37+
MalformedDerive,
3738
MismatchedArgCount,
3839
MissingFields,
3940
MissingMatchArms,
@@ -104,6 +105,11 @@ pub struct InvalidDeriveTarget {
104105
pub node: InFile<SyntaxNodePtr>,
105106
}
106107

108+
#[derive(Debug)]
109+
pub struct MalformedDerive {
110+
pub node: InFile<SyntaxNodePtr>,
111+
}
112+
107113
#[derive(Debug)]
108114
pub struct NoSuchField {
109115
pub field: InFile<AstPtr<ast::RecordExprField>>,

crates/hir/src/lib.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,11 @@ pub use crate::{
8383
attrs::{HasAttrs, Namespace},
8484
diagnostics::{
8585
AddReferenceHere, AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase,
86-
InvalidDeriveTarget, MacroError, MismatchedArgCount, MissingFields, MissingMatchArms,
87-
MissingOkOrSomeInTailExpr, MissingUnsafe, NoSuchField, RemoveThisSemicolon,
88-
ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro, UnresolvedExternCrate,
89-
UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
86+
InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
87+
MissingMatchArms, MissingOkOrSomeInTailExpr, MissingUnsafe, NoSuchField,
88+
RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro,
89+
UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
90+
UnresolvedProcMacro,
9091
},
9192
has_source::HasSource,
9293
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
@@ -669,6 +670,21 @@ impl Module {
669670
None => stdx::never!("derive diagnostic on item without derive attribute"),
670671
}
671672
}
673+
DefDiagnosticKind::MalformedDerive { ast, id } => {
674+
let node = ast.to_node(db.upcast());
675+
let derive = node.attrs().nth(*id as usize);
676+
match derive {
677+
Some(derive) => {
678+
acc.push(
679+
MalformedDerive {
680+
node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&derive))),
681+
}
682+
.into(),
683+
);
684+
}
685+
None => stdx::never!("derive diagnostic on item without derive attribute"),
686+
}
687+
}
672688
}
673689
}
674690
for decl in self.declarations(db) {

crates/hir_def/src/nameres/collector.rs

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use std::iter;
88
use base_db::{CrateId, Edition, FileId, ProcMacroId};
99
use cfg::{CfgExpr, CfgOptions};
1010
use hir_expand::{
11-
ast_id_map::FileAstId,
1211
builtin_attr_macro::find_builtin_attr,
1312
builtin_derive_macro::find_builtin_derive,
1413
builtin_fn_macro::find_builtin_macro,
@@ -1081,8 +1080,10 @@ impl DefCollector<'_> {
10811080
return false;
10821081
}
10831082
}
1084-
MacroDirectiveKind::Attr { ast_id, mod_item, attr } => {
1085-
let file_id = ast_id.ast_id.file_id;
1083+
MacroDirectiveKind::Attr { ast_id: file_ast_id, mod_item, attr } => {
1084+
let &AstIdWithPath { ast_id, ref path } = file_ast_id;
1085+
let file_id = ast_id.file_id;
1086+
10861087
let mut recollect_without = |collector: &mut Self, item_tree| {
10871088
// Remove the original directive since we resolved it.
10881089
let mod_dir = collector.mod_dirs[&directive.module_id].clone();
@@ -1100,8 +1101,8 @@ impl DefCollector<'_> {
11001101
false
11011102
};
11021103

1103-
if let Some(ident) = ast_id.path.as_ident() {
1104-
if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id.ast_id) {
1104+
if let Some(ident) = path.as_ident() {
1105+
if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id) {
11051106
if helpers.contains(ident) {
11061107
cov_mark::hit!(resolved_derive_helper);
11071108
// Resolved to derive helper. Collect the item's attributes again,
@@ -1112,7 +1113,7 @@ impl DefCollector<'_> {
11121113
}
11131114
}
11141115

1115-
let def = resolver(ast_id.path.clone()).filter(MacroDefId::is_attribute);
1116+
let def = resolver(path.clone()).filter(MacroDefId::is_attribute);
11161117
if matches!(
11171118
def,
11181119
Some(MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. })
@@ -1121,26 +1122,23 @@ impl DefCollector<'_> {
11211122
// Resolved to `#[derive]`
11221123
let item_tree = self.db.file_item_tree(file_id);
11231124

1124-
let ast_id: FileAstId<ast::Item> = match *mod_item {
1125-
ModItem::Struct(it) => item_tree[it].ast_id.upcast(),
1126-
ModItem::Union(it) => item_tree[it].ast_id.upcast(),
1127-
ModItem::Enum(it) => item_tree[it].ast_id.upcast(),
1125+
match mod_item {
1126+
ModItem::Struct(_) | ModItem::Union(_) | ModItem::Enum(_) => (),
11281127
_ => {
11291128
let diag = DefDiagnostic::invalid_derive_target(
11301129
directive.module_id,
1131-
ast_id.ast_id,
1130+
ast_id,
11321131
attr.id,
11331132
);
11341133
self.def_map.diagnostics.push(diag);
1135-
res = ReachedFixedPoint::No;
1136-
return false;
1134+
return recollect_without(self, &item_tree);
11371135
}
1138-
};
1136+
}
11391137

11401138
match attr.parse_derive() {
11411139
Some(derive_macros) => {
11421140
for path in derive_macros {
1143-
let ast_id = AstIdWithPath::new(file_id, ast_id, path);
1141+
let ast_id = AstIdWithPath::new(file_id, ast_id.value, path);
11441142
self.unresolved_macros.push(MacroDirective {
11451143
module_id: directive.module_id,
11461144
depth: directive.depth + 1,
@@ -1152,8 +1150,12 @@ impl DefCollector<'_> {
11521150
}
11531151
}
11541152
None => {
1155-
// FIXME: diagnose
1156-
tracing::debug!("malformed derive: {:?}", attr);
1153+
let diag = DefDiagnostic::malformed_derive(
1154+
directive.module_id,
1155+
ast_id,
1156+
attr.id,
1157+
);
1158+
self.def_map.diagnostics.push(diag);
11571159
}
11581160
}
11591161

@@ -1165,7 +1167,8 @@ impl DefCollector<'_> {
11651167
}
11661168

11671169
// Not resolved to a derive helper or the derive attribute, so try to resolve as a normal attribute.
1168-
match attr_macro_as_call_id(ast_id, attr, self.db, self.def_map.krate, def) {
1170+
match attr_macro_as_call_id(file_ast_id, attr, self.db, self.def_map.krate, def)
1171+
{
11691172
Ok(call_id) => {
11701173
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
11711174

@@ -1198,7 +1201,7 @@ impl DefCollector<'_> {
11981201

11991202
self.def_map.modules[directive.module_id]
12001203
.scope
1201-
.add_attr_macro_invoc(ast_id.ast_id, call_id);
1204+
.add_attr_macro_invoc(ast_id, call_id);
12021205

12031206
resolved.push((directive.module_id, call_id, directive.depth));
12041207
res = ReachedFixedPoint::No;

crates/hir_def/src/nameres/diagnostics.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub enum DefDiagnosticKind {
3232
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
3333

3434
InvalidDeriveTarget { ast: AstId<ast::Item>, id: u32 },
35+
36+
MalformedDerive { ast: AstId<ast::Item>, id: u32 },
3537
}
3638

3739
#[derive(Debug, PartialEq, Eq)]
@@ -116,4 +118,15 @@ impl DefDiagnostic {
116118
kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id: id.ast_index },
117119
}
118120
}
121+
122+
pub(super) fn malformed_derive(
123+
container: LocalModuleId,
124+
ast: AstId<ast::Item>,
125+
id: AttrId,
126+
) -> Self {
127+
Self {
128+
in_module: container,
129+
kind: DefDiagnosticKind::MalformedDerive { ast, id: id.ast_index },
130+
}
131+
}
119132
}

crates/ide_diagnostics/src/handlers/invalid_derive_target.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ pub(crate) fn invalid_derive_target(
88
ctx: &DiagnosticsContext<'_>,
99
d: &hir::InvalidDeriveTarget,
1010
) -> Diagnostic {
11-
// Use more accurate position if available.
1211
let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
1312

1413
Diagnostic::new(
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use crate::{Diagnostic, DiagnosticsContext, Severity};
2+
3+
// Diagnostic: malformed-derive
4+
//
5+
// This diagnostic is shown when the derive attribute has invalid input.
6+
pub(crate) fn malformed_derive(
7+
ctx: &DiagnosticsContext<'_>,
8+
d: &hir::MalformedDerive,
9+
) -> Diagnostic {
10+
let display_range = ctx.sema.diagnostics_display_range(d.node.clone()).range;
11+
12+
Diagnostic::new(
13+
"malformed-derive",
14+
"malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`",
15+
display_range,
16+
)
17+
.severity(Severity::Error)
18+
}
19+
20+
#[cfg(test)]
21+
mod tests {
22+
use crate::tests::check_diagnostics;
23+
24+
#[test]
25+
fn invalid_input() {
26+
check_diagnostics(
27+
r#"
28+
//- minicore:derive
29+
mod __ {
30+
#[derive = "aaaa"]
31+
//^^^^^^^^^^^^^^^^^^ error: malformed derive input, derive attributes are of the form `#[derive(Derive1, Derive2, ...)]`
32+
struct Foo;
33+
}
34+
"#,
35+
);
36+
}
37+
}

crates/ide_diagnostics/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ mod handlers {
3030
pub(crate) mod incorrect_case;
3131
pub(crate) mod invalid_derive_target;
3232
pub(crate) mod macro_error;
33+
pub(crate) mod malformed_derive;
3334
pub(crate) mod mismatched_arg_count;
3435
pub(crate) mod missing_fields;
3536
pub(crate) mod missing_match_arms;
@@ -182,6 +183,7 @@ pub fn diagnostics(
182183
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
183184
AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
184185
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
186+
AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
185187
AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
186188
AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
187189
AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),

0 commit comments

Comments
 (0)