Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

diagnostics: do not suggest masked crate impls #109688

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,18 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
self.root.tables.is_intrinsic.get(self, index)
}

fn get_doc_masked_crates(self) -> Vec<CrateNum> {
let mut v: Vec<CrateNum> = self.root.doc_masked_crates.decode(self).collect();
// sort by crate num so that masked crates can use binary search
// see rustc_middle/ty/util.rs:is_doc_masked
//
// we need to do it now, rather than sorting at encode time, because
// the current crate num is based on the current crate, while encoded
// crate nums are based on the number when this dependency was compiled.
v.sort_unstable();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to sort here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment describing why it needs sorted. It's so that binary search can be used later.

v
}

fn get_doc_link_resolutions(self, index: DefIndex) -> DocLinkResMap {
self.root
.tables
Expand Down
30 changes: 28 additions & 2 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ use rustc_middle::query::LocalCrate;
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::query::{ExternProviders, Providers};
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::cstore::CrateStore;
use rustc_session::cstore::{CrateStore, ExternCrateSource};
use rustc_session::{Session, StableCrateId};
use rustc_span::hygiene::{ExpnHash, ExpnId};
use rustc_span::symbol::{kw, Symbol};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;

use rustc_data_structures::sync::Lrc;
Expand Down Expand Up @@ -355,6 +355,7 @@ provide! { tcx, def_id, other, cdata,
expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) }
generator_diagnostic_data => { cdata.get_generator_diagnostic_data(tcx, def_id.index) }
is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) }
doc_masked_crates => { tcx.arena.alloc_from_iter(cdata.get_doc_masked_crates()) }
doc_link_resolutions => { tcx.arena.alloc(cdata.get_doc_link_resolutions(def_id.index)) }
doc_link_traits_in_scope => {
tcx.arena.alloc_from_iter(cdata.get_doc_link_traits_in_scope(def_id.index))
Expand Down Expand Up @@ -495,6 +496,9 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
tcx.untracked().cstore.leak();
tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).iter_crate_data().map(|(cnum, _)| cnum))
},
doc_masked_crates: |tcx, LocalCrate| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you put this functionality in a function and only call that function in the closure here, please? I think keeping the provide functions short is preferable and is what seems to be done in most places in the compiler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. It's done.

tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).get_sorted_masked_crates(tcx))
},
..*providers
};
}
Expand Down Expand Up @@ -567,6 +571,28 @@ impl CStore {
) -> Span {
self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess)
}

fn get_sorted_masked_crates(&self, tcx: TyCtxt<'_>) -> Vec<CrateNum> {
let mut v: Vec<CrateNum> = CStore::from_tcx(tcx)
.iter_crate_data()
.filter_map(|(cnum, cdata)| {
if let Some(extern_crate) = *cdata.extern_crate.lock() &&
let ExternCrateSource::Extern(did) = extern_crate.src &&
tcx
.get_attrs(did, sym::doc)
.filter_map(|attr| attr.meta_item_list())
.any(|items| items.iter().any(|item| item.has_name(sym::masked))) {
Some(cnum)
} else {
None
}
})
.collect();
// sort by crate num so that masked crates can use binary search
// see rustc_middle/ty/util.rs:is_doc_masked
v.sort_unstable();
v
}
}

