-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathdisallowed_macros.rs
184 lines (164 loc) · 6.02 KB
/
disallowed_macros.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
use clippy_config::Conf;
use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::macros::macro_backtrace;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefIdMap;
use rustc_hir::{
AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TyCtxt;
use rustc_session::impl_lint_pass;
use rustc_span::{ExpnId, MacroKind, Span};
use crate::utils::attr_collector::AttrStorage;
declare_clippy_lint! {
/// ### What it does
/// Denies the configured macros in clippy.toml
///
/// Note: Even though this lint is warn-by-default, it will only trigger if
/// macros are defined in the clippy.toml file.
///
/// ### Why is this bad?
/// Some macros are undesirable in certain contexts, and it's beneficial to
/// lint for them as needed.
///
/// ### Example
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// disallowed-macros = [
/// # Can use a string as the path of the disallowed macro.
/// "std::print",
/// # Can also use an inline table with a `path` key.
/// { path = "std::println" },
/// # When using an inline table, can add a `reason` for why the macro
/// # is disallowed.
/// { path = "serde::Serialize", reason = "no serializing" },
/// ]
/// ```
/// ```no_run
/// use serde::Serialize;
///
/// println!("warns");
///
/// // The diagnostic will contain the message "no serializing"
/// #[derive(Serialize)]
/// struct Data {
/// name: String,
/// value: usize,
/// }
/// ```
#[clippy::version = "1.66.0"]
pub DISALLOWED_MACROS,
style,
"use of a disallowed macro"
}
pub struct DisallowedMacros {
disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
seen: FxHashSet<ExpnId>,
// Track the most recently seen node that can have a `derive` attribute.
// Needed to use the correct lint level.
derive_src: Option<OwnerId>,
// When a macro is disallowed in an early pass, it's stored
// and emitted during the late pass. This happens for attributes.
earlies: AttrStorage,
}
impl DisallowedMacros {
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, earlies: AttrStorage) -> Self {
let (disallowed, _) = create_disallowed_map(
tcx,
&conf.disallowed_macros,
|def_kind| matches!(def_kind, DefKind::Macro(_)),
"macro",
false,
);
Self {
disallowed,
seen: FxHashSet::default(),
derive_src: None,
earlies,
}
}
fn check(&mut self, cx: &LateContext<'_>, span: Span, derive_src: Option<OwnerId>) {
if self.disallowed.is_empty() {
return;
}
for mac in macro_backtrace(span) {
if !self.seen.insert(mac.expn) {
return;
}
if let Some(&(path, disallowed_path)) = self.disallowed.get(&mac.def_id) {
let msg = format!("use of a disallowed macro `{path}`");
let add_note = disallowed_path.diag_amendment(mac.span);
if matches!(mac.kind, MacroKind::Derive)
&& let Some(derive_src) = derive_src
{
span_lint_hir_and_then(
cx,
DISALLOWED_MACROS,
cx.tcx.local_def_id_to_hir_id(derive_src.def_id),
mac.span,
msg,
add_note,
);
} else {
span_lint_and_then(cx, DISALLOWED_MACROS, mac.span, msg, add_note);
}
}
}
}
}
impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]);
impl LateLintPass<'_> for DisallowedMacros {
fn check_crate(&mut self, cx: &LateContext<'_>) {
// once we check a crate in the late pass we can emit the early pass lints
if let Some(attr_spans) = self.earlies.clone().0.get() {
for span in attr_spans {
self.check(cx, *span, None);
}
}
}
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
self.check(cx, expr.span, None);
// `$t + $t` can have the context of $t, check also the span of the binary operator
if let ExprKind::Binary(op, ..) = expr.kind {
self.check(cx, op.span, None);
}
}
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
self.check(cx, stmt.span, None);
}
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_, AmbigArg>) {
self.check(cx, ty.span, None);
}
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
self.check(cx, pat.span, None);
}
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
self.check(cx, item.span, self.derive_src);
self.check(cx, item.vis_span, None);
if matches!(
item.kind,
ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)
) && macro_backtrace(item.span).all(|m| !matches!(m.kind, MacroKind::Derive))
{
self.derive_src = Some(item.owner_id);
}
}
fn check_foreign_item(&mut self, cx: &LateContext<'_>, item: &ForeignItem<'_>) {
self.check(cx, item.span, None);
self.check(cx, item.vis_span, None);
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) {
self.check(cx, item.span, None);
self.check(cx, item.vis_span, None);
}
fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
self.check(cx, item.span, None);
}
fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) {
self.check(cx, path.span, None);
}
}