1
Fork 0

Fix rustdoc and clippy

This commit is contained in:
Jana Dönszelmann 2025-02-09 22:49:57 +01:00
parent 7e0f5b5016
commit f321f107e3
No known key found for this signature in database
31 changed files with 135 additions and 122 deletions

View file

@ -1145,6 +1145,7 @@ impl AttributeExt for Attribute {
} }
fn is_doc_comment(&self) -> bool { fn is_doc_comment(&self) -> bool {
// FIXME(jdonszelmann): make the 2nd check unnecessary here
matches!(self, Attribute::Parsed(AttributeKind::DocComment { .. })) matches!(self, Attribute::Parsed(AttributeKind::DocComment { .. }))
} }

View file

@ -2737,13 +2737,13 @@ fn add_without_unwanted_attributes<'hir>(
import_parent: Option<DefId>, import_parent: Option<DefId>,
) { ) {
for attr in new_attrs { for attr in new_attrs {
if matches!(attr.kind, hir::AttrKind::DocComment(..)) { if attr.is_doc_comment() {
attrs.push((Cow::Borrowed(attr), import_parent)); attrs.push((Cow::Borrowed(attr), import_parent));
continue; continue;
} }
let mut attr = attr.clone(); let mut attr = attr.clone();
match attr.kind { match attr {
hir::AttrKind::Normal(ref mut normal) => { hir::Attribute::Unparsed(ref mut normal) => {
if let [ident] = &*normal.path.segments { if let [ident] = &*normal.path.segments {
let ident = ident.name; let ident = ident.name;
if ident == sym::doc { if ident == sym::doc {
@ -2755,7 +2755,11 @@ fn add_without_unwanted_attributes<'hir>(
} }
} }
} }
_ => unreachable!(), hir::Attribute::Parsed(..) => {
if is_inline {
attrs.push((Cow::Owned(attr), import_parent));
}
}
} }
} }
} }

View file

@ -265,7 +265,7 @@ impl ExternalCrate {
let attr_value = attr.value_str().expect("syntax should already be validated"); let attr_value = attr.value_str().expect("syntax should already be validated");
let Some(prim) = PrimitiveType::from_symbol(attr_value) else { let Some(prim) = PrimitiveType::from_symbol(attr_value) else {
span_bug!( span_bug!(
attr.span, attr.span(),
"primitive `{attr_value}` is not a member of `PrimitiveType`" "primitive `{attr_value}` is not a member of `PrimitiveType`"
); );
}; };

View file

@ -123,7 +123,7 @@ impl HirCollector<'_> {
.iter() .iter()
.find(|attr| attr.doc_str().is_some()) .find(|attr| attr.doc_str().is_some())
.map(|attr| { .map(|attr| {
attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span) attr.span().ctxt().outer_expn().expansion_cause().unwrap_or(attr.span())
}) })
.unwrap_or(DUMMY_SP) .unwrap_or(DUMMY_SP)
}; };

View file

@ -30,7 +30,7 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
/// This integer is incremented with every breaking change to the API, /// This integer is incremented with every breaking change to the API,
/// and is returned along with the JSON blob as [`Crate::format_version`]. /// and is returned along with the JSON blob as [`Crate::format_version`].
/// Consuming code should assert that this value matches the format version(s) that it supports. /// Consuming code should assert that this value matches the format version(s) that it supports.
pub const FORMAT_VERSION: u32 = 39; pub const FORMAT_VERSION: u32 = 40;
/// The root of the emitted JSON blob. /// The root of the emitted JSON blob.
/// ///
@ -120,7 +120,9 @@ pub struct Item {
pub docs: Option<String>, pub docs: Option<String>,
/// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs /// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs
pub links: HashMap<String, Id>, pub links: HashMap<String, Id>,
/// Stringified versions of the attributes on this item (e.g. `"#[inline]"`) /// Stringified versions of parsed attributes on this item.
/// Essentially debug printed (e.g. `#[inline]` becomes something similar to `#[attr="Inline(Hint)"]`).
/// Equivalent to the hir pretty-printing of attributes.
pub attrs: Vec<String>, pub attrs: Vec<String>,
/// Information about the items deprecation, if present. /// Information about the items deprecation, if present.
pub deprecation: Option<Deprecation>, pub deprecation: Option<Deprecation>,

View file

@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Att
span_lint( span_lint(
cx, cx,
INLINE_ALWAYS, INLINE_ALWAYS,
attr.span, attr.span(),
format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"), format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
); );
} }

