about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/large_include_file.rs63
-rw-r--r--tests/ui-toml/large_include_file/large_include_file.rs7
-rw-r--r--tests/ui-toml/large_include_file/large_include_file.stderr12
3 files changed, 63 insertions, 19 deletions
diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs
index f2f841dcec3..ab3d19f89c3 100644
--- a/clippy_lints/src/large_include_file.rs
+++ b/clippy_lints/src/large_include_file.rs
@@ -1,11 +1,12 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::root_macro_call_first_node;
-use rustc_ast::LitKind;
+use clippy_utils::source::snippet_opt;
+use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, LitKind};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::sym;
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -51,6 +52,24 @@ impl LargeIncludeFile {
 
 impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
 
+impl LargeIncludeFile {
+    fn emit_lint(&self, cx: &LateContext<'_>, span: Span) {
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(
+            cx,
+            LARGE_INCLUDE_FILE,
+            span,
+            "attempted to include a large file",
+            |diag| {
+                diag.note(format!(
+                    "the configuration allows a maximum size of {} bytes",
+                    self.max_file_size
+                ));
+            },
+        );
+    }
+}
+
 impl LateLintPass<'_> for LargeIncludeFile {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
         if let ExprKind::Lit(lit) = &expr.kind
@@ -66,19 +85,33 @@ impl LateLintPass<'_> for LargeIncludeFile {
             && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
                 || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
         {
-            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
-            span_lint_and_then(
-                cx,
-                LARGE_INCLUDE_FILE,
-                expr.span.source_callsite(),
-                "attempted to include a large file",
-                |diag| {
-                    diag.note(format!(
-                        "the configuration allows a maximum size of {} bytes",
-                        self.max_file_size
-                    ));
-                },
-            );
+            self.emit_lint(cx, expr.span.source_callsite());
+        }
+    }
+
+    fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) {
+        if !attr.span.from_expansion()
+            // Currently, rustc limits the usage of macro at the top-level of attributes,
+            // so we don't need to recurse into each level.
+            && let AttrKind::Normal(ref normal) = attr.kind
+            && let AttrArgs::Eq(_, AttrArgsEq::Hir(ref meta)) = normal.item.args
+            && !attr.span.contains(meta.span)
+            // Since the `include_str` is already expanded at this point, we can only take the
+            // whole attribute snippet and then modify for our suggestion.
+            && let Some(snippet) = snippet_opt(cx, attr.span)
+            // We cannot remove this because a `#[doc = include_str!("...")]` attribute can
+            // occupy several lines.
+            && let Some(start) = snippet.find('[')
+            && let Some(end) = snippet.rfind(']')
+            && let snippet = &snippet[start + 1..end]
+            // We check that the expansion actually comes from `include_str!` and not just from
+            // another macro.
+            && let Some(sub_snippet) = snippet.trim().strip_prefix("doc")
+            && let Some(sub_snippet) = sub_snippet.trim().strip_prefix("=")
+            && let sub_snippet = sub_snippet.trim()
+            && (sub_snippet.starts_with("include_str!") || sub_snippet.starts_with("include_bytes!"))
+        {
+            self.emit_lint(cx, attr.span);
         }
     }
 }
diff --git a/tests/ui-toml/large_include_file/large_include_file.rs b/tests/ui-toml/large_include_file/large_include_file.rs
index f3dbb6ad1cf..dc9349f75a0 100644
--- a/tests/ui-toml/large_include_file/large_include_file.rs
+++ b/tests/ui-toml/large_include_file/large_include_file.rs
@@ -1,8 +1,8 @@
 #![warn(clippy::large_include_file)]
 
 // Good
-const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs");
-const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs");
+const GOOD_INCLUDE_BYTES: &[u8; 68] = include_bytes!("../../ui/author.rs");
+const GOOD_INCLUDE_STR: &str = include_str!("../../ui/author.rs");
 
 #[allow(clippy::large_include_file)]
 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");
 
 // Bad
 const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+//~^ large_include_file
 const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+//~^ large_include_file
 
+#[doc = include_str!("too_big.txt")] //~ large_include_file
 fn main() {}
diff --git a/tests/ui-toml/large_include_file/large_include_file.stderr b/tests/ui-toml/large_include_file/large_include_file.stderr
index 34224065f07..9e1494a47bb 100644
--- a/tests/ui-toml/large_include_file/large_include_file.stderr
+++ b/tests/ui-toml/large_include_file/large_include_file.stderr
@@ -9,12 +9,20 @@ LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
    = help: to override `-D warnings` add `#[allow(clippy::large_include_file)]`
 
 error: attempted to include a large file
-  --> tests/ui-toml/large_include_file/large_include_file.rs:14:35
+  --> tests/ui-toml/large_include_file/large_include_file.rs:15:35
    |
 LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the configuration allows a maximum size of 600 bytes
 
-error: aborting due to 2 previous errors
+error: attempted to include a large file
+  --> tests/ui-toml/large_include_file/large_include_file.rs:18:1
+   |
+LL | #[doc = include_str!("too_big.txt")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the configuration allows a maximum size of 600 bytes
+
+error: aborting due to 3 previous errors