1 use clippy_utils
::diagnostics
::span_lint_and_help
;
2 use rustc_ast
::ast
::{Crate, Inline, Item, ItemKind, ModKind}
;
3 use rustc_errors
::MultiSpan
;
4 use rustc_lint
::{EarlyContext, EarlyLintPass, Level, LintContext}
;
5 use rustc_session
::{declare_tool_lint, impl_lint_pass}
;
6 use rustc_span
::{FileName, Span}
;
7 use std
::collections
::BTreeMap
;
8 use std
::path
::PathBuf
;
10 declare_clippy_lint
! {
12 /// Checks for files that are included as modules multiple times.
14 /// ### Why is this bad?
15 /// Loading a file as a module more than once causes it to be compiled
16 /// multiple times, taking longer and putting duplicate content into the
27 /// #[path = "./b.rs"]
42 #[clippy::version = "1.63.0"]
45 "file loaded as module multiple times"
48 #[derive(PartialOrd, Ord, PartialEq, Eq)]
52 lint_levels
: Vec
<Level
>,
56 pub struct DuplicateMod
{
57 /// map from the canonicalized path to `Modules`, `BTreeMap` to make the
58 /// order deterministic for tests
59 modules
: BTreeMap
<PathBuf
, Modules
>,
62 impl_lint_pass
!(DuplicateMod
=> [DUPLICATE_MOD
]);
64 impl EarlyLintPass
for DuplicateMod
{
65 fn check_item(&mut self, cx
: &EarlyContext
<'_
>, item
: &Item
) {
66 if let ItemKind
::Mod(_
, ModKind
::Loaded(_
, Inline
::No
, mod_spans
)) = &item
.kind
67 && let FileName
::Real(real
) = cx
.sess().source_map().span_to_filename(mod_spans
.inner_span
)
68 && let Some(local_path
) = real
.into_local_path()
69 && let Ok(absolute_path
) = local_path
.canonicalize()
71 let modules
= self.modules
.entry(absolute_path
).or_insert(Modules
{
74 lint_levels
: Vec
::new(),
76 modules
.spans
.push(item
.span_with_attributes());
77 modules
.lint_levels
.push(cx
.get_lint_level(DUPLICATE_MOD
));
81 fn check_crate_post(&mut self, cx
: &EarlyContext
<'_
>, _
: &Crate
) {
86 } in self.modules
.values()
92 // At this point the lint would be emitted
93 assert_eq
!(spans
.len(), lint_levels
.len());
94 let spans
: Vec
<_
> = spans
97 .filter_map(|(span
, lvl
)| {
98 if let Some(id
) = lvl
.get_expectation_id() {
99 cx
.fulfill_expectation(id
);
102 (!matches
!(lvl
, Level
::Allow
| Level
::Expect(_
))).then_some(*span
)
110 let mut multi_span
= MultiSpan
::from_spans(spans
.clone());
111 let (&first
, duplicates
) = spans
.split_first().unwrap();
113 multi_span
.push_span_label(first
, "first loaded here");
114 for &duplicate
in duplicates
{
115 multi_span
.push_span_label(duplicate
, "loaded again here");
122 &format
!("file is loaded as a module multiple times: `{}`", local_path
.display()),
124 "replace all but one `mod` item with `use` items",