Skip to content

Commit eb5e63e

Browse files
authored
Rollup merge of #107171 - petrochenkov:encattrs, r=cjgillot
rustc_metadata: Fix `encode_attrs` This function didn't do what the authors intended it to do. - Due to `move` in the closure `is_public` wasn't captured by mutalbe reference and wasn't used as a cache. - Due to iterator cloning all the `should_encode_attr` logic run for the second time to calculate `may_have_doc_links` This PR fixes these issues, and calculates all the needed attribute flags in one go. (Noticed while implementing #107136.)
2 parents bf321ec + c70b7aa commit eb5e63e

File tree

1 file changed

+48
-29
lines changed

1 file changed

+48
-29
lines changed

compiler/rustc_metadata/src/rmeta/encoder.rs

+48-29
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::rmeta::def_path_hash_map::DefPathHashMapRef;
33
use crate::rmeta::table::TableBuilder;
44
use crate::rmeta::*;
55

6+
use rustc_ast::util::comments;
67
use rustc_ast::Attribute;
78
use rustc_data_structures::fingerprint::Fingerprint;
89
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
@@ -759,36 +760,54 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
759760
}
760761
}
761762

763+
struct AnalyzeAttrState {
764+
is_exported: bool,
765+
may_have_doc_links: bool,
766+
is_doc_hidden: bool,
767+
}
768+
762769
/// Returns whether an attribute needs to be recorded in metadata, that is, if it's usable and
763770
/// useful in downstream crates. Local-only attributes are an obvious example, but some
764771
/// rustdoc-specific attributes can equally be of use while documenting the current crate only.
765772
///
766773
/// Removing these superfluous attributes speeds up compilation by making the metadata smaller.
767774
///
768-
/// Note: the `is_def_id_public` parameter is used to cache whether the given `DefId` has a public
775+
/// Note: the `is_exported` parameter is used to cache whether the given `DefId` has a public
769776
/// visibility: this is a piece of data that can be computed once per defid, and not once per
770777
/// attribute. Some attributes would only be usable downstream if they are public.
771778
#[inline]
772-
fn should_encode_attr(
773-
tcx: TyCtxt<'_>,
774-
attr: &Attribute,
775-
def_id: LocalDefId,
776-
is_def_id_public: &mut Option<bool>,
777-
) -> bool {
779+
fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState) -> bool {
780+
let mut should_encode = false;
778781
if rustc_feature::is_builtin_only_local(attr.name_or_empty()) {
779782
// Attributes marked local-only don't need to be encoded for downstream crates.
780-
false
781-
} else if attr.doc_str().is_some() {
782-
// We keep all public doc comments because they might be "imported" into downstream crates
783-
// if they use `#[doc(inline)]` to copy an item's documentation into their own.
784-
*is_def_id_public.get_or_insert_with(|| tcx.effective_visibilities(()).is_exported(def_id))
783+
} else if let Some(s) = attr.doc_str() {
784+
// We keep all doc comments reachable to rustdoc because they might be "imported" into
785+
// downstream crates if they use `#[doc(inline)]` to copy an item's documentation into
786+
// their own.
787+
if state.is_exported {
788+
should_encode = true;
789+
if comments::may_have_doc_links(s.as_str()) {
790+
state.may_have_doc_links = true;
791+
}
792+
}
785793
} else if attr.has_name(sym::doc) {
786-
// If this is a `doc` attribute, and it's marked `inline` (as in `#[doc(inline)]`), we can
787-
// remove it. It won't be inlinable in downstream crates.
788-
attr.meta_item_list().map(|l| l.iter().any(|l| !l.has_name(sym::inline))).unwrap_or(false)
794+
// If this is a `doc` attribute that doesn't have anything except maybe `inline` (as in
795+
// `#[doc(inline)]`), then we can remove it. It won't be inlinable in downstream crates.
796+
if let Some(item_list) = attr.meta_item_list() {
797+
for item in item_list {
798+
if !item.has_name(sym::inline) {
799+
should_encode = true;
800+
if item.has_name(sym::hidden) {
801+
state.is_doc_hidden = true;
802+
break;
803+
}
804+
}
805+
}
806+
}
789807
} else {
790-
true
808+
should_encode = true;
791809
}
810+
should_encode
792811
}
793812

794813
fn should_encode_visibility(def_kind: DefKind) -> bool {
@@ -1108,24 +1127,24 @@ fn should_encode_trait_impl_trait_tys(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
11081127
impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
11091128
fn encode_attrs(&mut self, def_id: LocalDefId) {
11101129
let tcx = self.tcx;
1111-
let mut is_public: Option<bool> = None;
1112-
1113-
let hir_attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id));
1114-
let mut attrs = hir_attrs
1130+
let mut state = AnalyzeAttrState {
1131+
is_exported: tcx.effective_visibilities(()).is_exported(def_id),
1132+
may_have_doc_links: false,
1133+
is_doc_hidden: false,
1134+
};
1135+
let attr_iter = tcx
1136+
.hir()
1137+
.attrs(tcx.hir().local_def_id_to_hir_id(def_id))
11151138
.iter()
1116-
.filter(move |attr| should_encode_attr(tcx, attr, def_id, &mut is_public));
1139+
.filter(|attr| analyze_attr(attr, &mut state));
1140+
1141+
record_array!(self.tables.attributes[def_id.to_def_id()] <- attr_iter);
11171142

1118-
record_array!(self.tables.attributes[def_id.to_def_id()] <- attrs.clone());
11191143
let mut attr_flags = AttrFlags::empty();
1120-
if attrs.any(|attr| attr.may_have_doc_links()) {
1144+
if state.may_have_doc_links {
11211145
attr_flags |= AttrFlags::MAY_HAVE_DOC_LINKS;
11221146
}
1123-
if hir_attrs
1124-
.iter()
1125-
.filter(|attr| attr.has_name(sym::doc))
1126-
.filter_map(|attr| attr.meta_item_list())
1127-
.any(|items| items.iter().any(|item| item.has_name(sym::hidden)))
1128-
{
1147+
if state.is_doc_hidden {
11291148
attr_flags |= AttrFlags::IS_DOC_HIDDEN;
11301149
}
11311150
if !attr_flags.is_empty() {

0 commit comments

Comments
 (0)