View file

@ -1,6 +1,7 @@
use rustc_attr_parsing::{find_attr, AttributeKind, ReprAttr};
use rustc_hir::Attribute; use rustc_hir::Attribute;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::{Span, sym}; use rustc_span::Span;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs; use clippy_utils::msrvs;
@ -14,30 +15,21 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute],
} }
fn check_packed(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) { fn check_packed(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) {
if let Some(items) = attrs.iter().find_map(|attr| { if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
if attr.ident().is_some_and(|ident| matches!(ident.name, sym::repr)) { let packed_span = reprs.iter().find(|(r, _)| matches!(r, ReprAttr::ReprPacked(..))).map(|(_, s)| *s);
attr.meta_item_list()
} else { if let Some(packed_span) = packed_span && !reprs.iter().any(|(x, _)| *x == ReprAttr::ReprC || *x == ReprAttr::ReprRust) {
None span_lint_and_then(
cx,
REPR_PACKED_WITHOUT_ABI,
item_span,
"item uses `packed` representation without ABI-qualification",
|diag| {
diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI")
.help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
.span_label(packed_span, "`packed` representation set here");
},
);
} }
}) && let Some(packed) = items
.iter()
.find(|item| item.ident().is_some_and(|ident| matches!(ident.name, sym::packed)))
&& !items.iter().any(|item| {
item.ident()
.is_some_and(|ident| matches!(ident.name, sym::C | sym::Rust))
})
{
span_lint_and_then(
cx,
REPR_PACKED_WITHOUT_ABI,
item_span,
"item uses `packed` representation without ABI-qualification",
|diag| {
diag.warn("unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI")
.help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
.span_label(packed.span(), "`packed` representation set here");
},
);
} }
} }

View file

@ -15,7 +15,7 @@ pub(super) fn check(
) { ) {
if cfg_attr.has_name(sym::clippy) if cfg_attr.has_name(sym::clippy)
&& let Some(ident) = behind_cfg_attr.ident() && let Some(ident) = behind_cfg_attr.ident()
&& Level::from_symbol(ident.name, Some(attr.id)).is_some() && Level::from_symbol(ident.name, || Some(attr.id)).is_some()
&& let Some(items) = behind_cfg_attr.meta_item_list() && let Some(items) = behind_cfg_attr.meta_item_list()
{ {
let nb_items = items.len(); let nb_items = items.len();

View file

@ -17,7 +17,7 @@ pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool {
} }
pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool { pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool {
Level::from_symbol(symbol, Some(attr_id)).is_some() Level::from_symbol(symbol, || Some(attr_id)).is_some()
} }
pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {

View file

@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use rustc_attr_parsing::{find_attr, AttributeKind, ReprAttr};
use rustc_hir::{HirId, Item, ItemKind}; use rustc_hir::{HirId, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, FieldDef}; use rustc_middle::ty::{self, FieldDef};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -97,16 +97,7 @@ fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: ty::GenericArgsR
} }
fn has_c_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { fn has_c_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
cx.tcx.hir().attrs(hir_id).iter().any(|attr| { let attrs = cx.tcx.hir().attrs(hir_id);
if attr.has_name(sym::repr) {
if let Some(items) = attr.meta_item_list() { find_attr!(attrs, AttributeKind::Repr(r) if r.iter().any(|(x, _)| *x == ReprAttr::ReprC))
for item in items {
if item.is_word() && matches!(item.name_or_empty(), sym::C) {
return true;
}
}
}
}
false
})
} }

View file

@ -1,3 +1,4 @@
use clippy_config::Conf; use clippy_config::Conf;
use clippy_config::types::create_disallowed_map; use clippy_config::types::create_disallowed_map;
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};

View file