impl CrateStore for CStore {
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ use std::collections::hash_map::Entry;
use std::hash::Hash;
use std::io::{Read, Seek, Write};
use std::iter;
use std::mem;
use std::num::NonZeroUsize;
use std::path::{Path, PathBuf};

Expand Down Expand Up @@ -74,6 +75,8 @@ pub(super) struct EncodeContext<'a, 'tcx> {
is_proc_macro: bool,
hygiene_ctxt: &'a HygieneEncodeContext,
symbol_table: FxHashMap<Symbol, usize>,

doc_masked_crates: Vec<CrateNum>,
}

/// If the current crate is a proc-macro, returns early with `LazyArray::default()`.
Expand Down Expand Up @@ -642,6 +645,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
// encode_def_path_table.
let proc_macro_data = stat!("proc-macro-data", || self.encode_proc_macros());

let doc_masked_crates = stat!("doc-masked-crates", || self.encode_doc_masked_crates());

let tables = stat!("tables", || self.tables.encode(&mut self.opaque));

let debugger_visualizers =
Expand Down Expand Up @@ -683,6 +688,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE),
has_default_lib_allocator: attr::contains_name(&attrs, sym::default_lib_allocator),
proc_macro_data,
doc_masked_crates,
debugger_visualizers,
compiler_builtins: attr::contains_name(&attrs, sym::compiler_builtins),
needs_allocator: attr::contains_name(&attrs, sym::needs_allocator),
Expand Down Expand Up @@ -770,6 +776,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
struct AnalyzeAttrState {
is_exported: bool,
is_doc_hidden: bool,
is_doc_masked: bool,
}

/// Returns whether an attribute needs to be recorded in metadata, that is, if it's usable and
Expand Down Expand Up @@ -804,6 +811,10 @@ fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState) -> bool {
state.is_doc_hidden = true;
break;
}
if item.has_name(sym::masked) {
state.is_doc_masked = true;
break;
}
}
}
}
Expand Down Expand Up @@ -1136,6 +1147,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let mut state = AnalyzeAttrState {
is_exported: tcx.effective_visibilities(()).is_exported(def_id),
is_doc_hidden: false,
is_doc_masked: false,
};
let attr_iter = tcx
.opt_local_def_id_to_hir_id(def_id)
Expand All @@ -1149,6 +1161,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
if state.is_doc_hidden {
attr_flags |= AttrFlags::IS_DOC_HIDDEN;
}
if state.is_doc_masked && let Some(krate) = tcx.extern_mod_stmt_cnum(def_id) {
self.doc_masked_crates.push(krate);
}
self.tables.attr_flags.set(def_id.local_def_index, attr_flags);
}

Expand Down Expand Up @@ -1784,6 +1799,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
}

fn encode_doc_masked_crates(&mut self) -> LazyArray<CrateNum> {
empty_proc_macro!(self);
let masked_crates = mem::take(&mut self.doc_masked_crates);
self.lazy_array(masked_crates.iter())
}

fn encode_debugger_visualizers(&mut self) -> LazyArray<DebuggerVisualizerFile> {
empty_proc_macro!(self);
self.lazy_array(self.tcx.debugger_visualizers(LOCAL_CRATE).iter())
Expand Down Expand Up @@ -2207,6 +2228,7 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) {
is_proc_macro: tcx.sess.crate_types().contains(&CrateType::ProcMacro),
hygiene_ctxt: &hygiene_ctxt,
symbol_table: Default::default(),
doc_masked_crates: Vec::new(),
};

// Encode the rustc version string in a predictable location.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ pub(crate) struct CrateRoot {
incoherent_impls: LazyArray<IncoherentImpls>,
interpret_alloc_index: LazyArray<u32>,
proc_macro_data: Option<ProcMacroData>,
doc_masked_crates: LazyArray<CrateNum>,

