Merge #10804
10804: fix: Diagnose using `derive` on non-adt items r=Veykril a=Veykril Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
2fafe0e37c
@ -32,6 +32,7 @@ fn from(d: $diag) -> AnyDiagnostic {
|
||||
BreakOutsideOfLoop,
|
||||
InactiveCode,
|
||||
IncorrectCase,
|
||||
InvalidDeriveTarget,
|
||||
MacroError,
|
||||
MismatchedArgCount,
|
||||
MissingFields,
|
||||
@ -98,6 +99,11 @@ pub struct UnimplementedBuiltinMacro {
|
||||
pub node: InFile<SyntaxNodePtr>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidDeriveTarget {
|
||||
pub node: InFile<SyntaxNodePtr>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NoSuchField {
|
||||
pub field: InFile<AstPtr<ast::RecordExprField>>,
|
||||
|
@ -83,10 +83,10 @@
|
||||
attrs::{HasAttrs, Namespace},
|
||||
diagnostics::{
|
||||
AddReferenceHere, AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase,
|
||||
MacroError, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
|
||||
MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
|
||||
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
|
||||
UnresolvedModule, UnresolvedProcMacro,
|
||||
InvalidDeriveTarget, MacroError, MismatchedArgCount, MissingFields, MissingMatchArms,
|
||||
MissingOkOrSomeInTailExpr, MissingUnsafe, NoSuchField, RemoveThisSemicolon,
|
||||
ReplaceFilterMapNextWithFindMap, UnimplementedBuiltinMacro, UnresolvedExternCrate,
|
||||
UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
|
||||
},
|
||||
has_source::HasSource,
|
||||
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo},
|
||||
@ -654,6 +654,21 @@ pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
DefDiagnosticKind::InvalidDeriveTarget { ast, id } => {
|
||||
let node = ast.to_node(db.upcast());
|
||||
let derive = node.attrs().nth(*id as usize);
|
||||
match derive {
|
||||
Some(derive) => {
|
||||
acc.push(
|
||||
InvalidDeriveTarget {
|
||||
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) {
|
||||
|
@ -1055,13 +1055,10 @@ fn resolve_macros(&mut self) -> ReachedFixedPoint {
|
||||
&resolver,
|
||||
&mut |_err| (),
|
||||
);
|
||||
match call_id {
|
||||
Ok(Ok(call_id)) => {
|
||||
resolved.push((directive.module_id, call_id, directive.depth));
|
||||
res = ReachedFixedPoint::No;
|
||||
return false;
|
||||
}
|
||||
Err(UnresolvedMacro { .. }) | Ok(Err(_)) => {}
|
||||
if let Ok(Ok(call_id)) = call_id {
|
||||
resolved.push((directive.module_id, call_id, directive.depth));
|
||||
res = ReachedFixedPoint::No;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
MacroDirectiveKind::Derive { ast_id, derive_attr } => {
|
||||
@ -1072,19 +1069,16 @@ fn resolve_macros(&mut self) -> ReachedFixedPoint {
|
||||
self.def_map.krate,
|
||||
&resolver,
|
||||
);
|
||||
match call_id {
|
||||
Ok(call_id) => {
|
||||
self.def_map.modules[directive.module_id].scope.add_derive_macro_invoc(
|
||||
ast_id.ast_id,
|
||||
call_id,
|
||||
*derive_attr,
|
||||
);
|
||||
if let Ok(call_id) = call_id {
|
||||
self.def_map.modules[directive.module_id].scope.add_derive_macro_invoc(
|
||||
ast_id.ast_id,
|
||||
call_id,
|
||||
*derive_attr,
|
||||
);
|
||||
|
||||
resolved.push((directive.module_id, call_id, directive.depth));
|
||||
res = ReachedFixedPoint::No;
|
||||
return false;
|
||||
}
|
||||
Err(UnresolvedMacro { .. }) => {}
|
||||
resolved.push((directive.module_id, call_id, directive.depth));
|
||||
res = ReachedFixedPoint::No;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
MacroDirectiveKind::Attr { ast_id, mod_item, attr } => {
|
||||
@ -1125,7 +1119,6 @@ fn resolve_macros(&mut self) -> ReachedFixedPoint {
|
||||
if expander.is_derive()
|
||||
) {
|
||||
// Resolved to `#[derive]`
|
||||
let file_id = ast_id.ast_id.file_id;
|
||||
let item_tree = self.db.file_item_tree(file_id);
|
||||
|
||||
let ast_id: FileAstId<ast::Item> = match *mod_item {
|
||||
@ -1133,8 +1126,12 @@ fn resolve_macros(&mut self) -> ReachedFixedPoint {
|
||||
ModItem::Union(it) => item_tree[it].ast_id.upcast(),
|
||||
ModItem::Enum(it) => item_tree[it].ast_id.upcast(),
|
||||
_ => {
|
||||
// Cannot use derive on this item.
|
||||
// FIXME: diagnose
|
||||
let diag = DefDiagnostic::invalid_derive_target(
|
||||
directive.module_id,
|
||||
ast_id.ast_id,
|
||||
attr.id,
|
||||
);
|
||||
self.def_map.diagnostics.push(diag);
|
||||
res = ReachedFixedPoint::No;
|
||||
return false;
|
||||
}
|
||||
@ -1194,7 +1191,6 @@ fn resolve_macros(&mut self) -> ReachedFixedPoint {
|
||||
),
|
||||
);
|
||||
|
||||
let file_id = ast_id.ast_id.file_id;
|
||||
let item_tree = self.db.file_item_tree(file_id);
|
||||
return recollect_without(self, &item_tree);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
attr::AttrId,
|
||||
item_tree::{self, ItemTreeId},
|
||||
nameres::LocalModuleId,
|
||||
path::ModPath,
|
||||
@ -29,6 +30,8 @@ pub enum DefDiagnosticKind {
|
||||
MacroError { ast: MacroCallKind, message: String },
|
||||
|
||||
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
|
||||
|
||||
InvalidDeriveTarget { ast: AstId<ast::Item>, id: u32 },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
@ -102,4 +105,15 @@ pub(super) fn unimplemented_builtin_macro(
|
||||
) -> Self {
|
||||
Self { in_module: container, kind: DefDiagnosticKind::UnimplementedBuiltinMacro { ast } }
|
||||
}
|
||||
|
||||
pub(super) fn invalid_derive_target(
|
||||
container: LocalModuleId,
|
||||
ast: AstId<ast::Item>,
|
||||
id: AttrId,
|
||||
) -> Self {
|
||||
Self {
|
||||
in_module: container,
|
||||
kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id: id.ast_index },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
39
crates/ide_diagnostics/src/handlers/invalid_derive_target.rs
Normal file
39
crates/ide_diagnostics/src/handlers/invalid_derive_target.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use crate::{Diagnostic, DiagnosticsContext, Severity};
|
||||
|
||||
// Diagnostic: invalid-derive-target
|
||||
//
|
||||
// This diagnostic is shown when the derive attribute is used on an item other than a `struct`,
|
||||
// `enum` or `union`.
|
||||
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(
|
||||
"invalid-derive-target",
|
||||
"`derive` may only be applied to `struct`s, `enum`s and `union`s",
|
||||
display_range,
|
||||
)
|
||||
.severity(Severity::Error)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::check_diagnostics;
|
||||
|
||||
#[test]
|
||||
fn fails_on_function() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore:derive
|
||||
mod __ {
|
||||
#[derive()]
|
||||
//^^^^^^^^^^^ error: `derive` may only be applied to `struct`s, `enum`s and `union`s
|
||||
fn main() {}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ mod handlers {
|
||||
pub(crate) mod break_outside_of_loop;
|
||||
pub(crate) mod inactive_code;
|
||||
pub(crate) mod incorrect_case;
|
||||
pub(crate) mod invalid_derive_target;
|
||||
pub(crate) mod macro_error;
|
||||
pub(crate) mod mismatched_arg_count;
|
||||
pub(crate) mod missing_fields;
|
||||
@ -195,6 +196,7 @@ pub fn diagnostics(
|
||||
AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
|
||||
AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
|
||||
AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
|
||||
AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
|
||||
|
||||
AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
|
||||
Some(it) => it,
|
||||
|
Loading…
Reference in New Issue
Block a user