@ -1,18 +1,17 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use rustc_ast::AttrStyle;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{AttrArgs, AttrKind, Attribute}; use rustc_lint::EarlyContext;
use rustc_lint::LateContext; use rustc_ast::{Attribute, AttrKind, AttrArgs, AttrStyle};
use super::DOC_INCLUDE_WITHOUT_CFG; use super::DOC_INCLUDE_WITHOUT_CFG;
pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) { pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
for attr in attrs { for attr in attrs {
if !attr.span.from_expansion() if !attr.span.from_expansion()
&& let AttrKind::Normal(ref item) = attr.kind && let AttrKind::Normal(ref item) = attr.kind
&& attr.doc_str().is_some() && attr.doc_str().is_some()
&& let AttrArgs::Eq { expr: meta, .. } = &item.args && let AttrArgs::Eq { expr: meta, .. } = &item.item.args
&& !attr.span.contains(meta.span) && !attr.span.contains(meta.span)
// Since the `include_str` is already expanded at this point, we can only take the // Since the `include_str` is already expanded at this point, we can only take the
// whole attribute snippet and then modify for our suggestion. // whole attribute snippet and then modify for our suggestion.

View file

@ -20,7 +20,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_resolve::rustdoc::{ use rustc_resolve::rustdoc::{
@ -577,6 +577,13 @@ impl_lint_pass!(Documentation => [
DOC_INCLUDE_WITHOUT_CFG, DOC_INCLUDE_WITHOUT_CFG,
]); ]);
impl EarlyLintPass for Documentation {
fn check_attributes(&mut self, cx: &EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) {
include_in_doc_without_cfg::check(cx, attrs);
}
}
impl<'tcx> LateLintPass<'tcx> for Documentation { impl<'tcx> LateLintPass<'tcx> for Documentation {
fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
@ -704,14 +711,13 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
Some(("fake".into(), "fake".into())) Some(("fake".into(), "fake".into()))
} }
include_in_doc_without_cfg::check(cx, attrs);
if suspicious_doc_comments::check(cx, attrs) || is_doc_hidden(attrs) { if suspicious_doc_comments::check(cx, attrs) || is_doc_hidden(attrs) {
return None; return None;
} }
let (fragments, _) = attrs_to_doc_fragments( let (fragments, _) = attrs_to_doc_fragments(
attrs.iter().filter_map(|attr| { attrs.iter().filter_map(|attr| {
if attr.span.in_external_macro(cx.sess().source_map()) { if !attr.doc_str_and_comment_kind().is_some() || attr.span().in_external_macro(cx.sess().source_map()) {
None None
} else { } else {
Some((attr, None)) Some((attr, None))

View file

@ -3,6 +3,7 @@ use rustc_ast::AttrStyle;
use rustc_ast::token::CommentKind; use rustc_ast::token::CommentKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::Attribute; use rustc_hir::Attribute;
use rustc_attr_parsing::AttributeKind;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::Span; use rustc_span::Span;
@ -36,15 +37,14 @@ fn collect_doc_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> {
attrs attrs
.iter() .iter()
.filter_map(|attr| { .filter_map(|attr| {
if let Some((sym, com_kind)) = attr.doc_str_and_comment_kind() if let Attribute::Parsed(AttributeKind::DocComment{ style: AttrStyle::Outer, kind, comment, ..}) = attr
&& let AttrStyle::Outer = attr.style && let Some(com) = comment.as_str().strip_prefix('!')
&& let Some(com) = sym.as_str().strip_prefix('!')
{ {
let sugg = match com_kind { let sugg = match kind {
CommentKind::Line => format!("//!{com}"), CommentKind::Line => format!("//!{com}"),
CommentKind::Block => format!("/*!{com}*/"), CommentKind::Block => format!("/*!{com}*/"),
}; };
Some((attr.span, sugg)) Some((attr.span(), sugg))
} else { } else {
None None
} }

View file

@ -1,5 +1,6 @@
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Attribute, Item, ItemKind}; use rustc_hir::{Attribute, Item, ItemKind};
use rustc_attr_parsing::AttributeKind;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
@ -43,9 +44,9 @@ pub(super) fn check(
let mut should_suggest_empty_doc = false; let mut should_suggest_empty_doc = false;
for attr in attrs { for attr in attrs {
if let Some(doc) = attr.doc_str() { if let Attribute::Parsed(AttributeKind::DocComment {span, comment, ..}) = attr {
spans.push(attr.span); spans.push(span);
let doc = doc.as_str(); let doc = comment.as_str();
let doc = doc.trim(); let doc = doc.trim();
if spans.len() == 1 { if spans.len() == 1 {
// We make this suggestion only if the first doc line ends with a punctuation // We make this suggestion only if the first doc line ends with a punctuation
@ -78,7 +79,7 @@ pub(super) fn check(
&& let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi()) && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi())
&& let Some(snippet) = snippet_opt(cx, new_span) && let Some(snippet) = snippet_opt(cx, new_span)
{ {
let Some(first) = snippet_opt(cx, first_span) else { let Some(first) = snippet_opt(cx, *first_span) else {
return; return;
}; };
let Some(comment_form) = first.get(..3) else { let Some(comment_form) = first.get(..3) else {

View file

@ -46,7 +46,8 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes {
.hir() .hir()
.attrs(item.hir_id()) .attrs(item.hir_id())
.iter() .iter()
.fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span)); .filter(|i| i.is_doc_comment())
.fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span()));
let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else { let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else {
return; return;
}; };

View file

@ -95,6 +95,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
} }
} }
// FIXME: needs to be an EARLY LINT. all attribute lints should be
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn check_needless_must_use( fn check_needless_must_use(
cx: &LateContext<'_>, cx: &LateContext<'_>,
@ -117,7 +118,7 @@ fn check_needless_must_use(
fn_header_span, fn_header_span,
"this unit-returning function has a `#[must_use]` attribute", "this unit-returning function has a `#[must_use]` attribute",
|diag| { |diag| {
diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable); diag.span_suggestion(attr.span(), "remove the attribute", "", Applicability::MachineApplicable);
}, },
); );
} else { } else {
@ -130,7 +131,7 @@ fn check_needless_must_use(
"this unit-returning function has a `#[must_use]` attribute", "this unit-returning function has a `#[must_use]` attribute",
|diag| { |diag| {
let mut attrs_without_must_use = attrs.to_vec(); let mut attrs_without_must_use = attrs.to_vec();
attrs_without_must_use.retain(|a| a.id != attr.id); attrs_without_must_use.retain(|a| a.id() != attr.id());
let sugg_str = attrs_without_must_use let sugg_str = attrs_without_must_use
.iter() .iter()
.map(|a| { .map(|a| {
@ -143,7 +144,7 @@ fn check_needless_must_use(
.join(", "); .join(", ");
diag.span_suggestion( diag.span_suggestion(
attrs[0].span.with_hi(attrs[attrs.len() - 1].span.hi()), attrs[0].span().with_hi(attrs[attrs.len() - 1].span().hi()),
"change these attributes to", "change these attributes to",
sugg_str, sugg_str,
Applicability::MachineApplicable, Applicability::MachineApplicable,

View file

@ -183,7 +183,7 @@ fn suggestion<'tcx>(
fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span { fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span {
if let Some(attr) = tcx.hir().attrs(field.hir_id).first() { if let Some(attr) = tcx.hir().attrs(field.hir_id).first() {
field.span.with_lo(attr.span.lo()) field.span.with_lo(attr.span().lo())
} else { } else {
field.span field.span
} }

View file

@ -42,10 +42,10 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody {
span_lint_and_then( span_lint_and_then(
cx, cx,
INLINE_FN_WITHOUT_BODY, INLINE_FN_WITHOUT_BODY,
attr.span, attr.span(),
format!("use of `#[inline]` on trait method `{}` which has no body", item.ident), format!("use of `#[inline]` on trait method `{}` which has no body", item.ident),
|diag| { |diag| {
diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable); diag.suggest_remove_item(cx, attr.span(), "remove", Applicability::MachineApplicable);
}, },
); );
} }

View file

@ -3,10 +3,11 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use rustc_ast::LitKind; use rustc_ast::LitKind;
use rustc_hir::{AttrArgs, AttrKind, Attribute, Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_ast::{Attribute, AttrArgs, AttrKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::{Span, sym}; use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -52,24 +53,6 @@ impl LargeIncludeFile {
impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); 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 { impl LateLintPass<'_> for LargeIncludeFile {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
if let ExprKind::Lit(lit) = &expr.kind if let ExprKind::Lit(lit) = &expr.kind
@ -85,18 +68,32 @@ impl LateLintPass<'_> for LargeIncludeFile {
&& (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) && (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)) || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
{ {
self.emit_lint(cx, expr.span.source_callsite()); #[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
));
},
);
} }
} }
}
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { impl EarlyLintPass for LargeIncludeFile {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
if !attr.span.from_expansion() if !attr.span.from_expansion()
// Currently, rustc limits the usage of macro at the top-level of attributes, // Currently, rustc limits the usage of macro at the top-level of attributes,
// so we don't need to recurse into each level. // so we don't need to recurse into each level.
&& let AttrKind::Normal(ref item) = attr.kind && let AttrKind::Normal(ref item) = attr.kind
&& let Some(doc) = attr.doc_str() && let Some(doc) = attr.doc_str()
&& doc.as_str().len() as u64 > self.max_file_size && doc.as_str().len() as u64 > self.max_file_size
&& let AttrArgs::Eq { expr: meta, .. } = &item.args && let AttrArgs::Eq { expr: meta, .. } = &item.item.args
&& !attr.span.contains(meta.span) && !attr.span.contains(meta.span)
// Since the `include_str` is already expanded at this point, we can only take the // Since the `include_str` is already expanded at this point, we can only take the
// whole attribute snippet and then modify for our suggestion. // whole attribute snippet and then modify for our suggestion.
@ -113,7 +110,19 @@ impl LateLintPass<'_> for LargeIncludeFile {
&& let sub_snippet = sub_snippet.trim() && let sub_snippet = sub_snippet.trim()
&& (sub_snippet.starts_with("include_str!") || sub_snippet.starts_with("include_bytes!")) && (sub_snippet.starts_with("include_str!") || sub_snippet.starts_with("include_bytes!"))
{ {
self.emit_lint(cx, attr.span); #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LARGE_INCLUDE_FILE,
attr.span,
"attempted to include a large file",
|diag| {
diag.note(format!(
"the configuration allows a maximum size of {} bytes",
self.max_file_size
));
},
);
} }
} }
} }

View file

@ -407,9 +407,9 @@ mod zombie_processes;
use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
use clippy_utils::macros::FormatArgsStorage; use clippy_utils::macros::FormatArgsStorage;
use utils::attr_collector::{AttrCollector, AttrStorage};
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_lint::{Lint, LintId}; use rustc_lint::{Lint, LintId};
use utils::attr_collector::{AttrCollector, AttrStorage};
/// Register all pre expansion lints /// Register all pre expansion lints
/// ///
@ -717,6 +717,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(conf))); store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(conf)));
store.register_late_pass(move |tcx| Box::new(functions::Functions::new(tcx, conf))); store.register_late_pass(move |tcx| Box::new(functions::Functions::new(tcx, conf)));
store.register_late_pass(move |_| Box::new(doc::Documentation::new(conf))); store.register_late_pass(move |_| Box::new(doc::Documentation::new(conf)));
store.register_early_pass(move || Box::new(doc::Documentation::new(conf)));
store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply)); store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq)); store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)); store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
@ -860,6 +861,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_early_pass(|| Box::new(pub_use::PubUse));
store.register_late_pass(|_| Box::new(format_push_string::FormatPushString)); store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf))); store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf)));
store.register_early_pass(move || Box::new(large_include_file::LargeIncludeFile::new(conf)));
store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace));
store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default()); store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());