tables: LazyTables,
debugger_visualizers: LazyArray<rustc_span::DebuggerVisualizerFile>,
Expand Down Expand Up @@ -357,6 +358,7 @@ define_tables! {
associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>,
opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>,
unused_generic_params: Table<DefIndex, UnusedGenericParams>,
doc_masked_crates: Table<CrateNum, bool>,

- optional:
attributes: Table<DefIndex, LazyArray<ast::Attribute>>,
Expand Down
21 changes: 21 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,27 @@ rustc_queries! {
desc { |tcx| "checking whether `{}` is `doc(notable_trait)`", tcx.def_path_str(def_id) }
}

/// Determines whether a crate is [`#[doc(masked)]`][doc_masked].
///
/// [doc_masked]: https://doc.rust-lang.org/nightly/unstable-book/language-features/doc-masked.html
query is_doc_masked(crate_num: CrateNum) -> bool {
cache_on_disk_if { false }
desc { |tcx| "checking whether crate is `doc(masked)`" }
}

/// Returns a list of crates declared [`#[doc(masked)]`][doc_masked] at import time.
///
/// This query is used along with `ExternCrate::dependency_of` to derive the `is_doc_masked`
/// flag. It returns a sorted list of `CrateNum`s that can be binary searched (it doesn't use
/// `UnsortSet` because that would require declaring a new arena to intern the results, and
/// `#[doc(masked)]` is an unstable feature used by the standard library, so N=3).
///
/// [doc_masked]: https://doc.rust-lang.org/nightly/unstable-book/language-features/doc-masked.html
query doc_masked_crates(crate_num: CrateNum) -> &'tcx [CrateNum] {
desc { |tcx| "list crates marked as #[doc(masked)] by given crate" }
separate_provide_extern
}

/// Returns the attributes on the item at `def_id`.
///
/// Do not use this directly, use `tcx.get_attrs` instead.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/parameterized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ trivially_parameterized_over_tcx! {
rustc_hir::LangItem,
rustc_hir::def::DefKind,
rustc_hir::def::DocLinkResMap,
rustc_hir::def_id::CrateNum,
rustc_hir::def_id::DefId,
rustc_hir::def_id::DefIndex,
rustc_hir::definitions::DefKey,
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::ty::{
TypeVisitableExt,
};
use crate::ty::{GenericArgKind, SubstsRef};
use hir::def_id::CrateNum;
use rustc_apfloat::Float as _;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
Expand Down Expand Up @@ -1452,6 +1453,23 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
.any(|items| items.iter().any(|item| item.has_name(sym::notable_trait)))
}

/// Determines whether a crate is annotated with `doc(masked)`.
pub fn is_doc_masked(tcx: TyCtxt<'_>, crate_num: CrateNum) -> bool {
// The current crate is never masked
if crate_num == rustc_span::def_id::LOCAL_CRATE {
return false;
}

// `compiler_builtins` should be masked too, but we can't apply
// `#[doc(masked)]` to the injected `extern crate` because it's unstable.
if tcx.is_compiler_builtins(crate_num) {
return true;
}

let extern_crate = tcx.extern_crate(crate_num.as_def_id()).expect("crate root");
tcx.doc_masked_crates(extern_crate.dependency_of).binary_search(&crate_num).is_ok()
}

/// Determines whether an item is an intrinsic by Abi.
pub fn is_intrinsic(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic | Abi::PlatformIntrinsic)
Expand All @@ -1462,6 +1480,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
reveal_opaque_types_in_bounds,
is_doc_hidden,
is_doc_notable_trait,
is_doc_masked,
is_intrinsic,
..*providers
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2006,6 +2006,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|| !trait_pred
.skip_binder()
.is_constness_satisfied_by(self.tcx.constness(def_id))
// FIXME: `#[doc(masked)]` prevents private dependencies of the standard
// library from showing up in the docs. It's probably not great using `#[doc]`
// attributes in the trait engine, and `masked` in particular doesn't have
// a good specification for its semantics.
|| self.tcx.is_doc_masked(def_id.krate)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm I'm not sure whether relying on an attribute that seems to solely be used in rustdoc inside the trait system is such a good idea. 🤔 But I can't think of a better way to filter for crates we want to ignore here either, so I guess this is fine.

Still can you put a FIXME here that states that ideally we should't rely on an attribute for extern crate for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, FIXME added.

{
return None;
}
Expand Down
16 changes: 0 additions & 16 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1057,22 +1057,6 @@ impl Attributes {
self.other_attrs.lists(name)
}

pub(crate) fn has_doc_flag(&self, flag: Symbol) -> bool {
for attr in &self.other_attrs {
if !attr.has_name(sym::doc) {
continue;
}

if let Some(items) = attr.meta_item_list() {
if items.iter().filter_map(|i| i.meta_item()).any(|it| it.has_name(flag)) {
return true;
}
}
}

false
}

