1
1
use clippy_utils:: diagnostics:: span_lint;
2
- use clippy_utils:: source :: snippet_opt ;
2
+ use clippy_utils:: is_from_proc_macro ;
3
3
use rustc_data_structures:: fx:: FxHashSet ;
4
4
use rustc_hir:: def:: { DefKind , Res } ;
5
5
use rustc_hir:: def_id:: { DefId , CRATE_DEF_INDEX } ;
6
6
use rustc_hir:: { HirId , ItemKind , Node , Path } ;
7
7
use rustc_lint:: { LateContext , LateLintPass } ;
8
8
use rustc_session:: impl_lint_pass;
9
9
use rustc_span:: symbol:: kw;
10
+ use rustc_span:: Symbol ;
10
11
11
12
declare_clippy_lint ! {
12
13
/// ### What it does
@@ -23,6 +24,14 @@ declare_clippy_lint! {
23
24
/// Note: One exception to this is code from macro expansion - this does not lint such cases, as
24
25
/// using absolute paths is the proper way of referencing items in one.
25
26
///
27
+ /// ### Known issues
28
+ ///
29
+ /// There are currently a few cases which are not caught by this lint:
30
+ /// * Macro calls. e.g. `path::to::macro!()`
31
+ /// * Derive macros. e.g. `#[derive(path::to::macro)]`
32
+ /// * Attribute macros. e.g. `#[path::to::macro]`
33
+ /// * Traits in disambiguated paths. e.g. `<T as path::to::trait>::item`
34
+ ///
26
35
/// ### Example
27
36
/// ```no_run
28
37
/// let x = std::f64::consts::PI;
@@ -47,54 +56,40 @@ impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
47
56
48
57
pub struct AbsolutePaths {
49
58
pub absolute_paths_max_segments : u64 ,
50
- pub absolute_paths_allowed_crates : FxHashSet < String > ,
59
+ pub absolute_paths_allowed_crates : FxHashSet < Symbol > ,
51
60
}
52
61
53
- impl LateLintPass < ' _ > for AbsolutePaths {
62
+ impl < ' tcx > LateLintPass < ' tcx > for AbsolutePaths {
54
63
// We should only lint `QPath::Resolved`s, but since `Path` is only used in `Resolved` and `UsePath`
55
64
// we don't need to use a visitor or anything as we can just check if the `Node` for `hir_id` isn't
56
65
// a `Use`
57
- #[ expect( clippy:: cast_possible_truncation) ]
58
- fn check_path ( & mut self , cx : & LateContext < ' _ > , path : & Path < ' _ > , hir_id : HirId ) {
59
- let Self {
60
- absolute_paths_max_segments,
61
- absolute_paths_allowed_crates,
62
- } = self ;
66
+ fn check_path ( & mut self , cx : & LateContext < ' tcx > , path : & Path < ' tcx > , hir_id : HirId ) {
67
+ let ( first, len) = match path. segments {
68
+ // Any path starting with `::` must have the second segment refer to an external crate.
69
+ // Make sure not to include the `::` segment when counting the segments.
70
+ [ root, first, ..] if root. ident . name == kw:: PathRoot => ( first, path. segments . len ( ) - 1 ) ,
71
+ [ first, _, ..]
72
+ if first. ident . name == kw:: Crate
73
+ || matches ! ( first. res, Res :: Def ( DefKind :: Mod , DefId { index, .. } ) if index == CRATE_DEF_INDEX ) =>
74
+ {
75
+ ( first, path. segments . len ( ) )
76
+ } ,
77
+ _ => return ,
78
+ } ;
63
79
64
- if !path. span . from_expansion ( )
80
+ if len as u64 > self . absolute_paths_max_segments
81
+ && !path. span . from_expansion ( )
65
82
&& let node = cx. tcx . hir_node ( hir_id)
66
- && !matches ! ( node, Node :: Item ( item) if matches!( item. kind, ItemKind :: Use ( _, _) ) )
67
- && let [ first, rest @ ..] = path. segments
68
- // Handle `::std`
69
- && let ( segment, len) = if first. ident . name == kw:: PathRoot {
70
- // Indexing is fine as `PathRoot` must be followed by another segment. `len() - 1`
71
- // is fine here for the same reason
72
- ( & rest[ 0 ] , path. segments . len ( ) - 1 )
73
- } else {
74
- ( first, path. segments . len ( ) )
75
- }
76
- && len > * absolute_paths_max_segments as usize
77
- && let Some ( segment_snippet) = snippet_opt ( cx, segment. ident . span )
78
- && segment_snippet == segment. ident . as_str ( )
83
+ && !matches ! ( node, Node :: Item ( item) if matches!( item. kind, ItemKind :: Use ( ..) ) )
84
+ && !self . absolute_paths_allowed_crates . contains ( & first. ident . name )
85
+ && !is_from_proc_macro ( cx, path)
79
86
{
80
- let is_abs_external =
81
- matches ! ( segment. res, Res :: Def ( DefKind :: Mod , DefId { index, .. } ) if index == CRATE_DEF_INDEX ) ;
82
- let is_abs_crate = segment. ident . name == kw:: Crate ;
83
-
84
- if is_abs_external && absolute_paths_allowed_crates. contains ( segment. ident . name . as_str ( ) )
85
- || is_abs_crate && absolute_paths_allowed_crates. contains ( "crate" )
86
- {
87
- return ;
88
- }
89
-
90
- if is_abs_external || is_abs_crate {
91
- span_lint (
92
- cx,
93
- ABSOLUTE_PATHS ,
94
- path. span ,
95
- "consider bringing this path into scope with the `use` keyword" ,
96
- ) ;
97
- }
87
+ span_lint (
88
+ cx,
89
+ ABSOLUTE_PATHS ,
90
+ path. span ,
91
+ "consider bringing this path into scope with the `use` keyword" ,
92
+ ) ;
98
93
}
99
94
}
100
95
}
0 commit comments