Skip to content

Commit 0c29fcc

Browse files
Extend large_include_file lint to also work on attributes
1 parent 73bad36 commit 0c29fcc

File tree

3 files changed

+63
-19
lines changed

3 files changed

+63
-19
lines changed

clippy_lints/src/large_include_file.rs

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::macros::root_macro_call_first_node;
4-
use rustc_ast::LitKind;
4+
use clippy_utils::source::snippet_opt;
5+
use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, LitKind};
56
use rustc_hir::{Expr, ExprKind};
67
use rustc_lint::{LateContext, LateLintPass};
78
use rustc_session::impl_lint_pass;
8-
use rustc_span::sym;
9+
use rustc_span::{Span, sym};
910

1011
declare_clippy_lint! {
1112
/// ### What it does
@@ -51,6 +52,24 @@ impl LargeIncludeFile {
5152

5253
impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
5354

55+
impl LargeIncludeFile {
56+
fn emit_lint(&self, cx: &LateContext<'_>, span: Span) {
57+
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
58+
span_lint_and_then(
59+
cx,
60+
LARGE_INCLUDE_FILE,
61+
span,
62+
"attempted to include a large file",
63+
|diag| {
64+
diag.note(format!(
65+
"the configuration allows a maximum size of {} bytes",
66+
self.max_file_size
67+
));
68+
},
69+
);
70+
}
71+
}
72+
5473
impl LateLintPass<'_> for LargeIncludeFile {
5574
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
5675
if let ExprKind::Lit(lit) = &expr.kind
@@ -66,19 +85,33 @@ impl LateLintPass<'_> for LargeIncludeFile {
6685
&& (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
6786
|| cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
6887
{
69-
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
70-
span_lint_and_then(
71-
cx,
72-
LARGE_INCLUDE_FILE,
73-
expr.span.source_callsite(),
74-
"attempted to include a large file",
75-
|diag| {
76-
diag.note(format!(
77-
"the configuration allows a maximum size of {} bytes",
78-
self.max_file_size
79-
));
80-
},
81-
);
88+
self.emit_lint(cx, expr.span.source_callsite());
89+
}
90+
}
91+
92+
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) {
93+
if !attr.span.from_expansion()
94+
// Currently, rustc limits the usage of macro at the top-level of attributes,
95+
// so we don't need to recurse into each level.
96+
&& let AttrKind::Normal(ref normal) = attr.kind
97+
&& let AttrArgs::Eq(_, AttrArgsEq::Hir(ref meta)) = normal.item.args
98+
&& !attr.span.contains(meta.span)
99+
// Since the `include_str` is already expanded at this point, we can only take the
100+
// whole attribute snippet and then modify for our suggestion.
101+
&& let Some(snippet) = snippet_opt(cx, attr.span)
102+
// We cannot remove this because a `#[doc = include_str!("...")]` attribute can
103+
// occupy several lines.
104+
&& let Some(start) = snippet.find('[')
105+
&& let Some(end) = snippet.rfind(']')
106+
&& let snippet = &snippet[start + 1..end]
107+
// We check that the expansion actually comes from `include_str!` and not just from
108+
// another macro.
109+
&& let Some(sub_snippet) = snippet.trim().strip_prefix("doc")
110+
&& let Some(sub_snippet) = sub_snippet.trim().strip_prefix("=")
111+
&& let sub_snippet = sub_snippet.trim()
112+
&& (sub_snippet.starts_with("include_str!") || sub_snippet.starts_with("include_bytes!"))
113+
{
114+
self.emit_lint(cx, attr.span);
82115
}
83116
}
84117
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#![warn(clippy::large_include_file)]
22

33
// Good
4-
const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs");
5-
const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs");
4+
const GOOD_INCLUDE_BYTES: &[u8; 68] = include_bytes!("../../ui/author.rs");
5+
const GOOD_INCLUDE_STR: &str = include_str!("../../ui/author.rs");
66

77
#[allow(clippy::large_include_file)]
88
const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
@@ -11,6 +11,9 @@ const ALLOWED_TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
1111

1212
// Bad
1313
const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
14+
//~^ large_include_file
1415
const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
16+
//~^ large_include_file
1517

18+
#[doc = include_str!("too_big.txt")] //~ large_include_file
1619
fn main() {}

tests/ui-toml/large_include_file/large_include_file.stderr

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,20 @@ LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
99
= help: to override `-D warnings` add `#[allow(clippy::large_include_file)]`
1010

1111
error: attempted to include a large file
12-
--> tests/ui-toml/large_include_file/large_include_file.rs:14:35
12+
--> tests/ui-toml/large_include_file/large_include_file.rs:15:35
1313
|
1414
LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
1515
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
1616
|
1717
= note: the configuration allows a maximum size of 600 bytes
1818

19-
error: aborting due to 2 previous errors
19+
error: attempted to include a large file
20+
--> tests/ui-toml/large_include_file/large_include_file.rs:18:1
21+
|
22+
LL | #[doc = include_str!("too_big.txt")]
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
|
25+
= note: the configuration allows a maximum size of 600 bytes
26+
27+
error: aborting due to 3 previous errors
2028

0 commit comments

Comments
 (0)