View file

@ -94,7 +94,7 @@ impl LateLintPass<'_> for MacroUseImports {
{ {
for kid in cx.tcx.module_children(id) { for kid in cx.tcx.module_children(id) {
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
let span = mac_attr.span; let span = mac_attr.span();
let def_path = cx.tcx.def_path_str(mac_id); let def_path = cx.tcx.def_path_str(mac_id);
self.imports.push((def_path, span, hir_id)); self.imports.push((def_path, span, hir_id));
} }
@ -104,8 +104,8 @@ impl LateLintPass<'_> for MacroUseImports {
} }
} }
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &hir::Attribute) { fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &hir::Attribute) {
if attr.span.from_expansion() { if attr.span().from_expansion() {
self.push_unique_macro(cx, attr.span); self.push_unique_macro(cx, attr.span());
} }
} }
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {

View file

@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive {
if let Some(non_exhaustive) = if let Some(non_exhaustive) =
attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive) attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)
{ {
diag.span_note(non_exhaustive.span, "the struct is already non-exhaustive"); diag.span_note(non_exhaustive.span(), "the struct is already non-exhaustive");
} else { } else {
let indent = snippet_indent(cx, item.span).unwrap_or_default(); let indent = snippet_indent(cx, item.span).unwrap_or_default();
diag.span_suggestion_verbose( diag.span_suggestion_verbose(

View file

@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
.span .span
.with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len())) .with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
.shrink_to_lo(); .shrink_to_lo();
let attr_snippet = snippet(cx, attr.span, ".."); let attr_snippet = snippet(cx, attr.span(), "..");
span_lint_and_then( span_lint_and_then(
cx, cx,

View file

@ -432,7 +432,7 @@ fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Spa
.hir() .hir()
.attrs(hir_id) .attrs(hir_id)
.iter() .iter()
.fold(span, |acc, attr| acc.to(attr.span))) .fold(span, |acc, attr| acc.to(attr.span())))
} }
enum HasSafetyComment { enum HasSafetyComment {

View file

@ -93,6 +93,7 @@ use std::sync::{Mutex, MutexGuard, OnceLock};
use itertools::Itertools; use itertools::Itertools;
use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_ast::ast::{self, LitKind, RangeLimits};
use rustc_attr_parsing::{find_attr, AttributeKind};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::packed::Pu128; use rustc_data_structures::packed::Pu128;
use rustc_data_structures::unhash::UnhashMap; use rustc_data_structures::unhash::UnhashMap;
@ -1949,7 +1950,7 @@ pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
} }
pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
has_attr(cx.tcx.hir().attrs(hir_id), sym::repr) find_attr!(cx.tcx.hir().attrs(hir_id), AttributeKind::Repr(..))
} }
pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool { pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {

View file

@ -1,3 +1,13 @@
error: attempted to include a large file
--> tests/ui-toml/large_include_file/large_include_file.rs:19:1
|
LL | #[doc = include_str!("too_big.txt")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the configuration allows a maximum size of 600 bytes
= note: `-D clippy::large-include-file` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::large_include_file)]`
error: attempted to include a large file error: attempted to include a large file
--> tests/ui-toml/large_include_file/large_include_file.rs:14:43 --> tests/ui-toml/large_include_file/large_include_file.rs:14:43
| |
@ -5,8 +15,6 @@ LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= note: the configuration allows a maximum size of 600 bytes = note: the configuration allows a maximum size of 600 bytes
= note: `-D clippy::large-include-file` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::large_include_file)]`
error: attempted to include a large file error: attempted to include a large file
--> tests/ui-toml/large_include_file/large_include_file.rs:16:35 --> tests/ui-toml/large_include_file/large_include_file.rs:16:35
@ -16,13 +24,5 @@ LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
| |
= note: the configuration allows a maximum size of 600 bytes = note: the configuration allows a maximum size of 600 bytes
error: attempted to include a large file
--> tests/ui-toml/large_include_file/large_include_file.rs:19: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 error: aborting due to 3 previous errors

