Skip to content

Commit f6b47b5

Browse files
committed
Improving is_from_proc_macro
Finally, after a few months I finally release this 0.9.9 version of the final 'is_from_proc_macro'. The main changes happen on: - Moving 'check_from_proc_macro.rs' from Clippy to rustc_lint - Applying a Jarcho's (yet to be merged) fix to that file (See rust-lang/rust-clippy#11538) - Change every occurence of 'is_from_proc_macro' to 'cx.in_proc_macro' - Added a new macro to 'rustc_lint/late.rs', now it checks if we're in Clippy, if we are indeed in Clippy, check if we're in a proc macro. - Apply some parallization to the checking with already provided functions (nothing too fancy, just using a parallization function) - Fix a bug on 'min_ident_chars.rs' - A few more things I don't remember... You can test this commit with 'RUST_BACKTRACE=full x test src/tools/clippy --keep-stage=2'
1 parent 67ad3c2 commit f6b47b5

31 files changed

+224
-99
lines changed

src/tools/clippy/clippy_utils/src/check_proc_macro.rs renamed to compiler/rustc_lint/src/check_proc_macro.rs

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@
1212
//! code was written, and check if the span contains that text. Note this will only work correctly
1313
//! if the span is not from a `macro_rules` based macro.
1414
15-
use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, UintTy};
15+
use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy};
1616
use rustc_ast::token::CommentKind;
1717
use rustc_ast::AttrStyle;
1818
use rustc_hir::intravisit::FnKind;
1919
use rustc_hir::{
20-
Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, HirId, Impl, ImplItem,
21-
ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, MutTy, Node, QPath, TraitItem, TraitItemKind, Ty,
22-
TyKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource,
20+
Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl,
21+
ImplItem, ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, MutTy, Node, QPath, TraitItem,
22+
TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource,
2323
};
24-
use rustc_lint::{LateContext, LintContext};
24+
use crate::{LateContext, LintContext};
2525
use rustc_middle::ty::TyCtxt;
2626
use rustc_session::Session;
2727
use rustc_span::symbol::Ident;
@@ -33,8 +33,6 @@ use rustc_target::spec::abi::Abi;
3333
pub enum Pat {
3434
/// A single string.
3535
Str(&'static str),
36-
/// A single string.
37-
OwnedStr(String),
3836
/// Any of the given strings.
3937
MultiStr(&'static [&'static str]),
4038
/// Any of the given strings.
@@ -47,7 +45,7 @@ pub enum Pat {
4745

4846
/// Checks if the start and the end of the span's text matches the patterns. This will return false
4947
/// if the span crosses multiple files or if source is not available.
50-
fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> bool {
48+
pub fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> bool {
5149
let pos = sess.source_map().lookup_byte_offset(span.lo());
5250
let Some(ref src) = pos.sf.src else {
5351
return false;
@@ -59,14 +57,12 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
5957
let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
6058
(match start_pat {
6159
Pat::Str(text) => start_str.starts_with(text),
62-
Pat::OwnedStr(text) => start_str.starts_with(&text),
6360
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
6461
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
6562
Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
6663
Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
6764
} && match end_pat {
6865
Pat::Str(text) => end_str.ends_with(text),
69-
Pat::OwnedStr(text) => end_str.starts_with(&text),
7066
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)),
7167
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
7268
Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
@@ -125,6 +121,7 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
125121
fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
126122
match e.kind {
127123
ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
124+
// Parenthesis are skipped before the patterns are matched.
128125
ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
129126
ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat(tcx, e).1),
130127
ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat(tcx, e).1),
@@ -286,21 +283,17 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI
286283
fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
287284
match attr.kind {
288285
AttrKind::Normal(..) => {
289-
let mut pat = if matches!(attr.style, AttrStyle::Outer) {
290-
(Pat::Str("#["), Pat::Str("]"))
291-
} else {
292-
(Pat::Str("#!["), Pat::Str("]"))
293-
};
294-
295-
if let Some(ident) = attr.ident() && let Pat::Str(old_pat) = pat.0 {
286+
if let Some(ident) = attr.ident() {
296287
// TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of
297288
// refactoring
298289
// NOTE: This will likely have false positives, like `allow = 1`
299-
pat.0 = Pat::OwnedMultiStr(vec![ident.to_string(), old_pat.to_owned()]);
300-
pat.1 = Pat::Str("");
290+
(
291+
Pat::OwnedMultiStr(vec![ident.to_string(), "#".to_owned()]),
292+
Pat::Str(""),
293+
)
294+
} else {
295+
(Pat::Str("#"), Pat::Str("]"))
301296
}
302-
303-
pat
304297
},
305298
AttrKind::DocComment(_kind @ CommentKind::Line, ..) => {
306299
if matches!(attr.style, AttrStyle::Outer) {
@@ -322,32 +315,41 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
322315
fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
323316
match ty.kind {
324317
TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")),
325-
TyKind::Ptr(MutTy { mutbl, ty }) => (
326-
if mutbl.is_mut() {
327-
Pat::Str("*const")
328-
} else {
329-
Pat::Str("*mut")
330-
},
331-
ty_search_pat(ty).1,
332-
),
318+
TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1),
333319
TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1),
334320
TyKind::BareFn(bare_fn) => (
335-
Pat::OwnedStr(format!("{}{} fn", bare_fn.unsafety.prefix_str(), bare_fn.abi.name())),
336-
ty_search_pat(ty).1,
321+
if bare_fn.unsafety == Unsafety::Unsafe {
322+
Pat::Str("unsafe")
323+
} else if bare_fn.abi != Abi::Rust {
324+
Pat::Str("extern")
325+
} else {
326+
Pat::MultiStr(&["fn", "extern"])
327+
},
328+
match bare_fn.decl.output {
329+
FnRetTy::DefaultReturn(_) => {
330+
if let [.., ty] = bare_fn.decl.inputs {
331+
ty_search_pat(ty).1
332+
} else {
333+
Pat::Str("(")
334+
}
335+
},
336+
FnRetTy::Return(ty) => ty_search_pat(ty).1,
337+
},
337338
),
338-
TyKind::Never => (Pat::Str("!"), Pat::Str("")),
339-
TyKind::Tup(..) => (Pat::Str("("), Pat::Str(")")),
339+
TyKind::Never => (Pat::Str("!"), Pat::Str("!")),
340+
// Parenthesis are skipped before the patterns are matched.
341+
TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
342+
TyKind::Tup([ty]) => ty_search_pat(ty),
343+
TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1),
340344
TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")),
341345
TyKind::Path(qpath) => qpath_search_pat(&qpath),
342-
// NOTE: This is missing `TraitObject`. It will always return true then.
346+
TyKind::Infer => (Pat::Str("_"), Pat::Str("_")),
347+
TyKind::TraitObject(_, _, TraitObjectSyntax::Dyn) => (Pat::Str("dyn"), Pat::Str("")),
348+
// NOTE: `TraitObject` is incomplete. It will always return true then.
343349
_ => (Pat::Str(""), Pat::Str("")),
344350
}
345351
}
346352

347-
fn ident_search_pat(ident: Ident) -> (Pat, Pat) {
348-
(Pat::OwnedStr(ident.name.as_str().to_owned()), Pat::Str(""))
349-
}
350-
351353
pub trait WithSearchPat<'cx> {
352354
type Context: LintContext;
353355
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat);
@@ -406,7 +408,7 @@ impl<'cx> WithSearchPat<'cx> for Ident {
406408
type Context = LateContext<'cx>;
407409

408410
fn search_pat(&self, _cx: &Self::Context) -> (Pat, Pat) {
409-
ident_search_pat(*self)
411+
(Pat::Sym(self.name), Pat::Sym(self.name))
410412
}
411413

412414
fn span(&self) -> Span {
@@ -431,4 +433,4 @@ pub fn is_span_match(cx: &impl LintContext, span: Span) -> bool {
431433
/// Checks if the span actually refers to an if expression
432434
pub fn is_span_if(cx: &impl LintContext, span: Span) -> bool {
433435
span_matches_pat(cx.sess(), span, Pat::Str("if"), Pat::Str("}"))
434-
}
436+
}

compiler/rustc_lint/src/context.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,12 @@ pub struct LateContext<'tcx> {
509509

510510
/// We are only looking at one module
511511
pub only_module: bool,
512+
513+
/// If we're in a procedural macro expansion
514+
pub in_proc_macro: bool,
515+
516+
/// If the user executed `cargo clippy` (env. var. `CLIPPY_ARGS` is always defined)
517+
pub(super) in_clippy: bool
512518
}
513519

514520
/// Context for lint checking of the AST, after expansion, before lowering to HIR.

compiler/rustc_lint/src/late.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
//! upon. As the ast is traversed, this keeps track of the current lint level
1515
//! for all lint attributes.
1616
17-
use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore};
17+
use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore, is_from_proc_macro};
18+
use hir::HirId;
1819
use rustc_ast as ast;
1920
use rustc_data_structures::stack::ensure_sufficient_stack;
2021
use rustc_data_structures::sync::join;
@@ -28,6 +29,11 @@ use rustc_span::Span;
2829

2930
use std::any::Any;
3031
use std::cell::Cell;
32+
use std::env;
33+
34+
thread_local! {
35+
static MEOW: Cell<[HirId; 8]> = Cell::new([HirId::INVALID; 8]);
36+
}
3137

3238
/// Extract the `LintStore` from the query context.
3339
/// This function exists because we've erased `LintStore` as `dyn Any` in the context.
@@ -36,6 +42,53 @@ pub fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore {
3642
store.downcast_ref().unwrap()
3743
}
3844

45+
/// This macro checks if we're in Clippy, if that's the case,
46+
/// we'll check if the current token to be visited is in a proc macro.
47+
///
48+
/// This is useful for Clippy, so several lints that share the same token, can
49+
/// also share the `is_from_proc_macro` evaluation. We only evaluate if the two
50+
/// HirIds share parent and we're in a Clippy execution.
51+
///
52+
/// Each token is assigned a number, this number will be the index in the MEOW static
53+
/// `Cell`, so each token can evaluated separately (but still efficiently).
54+
///
55+
/// Indexes:
56+
///
57+
/// - Item: 0
58+
/// - Expr: 1
59+
/// - FieldDef: 2
60+
/// - Variant: 3
61+
/// - Type: 4
62+
/// - TraitItem: 5
63+
/// - ImplItem: 6
64+
/// - Attribute: 7
65+
///
66+
/// These indexes aren't in any particular order, they're
67+
/// better treated as separate variables packed in an array for
68+
/// the sake of optimization.
69+
macro_rules! clippy_in_proc_macro {
70+
($cx:expr, $it:expr, $id:expr, $meow:expr) => {
71+
if $cx.context.in_clippy {
72+
let parent_id = $cx.context.tcx.hir().parent_id($id);
73+
let meow = MEOW.get();
74+
if meow[$meow] != parent_id {
75+
$cx.context.in_proc_macro = is_from_proc_macro(&$cx.context, $it);
76+
let mut new_meow = meow;
77+
new_meow[$meow] = parent_id;
78+
MEOW.set(new_meow);
79+
} else if rustc_index::Idx::index(parent_id.owner) == 0 {
80+
$cx.context.in_proc_macro = is_from_proc_macro(&$cx.context, $it);
81+
};
82+
}
83+
};
84+
85+
($cx:expr, $it:expr, NO ID, $meow:expr) => {
86+
if $cx.context.in_clippy {
87+
$cx.context.in_proc_macro = is_from_proc_macro(&$cx.context, $it);
88+
}
89+
}
90+
}
91+
3992
macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({
4093
$cx.pass.$f(&$cx.context, $($args),*);
4194
}) }
@@ -133,6 +186,9 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
133186
self.context.generics = it.kind.generics();
134187
let old_cached_typeck_results = self.context.cached_typeck_results.take();
135188
let old_enclosing_body = self.context.enclosing_body.take();
189+
190+
clippy_in_proc_macro!(self, it, it.hir_id(), 0);
191+
136192
self.with_lint_attrs(it.hir_id(), |cx| {
137193
cx.with_param_env(it.owner_id, |cx| {
138194
lint_callback!(cx, check_item, it);
@@ -164,6 +220,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
164220
}
165221

166222
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
223+
clippy_in_proc_macro!(self, e, e.hir_id, 1);
167224
ensure_sufficient_stack(|| {
168225
self.with_lint_attrs(e.hir_id, |cx| {
169226
lint_callback!(cx, check_expr, e);
@@ -207,20 +264,23 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
207264
}
208265

209266
fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
267+
clippy_in_proc_macro!(self, s, s.hir_id, 2);
210268
self.with_lint_attrs(s.hir_id, |cx| {
211269
lint_callback!(cx, check_field_def, s);
212270
hir_visit::walk_field_def(cx, s);
213271
})
214272
}
215273

216274
fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
275+
clippy_in_proc_macro!(self, v, v.hir_id, 3);
217276
self.with_lint_attrs(v.hir_id, |cx| {
218277
lint_callback!(cx, check_variant, v);
219278
hir_visit::walk_variant(cx, v);
220279
})
221280
}
222281

223282
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
283+
clippy_in_proc_macro!(self, t, t.hir_id, 4);
224284
lint_callback!(self, check_ty, t);
225285
hir_visit::walk_ty(self, t);
226286
}
@@ -277,6 +337,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
277337
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
278338
let generics = self.context.generics.take();
279339
self.context.generics = Some(&trait_item.generics);
340+
clippy_in_proc_macro!(self, trait_item, trait_item.hir_id(), 5);
280341
self.with_lint_attrs(trait_item.hir_id(), |cx| {
281342
cx.with_param_env(trait_item.owner_id, |cx| {
282343
lint_callback!(cx, check_trait_item, trait_item);
@@ -289,6 +350,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
289350
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
290351
let generics = self.context.generics.take();
291352
self.context.generics = Some(&impl_item.generics);
353+
clippy_in_proc_macro!(self, impl_item, impl_item.hir_id(), 6);
292354
self.with_lint_attrs(impl_item.hir_id(), |cx| {
293355
cx.with_param_env(impl_item.owner_id, |cx| {
294356
lint_callback!(cx, check_impl_item, impl_item);
@@ -309,6 +371,12 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
309371
}
310372

311373
fn visit_attribute(&mut self, attr: &'tcx ast::Attribute) {
374+
clippy_in_proc_macro!(
375+
self,
376+
&attr,
377+
NO ID, // Attributes don't have HirIds
378+
7
379+
);
312380
lint_callback!(self, check_attribute, attr);
313381
}
314382
}
@@ -357,6 +425,8 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
357425
last_node_with_lint_attrs: tcx.hir().local_def_id_to_hir_id(module_def_id),
358426
generics: None,
359427
only_module: true,
428+
in_proc_macro: false,
429+
in_clippy: env::var("CLIPPY_ARGS").is_ok()
360430
};
361431

362432
// Note: `passes` is often empty. In that case, it's faster to run
@@ -416,6 +486,8 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
416486
last_node_with_lint_attrs: hir::CRATE_HIR_ID,
417487
generics: None,
418488
only_module: false,
489+
in_proc_macro: false,
490+
in_clippy: env::var("CLIPPY_ARGS").is_ok()
419491
};
420492

421493
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };

compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#![feature(iter_intersperse)]
3535
#![feature(iter_order_by)]
3636
#![feature(let_chains)]
37+
#![feature(local_key_cell_methods)]
3738
#![feature(min_specialization)]
3839
#![feature(never_type)]
3940
#![feature(rustc_attrs)]
@@ -64,6 +65,7 @@ pub mod hidden_unicode_codepoints;
6465
mod internal;
6566
mod invalid_from_utf8;
6667
mod late;
68+
pub mod check_proc_macro;
6769
mod let_underscore;
6870
mod levels;
6971
mod lints;
@@ -135,6 +137,7 @@ pub use passes::{EarlyLintPass, LateLintPass};
135137
pub use rustc_session::lint::Level::{self, *};
136138
pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId};
137139
pub use rustc_session::lint::{LintArray, LintPass};
140+
pub use check_proc_macro::is_from_proc_macro;
138141

139142
fluent_messages! { "../messages.ftl" }
140143

src/tools/clippy/clippy_lints/src/allow_attributes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use ast::{AttrStyle, Attribute};
22
use clippy_utils::diagnostics::span_lint_and_sugg;
3-
use clippy_utils::is_from_proc_macro;
3+
44
use rustc_ast as ast;
55
use rustc_errors::Applicability;
66
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -58,7 +58,7 @@ impl LateLintPass<'_> for AllowAttribute {
5858
if let AttrStyle::Outer = attr.style;
5959
if let Some(ident) = attr.ident();
6060
if ident.name == rustc_span::symbol::sym::allow;
61-
if !is_from_proc_macro(cx, &attr);
61+
if !cx.in_proc_macro;
6262
then {
6363
span_lint_and_sugg(
6464
cx,

0 commit comments

Comments
 (0)