-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathdisallowed_macros.rs
More file actions
192 lines (172 loc) · 6.47 KB
/
disallowed_macros.rs
File metadata and controls
192 lines (172 loc) · 6.47 KB
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
185
186
187
188
189
190
191
192
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 clippy_utils::paths::PathNS;
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, ImplItemImplKind, 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" },
/// # This would normally error if the path is incorrect, but with `allow-invalid` = `true`,
/// # it will be silently ignored
/// { path = "std::invalid_macro", reason = "use alternative instead", allow-invalid = true }
/// ]
/// ```
/// ```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"
}
impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]);
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.
early_macro_cache: AttrStorage,
}
impl DisallowedMacros {
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, early_macro_cache: AttrStorage) -> Self {
let (disallowed, _) = create_disallowed_map(
tcx,
&conf.disallowed_macros,
PathNS::Macro,
|def_kind| matches!(def_kind, DefKind::Macro(_)),
"macro",
false,
);
Self {
disallowed,
seen: FxHashSet::default(),
derive_src: None,
early_macro_cache,
}
}
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 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.early_macro_cache.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);
if let ImplItemImplKind::Inherent { vis_span, .. } = item.impl_kind {
self.check(cx, 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);
}
}