View file

@ -24,8 +24,8 @@ fn main() {
); );
} }
#[cfg_attr(all(), deprecated)] #[cfg_attr(all(), )]
fn issue_12320() {} fn issue_12320() {}
#[cfg_attr(all(), deprecated, doc = "foo")] #[cfg_attr(all(), deprecateddoc = "foo", doc = "foo", must_use)]
fn issue_12320_2() {} fn issue_12320_2() {}

View file

@ -25,19 +25,21 @@ LL | #[must_use = "With note"]
LL | pub fn must_use_with_note() {} LL | pub fn must_use_with_note() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
[Unparsed(AttrItem { path: AttrPath { segments: [must_use#0], span: tests/ui/must_use_unit.rs:30:19: 30:27 (#0) }, args: Empty, id: HashIgnoredAttrId { attr_id: AttrId(9) }, style: Outer, span: tests/ui/must_use_unit.rs:30:19: 30:27 (#0) }), Parsed(Deprecation { deprecation: Deprecation { since: Unspecified, note: None, suggestion: None }, span: tests/ui/must_use_unit.rs:30:29: 30:39 (#0) })]
error: this unit-returning function has a `#[must_use]` attribute error: this unit-returning function has a `#[must_use]` attribute
--> tests/ui/must_use_unit.rs:31:1 --> tests/ui/must_use_unit.rs:31:1
| |
LL | #[cfg_attr(all(), must_use, deprecated)] LL | #[cfg_attr(all(), must_use, deprecated)]
| -------------------- help: change these attributes to: `deprecated` | -------------------- help: change these attributes to
LL | fn issue_12320() {} LL | fn issue_12320() {}
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
[Unparsed(AttrItem { path: AttrPath { segments: [doc#0], span: tests/ui/must_use_unit.rs:33:31: 33:34 (#0) }, args: Eq { eq_span: tests/ui/must_use_unit.rs:33:35: 33:36 (#0), expr: MetaItemLit { symbol: "foo", suffix: None, kind: Str("foo", Cooked), span: tests/ui/must_use_unit.rs:33:37: 33:42 (#0) } }, id: HashIgnoredAttrId { attr_id: AttrId(12) }, style: Outer, span: tests/ui/must_use_unit.rs:33:31: 33:42 (#0) }), Unparsed(AttrItem { path: AttrPath { segments: [must_use#0], span: tests/ui/must_use_unit.rs:33:44: 33:52 (#0) }, args: Empty, id: HashIgnoredAttrId { attr_id: AttrId(13) }, style: Outer, span: tests/ui/must_use_unit.rs:33:44: 33:52 (#0) }), Parsed(Deprecation { deprecation: Deprecation { since: Unspecified, note: None, suggestion: None }, span: tests/ui/must_use_unit.rs:33:19: 33:29 (#0) })]
error: this unit-returning function has a `#[must_use]` attribute error: this unit-returning function has a `#[must_use]` attribute
--> tests/ui/must_use_unit.rs:34:1 --> tests/ui/must_use_unit.rs:34:1
| |
LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)] LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)]
| --------------------------------- help: change these attributes to: `deprecated, doc = "foo"` | -- help: change these attributes to: `doc = "foo",`
LL | fn issue_12320_2() {} LL | fn issue_12320_2() {}
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^

View file

@ -1,7 +1,7 @@
// ignore-tidy-linelength // ignore-tidy-linelength
#[repr(i32)] #[repr(i32)]
//@ is "$.index[*][?(@.name=='Foo')].attrs" '["#[repr(i32)]"]' //@ is "$.index[*][?(@.name=='Foo')].attrs" '["#[attr=\"Repr([ReprInt(SignedInt(I32))])\")]\n"]'
pub enum Foo { pub enum Foo {
//@ is "$.index[*][?(@.name=='Struct')].inner.variant.discriminant" null //@ is "$.index[*][?(@.name=='Struct')].inner.variant.discriminant" null
//@ count "$.index[*][?(@.name=='Struct')].inner.variant.kind.struct.fields[*]" 0 //@ count "$.index[*][?(@.name=='Struct')].inner.variant.kind.struct.fields[*]" 0

View file

@ -1,7 +1,7 @@
// ignore-tidy-linelength // ignore-tidy-linelength
#[repr(u32)] #[repr(u32)]
//@ is "$.index[*][?(@.name=='Foo')].attrs" '["#[repr(u32)]"]' //@ is "$.index[*][?(@.name=='Foo')].attrs" '["#[attr=\"Repr([ReprInt(UnsignedInt(U32))])\")]\n"]'
pub enum Foo { pub enum Foo {
//@ is "$.index[*][?(@.name=='Tuple')].inner.variant.discriminant" null //@ is "$.index[*][?(@.name=='Tuple')].inner.variant.discriminant" null
//@ count "$.index[*][?(@.name=='Tuple')].inner.variant.kind.tuple[*]" 0 //@ count "$.index[*][?(@.name=='Tuple')].inner.variant.kind.tuple[*]" 0