pub(crate) fn from_ast(attrs: &[ast::Attribute]) -> Attributes {
Attributes::from_ast_iter(attrs.iter().map(|attr| (attr, None)), false)
}
Expand Down
16 changes: 0 additions & 16 deletions src/librustdoc/clean/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,6 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
// understood by rustdoc.
let mut module = clean_doc_module(&module, cx);

match *module.kind {
ItemKind::ModuleItem(ref module) => {
for it in &module.items {
// `compiler_builtins` should be masked too, but we can't apply
// `#[doc(masked)]` to the injected `extern crate` because it's unstable.
if it.is_extern_crate()
&& (it.attrs.has_doc_flag(sym::masked)
|| cx.tcx.is_compiler_builtins(it.item_id.krate()))
{
cx.cache.masked_crates.insert(it.item_id.krate());
}
}
}
_ => unreachable!(),
}

let local_crate = ExternalCrate { crate_num: LOCAL_CRATE };
let primitives = local_crate.primitives(cx.tcx);
let keywords = local_crate.keywords(cx.tcx);
Expand Down
15 changes: 3 additions & 12 deletions src/librustdoc/formats/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,6 @@ pub(crate) struct Cache {
/// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions.
pub(crate) document_private: bool,

/// Crates marked with [`#[doc(masked)]`][doc_masked].
///
/// [doc_masked]: https://doc.rust-lang.org/nightly/unstable-book/language-features/doc-masked.html
pub(crate) masked_crates: FxHashSet<CrateNum>,

// Private fields only used when initially crawling a crate to build a cache
stack: Vec<Symbol>,
parent_stack: Vec<ParentStackItem>,
Expand Down Expand Up @@ -210,13 +205,9 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
// If the impl is from a masked crate or references something from a
// masked crate then remove it completely.
if let clean::ImplItem(ref i) = *item.kind {
if self.cache.masked_crates.contains(&item.item_id.krate())
|| i.trait_
.as_ref()
.map_or(false, |t| self.cache.masked_crates.contains(&t.def_id().krate))
|| i.for_
.def_id(self.cache)
.map_or(false, |d| self.cache.masked_crates.contains(&d.krate))
if self.tcx.is_doc_masked(item.item_id.krate())
|| i.trait_.as_ref().map_or(false, |t| self.tcx.is_doc_masked(t.def_id().krate))
|| i.for_.def_id(self.cache).map_or(false, |d| self.tcx.is_doc_masked(d.krate))
{
return None;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// derived from miniz_oxide

pub enum MZStatus {}
pub enum MZError {}

pub struct StreamResult;

pub type MZResult = Result<MZStatus, MZError>;

impl core::convert::From<StreamResult> for MZResult {
fn from(res: StreamResult) -> Self {
loop {}
}
}

impl core::convert::From<&StreamResult> for MZResult {
fn from(res: &StreamResult) -> Self {
loop {}
}
}
23 changes: 23 additions & 0 deletions tests/ui/suggestions/dont-suggest-doc-masked-impls/issue-88696.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// aux-build:masked-crate.rs
//
// This test case should ensure that miniz_oxide isn't
// suggested, since it's not a direct dependency.

#![feature(doc_masked)]

#[doc(masked)]
extern crate masked_crate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why you include this extern crate statement here. miniz_oxide is #[doc(masked)] in std, so just using the example in the fixed issue as a test should suffice here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to make sure that this test case didn't rely on the specifics of how libstd is implemented right now, so I copied the relevant parts of miniz_oxide into the test case.

When run without the fix, the error message lists both miniz_oxide and masked_crate.


fn a() -> Result<u64, i32> {
Err(1)
}

fn b() -> Result<u32, i32> {
a().into() //~ERROR [E0277]
}

fn main() {
let _ = dbg!(b());
// make sure crate actually gets loaded
let _ = masked_crate::StreamResult;
}
Loading