Skip to content

Commit d2e149c

Browse files
committed
Add diagnostic-item support for assert macros
Recognize `{static,const,build}_assert!` as special items, preferring explicit `#[klint::diagnostic_item]` annotations and keeping a conservative kernel-specific fallback for older trees. The fallback resolves known macro paths structurally, extending the existing diagnostic-item discovery pattern so later lints can identify assert macros semantically. Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
1 parent 9b2763f commit d2e149c

2 files changed

Lines changed: 99 additions & 20 deletions

File tree

Lines changed: 96 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
//! Out-of-band attributes attached without source code changes.
22
3-
use rustc_hir::def::{DefKind, Res};
4-
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE};
3+
use rustc_hir::def::DefKind;
4+
use rustc_hir::def::Res;
5+
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
56
use rustc_hir::diagnostic_items::DiagnosticItems;
67
use rustc_middle::middle::exported_symbols::ExportedSymbol;
78
use rustc_middle::ty::TyCtxt;
9+
use rustc_span::Symbol;
810

911
pub fn infer_missing_items<'tcx>(tcx: TyCtxt<'tcx>, items: &mut DiagnosticItems) {
10-
if !items.name_to_id.contains_key(&crate::symbol::build_error) {
11-
if let Some(def_id) = infer_build_error_diagnostic_item(tcx) {
12-
super::collect_item(tcx, items, crate::symbol::build_error, def_id);
13-
}
12+
if !items.name_to_id.contains_key(&crate::symbol::build_error)
13+
&& let Some(def_id) = infer_build_error_diagnostic_item(tcx)
14+
{
15+
super::collect_item(tcx, items, crate::symbol::build_error, def_id);
1416
}
1517

16-
if !items.name_to_id.contains_key(&crate::symbol::c_str) {
17-
if let Some(def_id) = infer_c_str_diagnostic_item(tcx) {
18-
super::collect_item(tcx, items, crate::symbol::c_str, def_id);
19-
}
18+
if !items.name_to_id.contains_key(&crate::symbol::build_assert)
19+
&& let Some(def_id) = infer_build_assert_diagnostic_item(tcx)
20+
{
21+
super::collect_item(tcx, items, crate::symbol::build_assert, def_id);
22+
}
23+
24+
if !items.name_to_id.contains_key(&crate::symbol::c_str)
25+
&& let Some(def_id) = infer_c_str_diagnostic_item(tcx)
26+
{
27+
super::collect_item(tcx, items, crate::symbol::c_str, def_id);
2028
}
2129
}
2230

@@ -32,21 +40,89 @@ pub fn infer_build_error_diagnostic_item<'tcx>(tcx: TyCtxt<'tcx>) -> Option<DefI
3240
None
3341
}
3442

35-
pub fn infer_c_str_diagnostic_item<'tcx>(tcx: TyCtxt<'tcx>) -> Option<DefId> {
43+
fn infer_local_macro_diagnostic_item<'tcx>(
44+
tcx: TyCtxt<'tcx>,
45+
expected_path: &[PathSegment],
46+
) -> Option<DefId> {
47+
let (root, rest) = expected_path.split_first()?;
48+
let PathSegment::Type(root) = root else {
49+
return None;
50+
};
51+
52+
if *root != tcx.crate_name(LOCAL_CRATE) {
53+
return None;
54+
}
55+
56+
lookup_with_base(tcx, LOCAL_CRATE.as_def_id(), rest)
57+
}
58+
59+
#[derive(Clone, Copy)]
60+
enum PathSegment {
61+
Type(Symbol),
62+
Macro(Symbol),
63+
}
64+
65+
fn lookup_with_base<'tcx>(tcx: TyCtxt<'tcx>, base: DefId, path: &[PathSegment]) -> Option<DefId> {
66+
let (segment, rest) = path.split_first()?;
67+
68+
let mut matches = tcx.module_children(base).iter().filter_map(|child| {
69+
let Res::Def(kind, def_id) = child.res else {
70+
return None;
71+
};
72+
73+
match (*segment, kind, child.ident.name) {
74+
(PathSegment::Type(expected), DefKind::Mod, actual) if actual == expected => {
75+
Some(def_id)
76+
}
77+
(PathSegment::Macro(expected), DefKind::Macro(_), actual) if actual == expected => {
78+
Some(def_id)
79+
}
80+
_ => None,
81+
}
82+
});
83+
84+
let def_id = matches.next()?;
85+
86+
if matches.next().is_some() {
87+
return None;
88+
}
89+
90+
if rest.is_empty() {
91+
Some(def_id)
92+
} else {
93+
lookup_with_base(tcx, def_id, rest)
94+
}
95+
}
96+
97+
pub fn infer_build_assert_diagnostic_item<'tcx>(tcx: TyCtxt<'tcx>) -> Option<DefId> {
3698
let name = tcx.crate_name(LOCAL_CRATE);
3799

38100
if name != crate::symbol::kernel {
39101
return None;
40102
}
41103

42-
let c_str = tcx
43-
.module_children_local(CRATE_DEF_ID)
44-
.iter()
45-
.find(|c| {
46-
c.ident.name == crate::symbol::c_str && matches!(c.res, Res::Def(DefKind::Macro(_), _))
47-
})?
48-
.res
49-
.def_id();
104+
infer_local_macro_diagnostic_item(
105+
tcx,
106+
&[
107+
PathSegment::Type(crate::symbol::kernel),
108+
PathSegment::Type(rustc_span::sym::prelude),
109+
PathSegment::Macro(crate::symbol::build_assert),
110+
],
111+
)
112+
}
113+
114+
pub fn infer_c_str_diagnostic_item<'tcx>(tcx: TyCtxt<'tcx>) -> Option<DefId> {
115+
let name = tcx.crate_name(LOCAL_CRATE);
116+
117+
if name != crate::symbol::kernel {
118+
return None;
119+
}
50120

51-
Some(c_str)
121+
infer_local_macro_diagnostic_item(
122+
tcx,
123+
&[
124+
PathSegment::Type(crate::symbol::kernel),
125+
PathSegment::Macro(crate::symbol::c_str),
126+
],
127+
)
52128
}

src/symbol.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ def! {
5555
// Diagnostic items
5656
c_str,
5757
build_error,
58+
build_assert,
59+
const_assert,
60+
static_assert,
5861

5962
CONFIG_FRAME_WARN,
6063
}

0 commit comments

Comments
 (0)