Rollup merge of #140560 - Urgau:test_attr-module-level, r=GuillaumeGomez
Allow `#![doc(test(attr(..)))]` everywhere This PR adds the ability to specify [`#![doc(test(attr(..)))]`](https://doc.rust-lang.org/nightly/rustdoc/write-documentation/the-doc-attribute.html#testattr) ~~at module level~~ everywhere in addition to allowing it at crate-root. This is motivated by a recent PR #140323 (by ````@tgross35)```` where we have to duplicate 2 attributes to every single `f16` and `f128` doctests, by allowing `#![doc(test(attr(..)))]` at module level (and everywhere else) we can omit them entirely and just have (in both module): ```rust #![doc(test(attr(feature(cfg_target_has_reliable_f16_f128))))] #![doc(test(attr(expect(internal_features))))] ``` Those new attributes are appended to the one found at crate-root or at a previous module. Those "global" attributes are compatible with merged doctests (they already were before). Given the small addition that this is, I'm proposing to insta-stabilize it, but I can feature-gate it if preferred. Best reviewed commit by commit. r? ````@GuillaumeGomez````
This commit is contained in:
commit
2c8a9cccd9
23 changed files with 705 additions and 102 deletions
|
|
@ -1266,13 +1266,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
true
|
||||
}
|
||||
|
||||
/// Checks that `doc(test(...))` attribute contains only valid attributes. Returns `true` if
|
||||
/// valid.
|
||||
fn check_test_attr(&self, meta: &MetaItemInner, hir_id: HirId) {
|
||||
/// Checks that `doc(test(...))` attribute contains only valid attributes and are at the right place.
|
||||
fn check_test_attr(&self, attr: &Attribute, meta: &MetaItemInner, hir_id: HirId) {
|
||||
if let Some(metas) = meta.meta_item_list() {
|
||||
for i_meta in metas {
|
||||
match (i_meta.name(), i_meta.meta_item()) {
|
||||
(Some(sym::attr | sym::no_crate_inject), _) => {}
|
||||
(Some(sym::attr), _) => {
|
||||
// Allowed everywhere like `#[doc]`
|
||||
}
|
||||
(Some(sym::no_crate_inject), _) => {
|
||||
self.check_attr_crate_level(attr, meta, hir_id);
|
||||
}
|
||||
(_, Some(m)) => {
|
||||
self.tcx.emit_node_span_lint(
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
|
|
@ -1359,9 +1363,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
}
|
||||
|
||||
Some(sym::test) => {
|
||||
if self.check_attr_crate_level(attr, meta, hir_id) {
|
||||
self.check_test_attr(meta, hir_id);
|
||||
}
|
||||
self.check_test_attr(attr, meta, hir_id);
|
||||
}
|
||||
|
||||
Some(
|
||||
|
|
|
|||
|
|
@ -143,15 +143,6 @@ But if you include this:
|
|||
|
||||
it will not.
|
||||
|
||||
### `test(attr(...))`
|
||||
|
||||
This form of the `doc` attribute allows you to add arbitrary attributes to all your doctests. For
|
||||
example, if you want your doctests to fail if they have dead code, you could add this:
|
||||
|
||||
```rust,no_run
|
||||
#![doc(test(attr(deny(dead_code))))]
|
||||
```
|
||||
|
||||
## At the item level
|
||||
|
||||
These forms of the `#[doc]` attribute are used on individual items, to control how
|
||||
|
|
@ -283,3 +274,26 @@ To get around this limitation, we just add `#[doc(alias = "lib_name_do_something
|
|||
on the `do_something` method and then it's all good!
|
||||
Users can now look for `lib_name_do_something` in our crate directly and find
|
||||
`Obj::do_something`.
|
||||
|
||||
### `test(attr(...))`
|
||||
|
||||
This form of the `doc` attribute allows you to add arbitrary attributes to all your doctests. For
|
||||
example, if you want your doctests to fail if they have dead code, you could add this:
|
||||
|
||||
```rust,no_run
|
||||
#![doc(test(attr(deny(dead_code))))]
|
||||
|
||||
mod my_mod {
|
||||
#![doc(test(attr(allow(dead_code))))] // but allow `dead_code` for this module
|
||||
}
|
||||
```
|
||||
|
||||
`test(attr(..))` attributes are appended to the parent module's, they do not replace the current
|
||||
list of attributes. In the previous example, both attributes would be present:
|
||||
|
||||
```rust,no_run
|
||||
// For every doctest in `my_mod`
|
||||
|
||||
#![deny(dead_code)] // from the crate-root
|
||||
#![allow(dead_code)] // from `my_mod`
|
||||
```
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ mod runner;
|
|||
mod rust;
|
||||
|
||||
use std::fs::File;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{self, Command, Stdio};
|
||||
|
|
@ -14,7 +15,7 @@ use std::{panic, str};
|
|||
|
||||
pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder};
|
||||
pub(crate) use markdown::test as test_markdown;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHasher, FxIndexMap, FxIndexSet};
|
||||
use rustc_errors::emitter::HumanReadableErrorType;
|
||||
use rustc_errors::{ColorConfig, DiagCtxtHandle};
|
||||
use rustc_hir as hir;
|
||||
|
|
@ -45,8 +46,6 @@ pub(crate) struct GlobalTestOptions {
|
|||
/// Whether inserting extra indent spaces in code block,
|
||||
/// default is `false`, only `true` for generating code link of Rust playground
|
||||
pub(crate) insert_indent_space: bool,
|
||||
/// Additional crate-level attributes to add to doctests.
|
||||
pub(crate) attrs: Vec<String>,
|
||||
/// Path to file containing arguments for the invocation of rustc.
|
||||
pub(crate) args_file: PathBuf,
|
||||
}
|
||||
|
|
@ -283,7 +282,7 @@ pub(crate) fn run_tests(
|
|||
rustdoc_options: &Arc<RustdocOptions>,
|
||||
unused_extern_reports: &Arc<Mutex<Vec<UnusedExterns>>>,
|
||||
mut standalone_tests: Vec<test::TestDescAndFn>,
|
||||
mergeable_tests: FxIndexMap<Edition, Vec<(DocTestBuilder, ScrapedDocTest)>>,
|
||||
mergeable_tests: FxIndexMap<MergeableTestKey, Vec<(DocTestBuilder, ScrapedDocTest)>>,
|
||||
// We pass this argument so we can drop it manually before using `exit`.
|
||||
mut temp_dir: Option<TempDir>,
|
||||
) {
|
||||
|
|
@ -298,7 +297,7 @@ pub(crate) fn run_tests(
|
|||
let mut ran_edition_tests = 0;
|
||||
let target_str = rustdoc_options.target.to_string();
|
||||
|
||||
for (edition, mut doctests) in mergeable_tests {
|
||||
for (MergeableTestKey { edition, global_crate_attrs_hash }, mut doctests) in mergeable_tests {
|
||||
if doctests.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -308,8 +307,8 @@ pub(crate) fn run_tests(
|
|||
|
||||
let rustdoc_test_options = IndividualTestOptions::new(
|
||||
rustdoc_options,
|
||||
&Some(format!("merged_doctest_{edition}")),
|
||||
PathBuf::from(format!("doctest_{edition}.rs")),
|
||||
&Some(format!("merged_doctest_{edition}_{global_crate_attrs_hash}")),
|
||||
PathBuf::from(format!("doctest_{edition}_{global_crate_attrs_hash}.rs")),
|
||||
);
|
||||
|
||||
for (doctest, scraped_test) in &doctests {
|
||||
|
|
@ -371,12 +370,9 @@ fn scrape_test_config(
|
|||
attrs: &[hir::Attribute],
|
||||
args_file: PathBuf,
|
||||
) -> GlobalTestOptions {
|
||||
use rustc_ast_pretty::pprust;
|
||||
|
||||
let mut opts = GlobalTestOptions {
|
||||
crate_name,
|
||||
no_crate_inject: false,
|
||||
attrs: Vec::new(),
|
||||
insert_indent_space: false,
|
||||
args_file,
|
||||
};
|
||||
|
|
@ -393,13 +389,7 @@ fn scrape_test_config(
|
|||
if attr.has_name(sym::no_crate_inject) {
|
||||
opts.no_crate_inject = true;
|
||||
}
|
||||
if attr.has_name(sym::attr)
|
||||
&& let Some(l) = attr.meta_item_list()
|
||||
{
|
||||
for item in l {
|
||||
opts.attrs.push(pprust::meta_list_item_to_string(item));
|
||||
}
|
||||
}
|
||||
// NOTE: `test(attr(..))` is handled when discovering the individual tests
|
||||
}
|
||||
|
||||
opts
|
||||
|
|
@ -848,6 +838,7 @@ pub(crate) struct ScrapedDocTest {
|
|||
text: String,
|
||||
name: String,
|
||||
span: Span,
|
||||
global_crate_attrs: Vec<String>,
|
||||
}
|
||||
|
||||
impl ScrapedDocTest {
|
||||
|
|
@ -858,6 +849,7 @@ impl ScrapedDocTest {
|
|||
langstr: LangString,
|
||||
text: String,
|
||||
span: Span,
|
||||
global_crate_attrs: Vec<String>,
|
||||
) -> Self {
|
||||
let mut item_path = logical_path.join("::");
|
||||
item_path.retain(|c| c != ' ');
|
||||
|
|
@ -867,7 +859,7 @@ impl ScrapedDocTest {
|
|||
let name =
|
||||
format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionaly());
|
||||
|
||||
Self { filename, line, langstr, text, name, span }
|
||||
Self { filename, line, langstr, text, name, span, global_crate_attrs }
|
||||
}
|
||||
fn edition(&self, opts: &RustdocOptions) -> Edition {
|
||||
self.langstr.edition.unwrap_or(opts.edition)
|
||||
|
|
@ -896,9 +888,15 @@ pub(crate) trait DocTestVisitor {
|
|||
fn visit_header(&mut self, _name: &str, _level: u32) {}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub(crate) struct MergeableTestKey {
|
||||
edition: Edition,
|
||||
global_crate_attrs_hash: u64,
|
||||
}
|
||||
|
||||
struct CreateRunnableDocTests {
|
||||
standalone_tests: Vec<test::TestDescAndFn>,
|
||||
mergeable_tests: FxIndexMap<Edition, Vec<(DocTestBuilder, ScrapedDocTest)>>,
|
||||
mergeable_tests: FxIndexMap<MergeableTestKey, Vec<(DocTestBuilder, ScrapedDocTest)>>,
|
||||
|
||||
rustdoc_options: Arc<RustdocOptions>,
|
||||
opts: GlobalTestOptions,
|
||||
|
|
@ -949,6 +947,7 @@ impl CreateRunnableDocTests {
|
|||
let edition = scraped_test.edition(&self.rustdoc_options);
|
||||
let doctest = BuildDocTestBuilder::new(&scraped_test.text)
|
||||
.crate_name(&self.opts.crate_name)
|
||||
.global_crate_attrs(scraped_test.global_crate_attrs.clone())
|
||||
.edition(edition)
|
||||
.can_merge_doctests(self.can_merge_doctests)
|
||||
.test_id(test_id)
|
||||
|
|
@ -965,7 +964,17 @@ impl CreateRunnableDocTests {
|
|||
let test_desc = self.generate_test_desc_and_fn(doctest, scraped_test);
|
||||
self.standalone_tests.push(test_desc);
|
||||
} else {
|
||||
self.mergeable_tests.entry(edition).or_default().push((doctest, scraped_test));
|
||||
self.mergeable_tests
|
||||
.entry(MergeableTestKey {
|
||||
edition,
|
||||
global_crate_attrs_hash: {
|
||||
let mut hasher = FxHasher::default();
|
||||
scraped_test.global_crate_attrs.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
},
|
||||
})
|
||||
.or_default()
|
||||
.push((doctest, scraped_test));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,13 +35,16 @@ impl ExtractedDocTests {
|
|||
) {
|
||||
let edition = scraped_test.edition(options);
|
||||
|
||||
let ScrapedDocTest { filename, line, langstr, text, name, .. } = scraped_test;
|
||||
let ScrapedDocTest { filename, line, langstr, text, name, global_crate_attrs, .. } =
|
||||
scraped_test;
|
||||
|
||||
let doctest = BuildDocTestBuilder::new(&text)
|
||||
.crate_name(&opts.crate_name)
|
||||
.global_crate_attrs(global_crate_attrs)
|
||||
.edition(edition)
|
||||
.lang_str(&langstr)
|
||||
.build(None);
|
||||
|
||||
let (full_test_code, size) = doctest.generate_unique_doctest(
|
||||
&text,
|
||||
langstr.test_harness,
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ pub(crate) struct BuildDocTestBuilder<'a> {
|
|||
test_id: Option<String>,
|
||||
lang_str: Option<&'a LangString>,
|
||||
span: Span,
|
||||
global_crate_attrs: Vec<String>,
|
||||
}
|
||||
|
||||
impl<'a> BuildDocTestBuilder<'a> {
|
||||
|
|
@ -57,6 +58,7 @@ impl<'a> BuildDocTestBuilder<'a> {
|
|||
test_id: None,
|
||||
lang_str: None,
|
||||
span: DUMMY_SP,
|
||||
global_crate_attrs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +98,12 @@ impl<'a> BuildDocTestBuilder<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn global_crate_attrs(mut self, global_crate_attrs: Vec<String>) -> Self {
|
||||
self.global_crate_attrs = global_crate_attrs;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn build(self, dcx: Option<DiagCtxtHandle<'_>>) -> DocTestBuilder {
|
||||
let BuildDocTestBuilder {
|
||||
source,
|
||||
|
|
@ -106,6 +114,7 @@ impl<'a> BuildDocTestBuilder<'a> {
|
|||
test_id,
|
||||
lang_str,
|
||||
span,
|
||||
global_crate_attrs,
|
||||
} = self;
|
||||
let can_merge_doctests = can_merge_doctests
|
||||
&& lang_str.is_some_and(|lang_str| {
|
||||
|
|
@ -133,6 +142,7 @@ impl<'a> BuildDocTestBuilder<'a> {
|
|||
// If the AST returned an error, we don't want this doctest to be merged with the
|
||||
// others.
|
||||
return DocTestBuilder::invalid(
|
||||
Vec::new(),
|
||||
String::new(),
|
||||
String::new(),
|
||||
String::new(),
|
||||
|
|
@ -155,6 +165,7 @@ impl<'a> BuildDocTestBuilder<'a> {
|
|||
DocTestBuilder {
|
||||
supports_color,
|
||||
has_main_fn,
|
||||
global_crate_attrs,
|
||||
crate_attrs,
|
||||
maybe_crate_attrs,
|
||||
crates,
|
||||
|
|
@ -173,6 +184,7 @@ pub(crate) struct DocTestBuilder {
|
|||
pub(crate) supports_color: bool,
|
||||
pub(crate) already_has_extern_crate: bool,
|
||||
pub(crate) has_main_fn: bool,
|
||||
pub(crate) global_crate_attrs: Vec<String>,
|
||||
pub(crate) crate_attrs: String,
|
||||
/// If this is a merged doctest, it will be put into `everything_else`, otherwise it will
|
||||
/// put into `crate_attrs`.
|
||||
|
|
@ -186,6 +198,7 @@ pub(crate) struct DocTestBuilder {
|
|||
|
||||
impl DocTestBuilder {
|
||||
fn invalid(
|
||||
global_crate_attrs: Vec<String>,
|
||||
crate_attrs: String,
|
||||
maybe_crate_attrs: String,
|
||||
crates: String,
|
||||
|
|
@ -195,6 +208,7 @@ impl DocTestBuilder {
|
|||
Self {
|
||||
supports_color: false,
|
||||
has_main_fn: false,
|
||||
global_crate_attrs,
|
||||
crate_attrs,
|
||||
maybe_crate_attrs,
|
||||
crates,
|
||||
|
|
@ -224,7 +238,8 @@ impl DocTestBuilder {
|
|||
let mut line_offset = 0;
|
||||
let mut prog = String::new();
|
||||
let everything_else = self.everything_else.trim();
|
||||
if opts.attrs.is_empty() {
|
||||
|
||||
if self.global_crate_attrs.is_empty() {
|
||||
// If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
|
||||
// lints that are commonly triggered in doctests. The crate-level test attributes are
|
||||
// commonly used to make tests fail in case they trigger warnings, so having this there in
|
||||
|
|
@ -233,8 +248,8 @@ impl DocTestBuilder {
|
|||
line_offset += 1;
|
||||
}
|
||||
|
||||
// Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
|
||||
for attr in &opts.attrs {
|
||||
// Next, any attributes that came from #![doc(test(attr(...)))].
|
||||
for attr in &self.global_crate_attrs {
|
||||
prog.push_str(&format!("#![{attr}]\n"));
|
||||
line_offset += 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ impl DocTestVisitor for MdCollector {
|
|||
config,
|
||||
test,
|
||||
DUMMY_SP,
|
||||
Vec::new(),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +97,6 @@ pub(crate) fn test(input: &Input, options: Options) -> Result<(), String> {
|
|||
crate_name,
|
||||
no_crate_inject: true,
|
||||
insert_indent_space: false,
|
||||
attrs: vec![],
|
||||
args_file,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use crate::html::markdown::{Ignore, LangString};
|
|||
/// Convenient type to merge compatible doctests into one.
|
||||
pub(crate) struct DocTestRunner {
|
||||
crate_attrs: FxIndexSet<String>,
|
||||
global_crate_attrs: FxIndexSet<String>,
|
||||
ids: String,
|
||||
output: String,
|
||||
output_merged_tests: String,
|
||||
|
|
@ -23,6 +24,7 @@ impl DocTestRunner {
|
|||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
crate_attrs: FxIndexSet::default(),
|
||||
global_crate_attrs: FxIndexSet::default(),
|
||||
ids: String::new(),
|
||||
output: String::new(),
|
||||
output_merged_tests: String::new(),
|
||||
|
|
@ -46,6 +48,9 @@ impl DocTestRunner {
|
|||
for line in doctest.crate_attrs.split('\n') {
|
||||
self.crate_attrs.insert(line.to_string());
|
||||
}
|
||||
for line in &doctest.global_crate_attrs {
|
||||
self.global_crate_attrs.insert(line.to_string());
|
||||
}
|
||||
}
|
||||
self.ids.push_str(&format!(
|
||||
"tests.push({}::TEST);\n",
|
||||
|
|
@ -85,7 +90,7 @@ impl DocTestRunner {
|
|||
code_prefix.push('\n');
|
||||
}
|
||||
|
||||
if opts.attrs.is_empty() {
|
||||
if self.global_crate_attrs.is_empty() {
|
||||
// If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
|
||||
// lints that are commonly triggered in doctests. The crate-level test attributes are
|
||||
// commonly used to make tests fail in case they trigger warnings, so having this there in
|
||||
|
|
@ -93,8 +98,8 @@ impl DocTestRunner {
|
|||
code_prefix.push_str("#![allow(unused)]\n");
|
||||
}
|
||||
|
||||
// Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
|
||||
for attr in &opts.attrs {
|
||||
// Next, any attributes that came from #![doc(test(attr(...)))].
|
||||
for attr in &self.global_crate_attrs {
|
||||
code_prefix.push_str(&format!("#![{attr}]\n"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use std::cell::Cell;
|
|||
use std::env;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
|
||||
use rustc_hir::{self as hir, CRATE_HIR_ID, intravisit};
|
||||
|
|
@ -11,7 +12,7 @@ use rustc_middle::hir::nested_filter;
|
|||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_resolve::rustdoc::span_of_fragments;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, DUMMY_SP, FileName, Pos, Span};
|
||||
use rustc_span::{BytePos, DUMMY_SP, FileName, Pos, Span, sym};
|
||||
|
||||
use super::{DocTestVisitor, ScrapedDocTest};
|
||||
use crate::clean::{Attributes, extract_cfg_from_attrs};
|
||||
|
|
@ -22,6 +23,7 @@ struct RustCollector {
|
|||
tests: Vec<ScrapedDocTest>,
|
||||
cur_path: Vec<String>,
|
||||
position: Span,
|
||||
global_crate_attrs: Vec<String>,
|
||||
}
|
||||
|
||||
impl RustCollector {
|
||||
|
|
@ -75,6 +77,7 @@ impl DocTestVisitor for RustCollector {
|
|||
config,
|
||||
test,
|
||||
span,
|
||||
self.global_crate_attrs.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +97,7 @@ impl<'tcx> HirCollector<'tcx> {
|
|||
cur_path: vec![],
|
||||
position: DUMMY_SP,
|
||||
tests: vec![],
|
||||
global_crate_attrs: Vec::new(),
|
||||
};
|
||||
Self { codes, tcx, collector }
|
||||
}
|
||||
|
|
@ -123,6 +127,26 @@ impl HirCollector<'_> {
|
|||
return;
|
||||
}
|
||||
|
||||
// Try collecting `#[doc(test(attr(...)))]`
|
||||
let old_global_crate_attrs_len = self.collector.global_crate_attrs.len();
|
||||
for doc_test_attrs in ast_attrs
|
||||
.iter()
|
||||
.filter(|a| a.has_name(sym::doc))
|
||||
.flat_map(|a| a.meta_item_list().unwrap_or_default())
|
||||
.filter(|a| a.has_name(sym::test))
|
||||
{
|
||||
let Some(doc_test_attrs) = doc_test_attrs.meta_item_list() else { continue };
|
||||
for attr in doc_test_attrs
|
||||
.iter()
|
||||
.filter(|a| a.has_name(sym::attr))
|
||||
.flat_map(|a| a.meta_item_list().unwrap_or_default())
|
||||
.map(|i| pprust::meta_list_item_to_string(i))
|
||||
{
|
||||
// Add the additional attributes to the global_crate_attrs vector
|
||||
self.collector.global_crate_attrs.push(attr);
|
||||
}
|
||||
}
|
||||
|
||||
let mut has_name = false;
|
||||
if let Some(name) = name {
|
||||
self.collector.cur_path.push(name);
|
||||
|
|
@ -157,6 +181,9 @@ impl HirCollector<'_> {
|
|||
|
||||
nested(self);
|
||||
|
||||
// Restore global_crate_attrs to it's previous size/content
|
||||
self.collector.global_crate_attrs.truncate(old_global_crate_attrs_len);
|
||||
|
||||
if has_name {
|
||||
self.collector.cur_path.pop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ fn make_test(
|
|||
crate_name: Option<&str>,
|
||||
dont_insert_main: bool,
|
||||
opts: &GlobalTestOptions,
|
||||
global_crate_attrs: Vec<&str>,
|
||||
test_id: Option<&str>,
|
||||
) -> (String, usize) {
|
||||
let mut builder = BuildDocTestBuilder::new(test_code);
|
||||
let mut builder = BuildDocTestBuilder::new(test_code)
|
||||
.global_crate_attrs(global_crate_attrs.into_iter().map(|a| a.to_string()).collect());
|
||||
if let Some(crate_name) = crate_name {
|
||||
builder = builder.crate_name(crate_name);
|
||||
}
|
||||
|
|
@ -28,7 +30,6 @@ fn default_global_opts(crate_name: impl Into<String>) -> GlobalTestOptions {
|
|||
crate_name: crate_name.into(),
|
||||
no_crate_inject: false,
|
||||
insert_indent_space: false,
|
||||
attrs: vec![],
|
||||
args_file: PathBuf::new(),
|
||||
}
|
||||
}
|
||||
|
|
@ -43,7 +44,7 @@ fn main() {
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ fn main() {
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +78,7 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 3));
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +95,7 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +113,7 @@ use std::*;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, Some("std"), false, &opts, None);
|
||||
let (output, len) = make_test(input, Some("std"), false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -131,7 +132,7 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -148,7 +149,7 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -156,8 +157,7 @@ assert_eq!(2+2, 4);
|
|||
fn make_test_opts_attrs() {
|
||||
// If you supplied some doctest attributes with `#![doc(test(attr(...)))]`, it will use
|
||||
// those instead of the stock `#![allow(unused)]`.
|
||||
let mut opts = default_global_opts("asdf");
|
||||
opts.attrs.push("feature(sick_rad)".to_string());
|
||||
let opts = default_global_opts("asdf");
|
||||
let input = "use asdf::qwop;
|
||||
assert_eq!(2+2, 4);";
|
||||
let expected = "#![feature(sick_rad)]
|
||||
|
|
@ -168,11 +168,10 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
let (output, len) =
|
||||
make_test(input, Some("asdf"), false, &opts, vec!["feature(sick_rad)"], None);
|
||||
assert_eq!((output, len), (expected, 3));
|
||||
|
||||
// Adding more will also bump the returned line offset.
|
||||
opts.attrs.push("feature(hella_dope)".to_string());
|
||||
let expected = "#![feature(sick_rad)]
|
||||
#![feature(hella_dope)]
|
||||
#[allow(unused_extern_crates)]
|
||||
|
|
@ -182,7 +181,18 @@ use asdf::qwop;
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
let (output, len) = make_test(
|
||||
input,
|
||||
Some("asdf"),
|
||||
false,
|
||||
&opts,
|
||||
vec![
|
||||
"feature(sick_rad)",
|
||||
// Adding more will also bump the returned line offset.
|
||||
"feature(hella_dope)",
|
||||
],
|
||||
None,
|
||||
);
|
||||
assert_eq!((output, len), (expected, 4));
|
||||
}
|
||||
|
||||
|
|
@ -200,7 +210,7 @@ fn main() {
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +226,7 @@ fn main() {
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 1));
|
||||
}
|
||||
|
||||
|
|
@ -232,7 +242,7 @@ fn main() {
|
|||
assert_eq!(2+2, 4);
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -246,7 +256,7 @@ assert_eq!(2+2, 4);";
|
|||
//Ceci n'est pas une `fn main`
|
||||
assert_eq!(2+2, 4);"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, true, &opts, None);
|
||||
let (output, len) = make_test(input, None, true, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 1));
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +274,7 @@ assert_eq!(2+2, 4);
|
|||
}"
|
||||
.to_string();
|
||||
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -284,7 +294,7 @@ assert_eq!(asdf::foo, 4);
|
|||
}"
|
||||
.to_string();
|
||||
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, None);
|
||||
let (output, len) = make_test(input, Some("asdf"), false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 3));
|
||||
}
|
||||
|
||||
|
|
@ -302,7 +312,7 @@ test_wrapper! {
|
|||
}"
|
||||
.to_string();
|
||||
|
||||
let (output, len) = make_test(input, Some("my_crate"), false, &opts, None);
|
||||
let (output, len) = make_test(input, Some("my_crate"), false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 1));
|
||||
}
|
||||
|
||||
|
|
@ -322,7 +332,7 @@ io::stdin().read_line(&mut input)?;
|
|||
Ok::<(), io:Error>(())
|
||||
} _inner().unwrap() }"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -336,7 +346,7 @@ fn main() { #[allow(non_snake_case)] fn _doctest_main__some_unique_name() {
|
|||
assert_eq!(2+2, 4);
|
||||
} _doctest_main__some_unique_name() }"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, false, &opts, Some("_some_unique_name"));
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), Some("_some_unique_name"));
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -355,7 +365,7 @@ fn main() {
|
|||
eprintln!(\"hello anan\");
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
||||
|
|
@ -375,7 +385,7 @@ fn main() {
|
|||
eprintln!(\"hello anan\");
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 1));
|
||||
}
|
||||
|
||||
|
|
@ -400,7 +410,7 @@ fn main() {
|
|||
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
|
||||
// And same, if there is a `main` function provided by the user, we ensure that it's
|
||||
|
|
@ -420,7 +430,7 @@ fn main() {}";
|
|||
|
||||
fn main() {}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 1));
|
||||
}
|
||||
|
||||
|
|
@ -448,6 +458,6 @@ pub mod outer_module {
|
|||
}
|
||||
}"
|
||||
.to_string();
|
||||
let (output, len) = make_test(input, None, false, &opts, None);
|
||||
let (output, len) = make_test(input, None, false, &opts, Vec::new(), None);
|
||||
assert_eq!((output, len), (expected, 2));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -300,7 +300,6 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
|
|||
crate_name: krate.map(String::from).unwrap_or_default(),
|
||||
no_crate_inject: false,
|
||||
insert_indent_space: true,
|
||||
attrs: vec![],
|
||||
args_file: PathBuf::new(),
|
||||
};
|
||||
let mut builder = doctest::BuildDocTestBuilder::new(&test).edition(edition);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,22 @@ fn setup_test_env<F: FnOnce(&Path, &Path)>(callback: F) {
|
|||
}
|
||||
|
||||
fn check_generated_binaries() {
|
||||
run("doctests/merged_doctest_2024/rust_out");
|
||||
let mut found_merged_doctest = false;
|
||||
rfs::read_dir_entries("doctests/", |path| {
|
||||
if path
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.is_some_and(|name| name.starts_with("merged_doctest_2024"))
|
||||
{
|
||||
found_merged_doctest = true;
|
||||
let rust_out = path.join("rust_out");
|
||||
let rust_out = rust_out.to_string_lossy();
|
||||
run(&*rust_out);
|
||||
}
|
||||
});
|
||||
if !found_merged_doctest {
|
||||
panic!("no directory starting with `merged_doctest_2024` found under `doctests/`");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
116
tests/rustdoc-ui/doctest/dead-code-items.rs
Normal file
116
tests/rustdoc-ui/doctest/dead-code-items.rs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Same test as dead-code-module but with 2 doc(test(attr())) at different levels.
|
||||
|
||||
//@ edition: 2024
|
||||
//@ compile-flags:--test --test-args=--test-threads=1
|
||||
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
|
||||
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
|
||||
//@ failure-status: 101
|
||||
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait OnlyWarning { fn no_deny_warnings(); }
|
||||
/// ```
|
||||
static S: u32 = 5;
|
||||
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// let unused_error = 5;
|
||||
///
|
||||
/// fn dead_code_but_no_error() {}
|
||||
/// ```
|
||||
const C: u32 = 5;
|
||||
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait OnlyWarningAtA { fn no_deny_warnings(); }
|
||||
/// ```
|
||||
struct A {
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait DeadCodeInField {}
|
||||
/// ```
|
||||
field: u32
|
||||
}
|
||||
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait OnlyWarningAtU { fn no_deny_warnings(); }
|
||||
/// ```
|
||||
union U {
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait DeadCodeInUnionField {}
|
||||
/// ```
|
||||
field: u32,
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait NotDeadCodeInUnionField {}
|
||||
/// ```
|
||||
field2: u64,
|
||||
}
|
||||
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// let not_dead_code_but_unused = 5;
|
||||
/// ```
|
||||
enum Enum {
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait NotDeadCodeInVariant {}
|
||||
///
|
||||
/// fn main() { let unused_in_variant = 5; }
|
||||
/// ```
|
||||
Variant1,
|
||||
}
|
||||
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait OnlyWarningAtImplA { fn no_deny_warnings(); }
|
||||
/// ```
|
||||
impl A {
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait NotDeadCodeInImplMethod {}
|
||||
/// ```
|
||||
fn method() {}
|
||||
}
|
||||
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait StillDeadCodeAtMyTrait { }
|
||||
/// ```
|
||||
trait MyTrait {
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait NotDeadCodeAtImplFn {}
|
||||
///
|
||||
/// fn main() { let unused_in_impl = 5; }
|
||||
/// ```
|
||||
fn my_trait_fn();
|
||||
}
|
||||
146
tests/rustdoc-ui/doctest/dead-code-items.stdout
Normal file
146
tests/rustdoc-ui/doctest/dead-code-items.stdout
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
|
||||
running 13 tests
|
||||
test $DIR/dead-code-items.rs - A (line 32) - compile ... ok
|
||||
test $DIR/dead-code-items.rs - A (line 88) - compile ... ok
|
||||
test $DIR/dead-code-items.rs - A::field (line 39) - compile ... FAILED
|
||||
test $DIR/dead-code-items.rs - A::method (line 94) - compile ... ok
|
||||
test $DIR/dead-code-items.rs - C (line 22) - compile ... FAILED
|
||||
test $DIR/dead-code-items.rs - Enum (line 70) - compile ... FAILED
|
||||
test $DIR/dead-code-items.rs - Enum::Variant1 (line 77) - compile ... FAILED
|
||||
test $DIR/dead-code-items.rs - MyTrait (line 103) - compile ... FAILED
|
||||
test $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110) - compile ... FAILED
|
||||
test $DIR/dead-code-items.rs - S (line 14) - compile ... ok
|
||||
test $DIR/dead-code-items.rs - U (line 48) - compile ... ok
|
||||
test $DIR/dead-code-items.rs - U::field (line 55) - compile ... FAILED
|
||||
test $DIR/dead-code-items.rs - U::field2 (line 61) - compile ... ok
|
||||
|
||||
failures:
|
||||
|
||||
---- $DIR/dead-code-items.rs - A::field (line 39) stdout ----
|
||||
error: trait `DeadCodeInField` is never used
|
||||
--> $DIR/dead-code-items.rs:40:7
|
||||
|
|
||||
LL | trait DeadCodeInField {}
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/dead-code-items.rs:38:9
|
||||
|
|
||||
LL | #![deny(dead_code)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Couldn't compile the test.
|
||||
---- $DIR/dead-code-items.rs - C (line 22) stdout ----
|
||||
error: unused variable: `unused_error`
|
||||
--> $DIR/dead-code-items.rs:23:5
|
||||
|
|
||||
LL | let unused_error = 5;
|
||||
| ^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_error`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/dead-code-items.rs:20:9
|
||||
|
|
||||
LL | #![deny(warnings)]
|
||||
| ^^^^^^^^
|
||||
= note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Couldn't compile the test.
|
||||
---- $DIR/dead-code-items.rs - Enum (line 70) stdout ----
|
||||
error: unused variable: `not_dead_code_but_unused`
|
||||
--> $DIR/dead-code-items.rs:71:5
|
||||
|
|
||||
LL | let not_dead_code_but_unused = 5;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_not_dead_code_but_unused`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/dead-code-items.rs:68:9
|
||||
|
|
||||
LL | #![deny(warnings)]
|
||||
| ^^^^^^^^
|
||||
= note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Couldn't compile the test.
|
||||
---- $DIR/dead-code-items.rs - Enum::Variant1 (line 77) stdout ----
|
||||
error: unused variable: `unused_in_variant`
|
||||
--> $DIR/dead-code-items.rs:80:17
|
||||
|
|
||||
LL | fn main() { let unused_in_variant = 5; }
|
||||
| ^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_in_variant`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/dead-code-items.rs:75:9
|
||||
|
|
||||
LL | #![deny(warnings)]
|
||||
| ^^^^^^^^
|
||||
= note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Couldn't compile the test.
|
||||
---- $DIR/dead-code-items.rs - MyTrait (line 103) stdout ----
|
||||
error: trait `StillDeadCodeAtMyTrait` is never used
|
||||
--> $DIR/dead-code-items.rs:104:7
|
||||
|
|
||||
LL | trait StillDeadCodeAtMyTrait { }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/dead-code-items.rs:102:9
|
||||
|
|
||||
LL | #![deny(dead_code)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Couldn't compile the test.
|
||||
---- $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110) stdout ----
|
||||
error: unused variable: `unused_in_impl`
|
||||
--> $DIR/dead-code-items.rs:113:17
|
||||
|
|
||||
LL | fn main() { let unused_in_impl = 5; }
|
||||
| ^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_in_impl`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/dead-code-items.rs:108:9
|
||||
|
|
||||
LL | #![deny(warnings)]
|
||||
| ^^^^^^^^
|
||||
= note: `#[deny(unused_variables)]` implied by `#[deny(warnings)]`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Couldn't compile the test.
|
||||
---- $DIR/dead-code-items.rs - U::field (line 55) stdout ----
|
||||
error: trait `DeadCodeInUnionField` is never used
|
||||
--> $DIR/dead-code-items.rs:56:7
|
||||
|
|
||||
LL | trait DeadCodeInUnionField {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/dead-code-items.rs:54:9
|
||||
|
|
||||
LL | #![deny(dead_code)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Couldn't compile the test.
|
||||
|
||||
failures:
|
||||
$DIR/dead-code-items.rs - A::field (line 39)
|
||||
$DIR/dead-code-items.rs - C (line 22)
|
||||
$DIR/dead-code-items.rs - Enum (line 70)
|
||||
$DIR/dead-code-items.rs - Enum::Variant1 (line 77)
|
||||
$DIR/dead-code-items.rs - MyTrait (line 103)
|
||||
$DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110)
|
||||
$DIR/dead-code-items.rs - U::field (line 55)
|
||||
|
||||
test result: FAILED. 6 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
|
||||
27
tests/rustdoc-ui/doctest/dead-code-module-2.rs
Normal file
27
tests/rustdoc-ui/doctest/dead-code-module-2.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Same test as dead-code-module but with 2 doc(test(attr())) at different levels.
|
||||
|
||||
//@ edition: 2024
|
||||
//@ compile-flags:--test
|
||||
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
|
||||
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
|
||||
//@ failure-status: 101
|
||||
|
||||
#![doc(test(attr(allow(unused_variables))))]
|
||||
|
||||
mod my_mod {
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait T { fn f(); }
|
||||
/// ```
|
||||
pub fn f() {}
|
||||
}
|
||||
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait OnlyWarning { fn no_deny_warnings(); }
|
||||
/// ```
|
||||
pub fn g() {}
|
||||
35
tests/rustdoc-ui/doctest/dead-code-module-2.stdout
Normal file
35
tests/rustdoc-ui/doctest/dead-code-module-2.stdout
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
running 1 test
|
||||
test $DIR/dead-code-module-2.rs - g (line 24) - compile ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
|
||||
|
||||
running 1 test
|
||||
test $DIR/dead-code-module-2.rs - my_mod::f (line 16) - compile ... FAILED
|
||||
|
||||
failures:
|
||||
|
||||
---- $DIR/dead-code-module-2.rs - my_mod::f (line 16) stdout ----
|
||||
error: trait `T` is never used
|
||||
--> $DIR/dead-code-module-2.rs:17:7
|
||||
|
|
||||
LL | trait T { fn f(); }
|
||||
| ^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/dead-code-module-2.rs:15:9
|
||||
|
|
||||
LL | #![deny(warnings)]
|
||||
| ^^^^^^^^
|
||||
= note: `#[deny(dead_code)]` implied by `#[deny(warnings)]`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Couldn't compile the test.
|
||||
|
||||
failures:
|
||||
$DIR/dead-code-module-2.rs - my_mod::f (line 16)
|
||||
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
|
||||
18
tests/rustdoc-ui/doctest/dead-code-module.rs
Normal file
18
tests/rustdoc-ui/doctest/dead-code-module.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Same test as dead-code but inside a module.
|
||||
|
||||
//@ edition: 2024
|
||||
//@ compile-flags:--test
|
||||
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
|
||||
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
|
||||
//@ failure-status: 101
|
||||
|
||||
mod my_mod {
|
||||
#![doc(test(attr(allow(unused_variables), deny(warnings))))]
|
||||
|
||||
/// Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// trait T { fn f(); }
|
||||
/// ```
|
||||
pub fn f() {}
|
||||
}
|
||||
29
tests/rustdoc-ui/doctest/dead-code-module.stdout
Normal file
29
tests/rustdoc-ui/doctest/dead-code-module.stdout
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
running 1 test
|
||||
test $DIR/dead-code-module.rs - my_mod::f (line 14) - compile ... FAILED
|
||||
|
||||
failures:
|
||||
|
||||
---- $DIR/dead-code-module.rs - my_mod::f (line 14) stdout ----
|
||||
error: trait `T` is never used
|
||||
--> $DIR/dead-code-module.rs:15:7
|
||||
|
|
||||
LL | trait T { fn f(); }
|
||||
| ^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/dead-code-module.rs:13:9
|
||||
|
|
||||
LL | #![deny(warnings)]
|
||||
| ^^^^^^^^
|
||||
= note: `#[deny(dead_code)]` implied by `#[deny(warnings)]`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Couldn't compile the test.
|
||||
|
||||
failures:
|
||||
$DIR/dead-code-module.rs - my_mod::f (line 14)
|
||||
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
|
||||
11
tests/rustdoc-ui/doctest/doc-test-attr-pass-module.rs
Normal file
11
tests/rustdoc-ui/doctest/doc-test-attr-pass-module.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
//@ check-pass
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![deny(invalid_doc_attributes)]
|
||||
#![doc(test(no_crate_inject))]
|
||||
|
||||
mod my_mod {
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
|
||||
pub fn foo() {}
|
||||
}
|
||||
|
|
@ -2,16 +2,14 @@
|
|||
// lints for changes that are not tied to an edition
|
||||
#![deny(future_incompatible)]
|
||||
|
||||
// Error since this is a `future_incompatible` lint
|
||||
macro_rules! m { ($i) => {} } //~ ERROR missing fragment specifier
|
||||
//~| WARN this was previously accepted
|
||||
|
||||
trait Tr {
|
||||
// Warn only since this is not a `future_incompatible` lint
|
||||
fn f(u8) {} //~ WARN anonymous parameters are deprecated
|
||||
//~| WARN this is accepted in the current edition
|
||||
}
|
||||
|
||||
pub mod submodule {
|
||||
// Error since this is a `future_incompatible` lint
|
||||
#![doc(test(some_test))]
|
||||
//~^ ERROR this attribute can only be applied at the crate level
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,20 @@
|
|||
error: missing fragment specifier
|
||||
--> $DIR/future-incompatible-lint-group.rs:6:19
|
||||
|
|
||||
LL | macro_rules! m { ($i) => {} }
|
||||
| ^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
|
||||
note: the lint level is defined here
|
||||
--> $DIR/future-incompatible-lint-group.rs:3:9
|
||||
|
|
||||
LL | #![deny(future_incompatible)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
= note: `#[deny(missing_fragment_specifier)]` implied by `#[deny(future_incompatible)]`
|
||||
|
||||
warning: anonymous parameters are deprecated and will be removed in the next edition
|
||||
--> $DIR/future-incompatible-lint-group.rs:7:10
|
||||
--> $DIR/future-incompatible-lint-group.rs:11:10
|
||||
|
|
||||
LL | fn f(u8) {}
|
||||
| ^^ help: try naming the parameter or explicitly ignoring it: `_: u8`
|
||||
|
|
@ -8,14 +23,21 @@ LL | fn f(u8) {}
|
|||
= note: for more information, see issue #41686 <https://github.com/rust-lang/rust/issues/41686>
|
||||
= note: `#[warn(anonymous_parameters)]` on by default
|
||||
|
||||
error: this attribute can only be applied at the crate level
|
||||
--> $DIR/future-incompatible-lint-group.rs:13:12
|
||||
|
|
||||
LL | #![doc(test(some_test))]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information
|
||||
= note: `#[deny(invalid_doc_attributes)]` on by default
|
||||
|
||||
error: aborting due to 1 previous error; 1 warning emitted
|
||||
|
||||
Future incompatibility report: Future breakage diagnostic:
|
||||
error: missing fragment specifier
|
||||
--> $DIR/future-incompatible-lint-group.rs:6:19
|
||||
|
|
||||
LL | macro_rules! m { ($i) => {} }
|
||||
| ^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
|
||||
note: the lint level is defined here
|
||||
--> $DIR/future-incompatible-lint-group.rs:3:9
|
||||
|
|
||||
LL | #![deny(future_incompatible)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
= note: `#[deny(missing_fragment_specifier)]` implied by `#[deny(future_incompatible)]`
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,13 @@ macro_rules! mac {
|
|||
/// foo //~ ERROR unused doc comment
|
||||
mac!();
|
||||
|
||||
/// a //~ ERROR unused doc comment
|
||||
#[doc(test(attr(allow(dead_code))))] //~ ERROR unused doc comment
|
||||
unsafe extern "C" { }
|
||||
|
||||
fn foo() {
|
||||
/// a //~ ERROR unused doc comment
|
||||
#[doc(test(attr(allow(dead_code))))] //~ ERROR unused doc comment
|
||||
let x = 12;
|
||||
|
||||
/// multi-line //~ ERROR unused doc comment
|
||||
|
|
@ -19,6 +24,7 @@ fn foo() {
|
|||
match x {
|
||||
/// c //~ ERROR unused doc comment
|
||||
1 => {},
|
||||
#[doc(test(attr(allow(dead_code))))] //~ ERROR unused doc comment
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
|
@ -32,6 +38,7 @@ fn foo() {
|
|||
/// bar //~ ERROR unused doc comment
|
||||
mac!();
|
||||
|
||||
#[doc(test(attr(allow(dead_code))))] //~ ERROR unused doc comment
|
||||
let x = /** comment */ 47; //~ ERROR unused doc comment
|
||||
|
||||
/// dox //~ ERROR unused doc comment
|
||||
|
|
|
|||
|
|
@ -12,7 +12,28 @@ LL | #![deny(unused_doc_comments)]
|
|||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:32:5
|
||||
--> $DIR/useless-comment.rs:12:1
|
||||
|
|
||||
LL | /// a
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | #[doc(test(attr(allow(dead_code))))]
|
||||
LL | unsafe extern "C" { }
|
||||
| --------------------- rustdoc does not generate documentation for extern blocks
|
||||
|
|
||||
= help: use `//` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:13:1
|
||||
|
|
||||
LL | #[doc(test(attr(allow(dead_code))))]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | unsafe extern "C" { }
|
||||
| --------------------- rustdoc does not generate documentation for extern blocks
|
||||
|
|
||||
= help: use `//` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:38:5
|
||||
|
|
||||
LL | /// bar
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macro invocations
|
||||
|
|
@ -20,17 +41,28 @@ LL | /// bar
|
|||
= help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:13:5
|
||||
--> $DIR/useless-comment.rs:17:5
|
||||
|
|
||||
LL | /// a
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | #[doc(test(attr(allow(dead_code))))]
|
||||
LL | let x = 12;
|
||||
| ----------- rustdoc does not generate documentation for statements
|
||||
|
|
||||
= help: use `//` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:16:5
|
||||
--> $DIR/useless-comment.rs:18:5
|
||||
|
|
||||
LL | #[doc(test(attr(allow(dead_code))))]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | let x = 12;
|
||||
| ----------- rustdoc does not generate documentation for statements
|
||||
|
|
||||
= help: use `//` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:21:5
|
||||
|
|
||||
LL | / /// multi-line
|
||||
LL | | /// doc comment
|
||||
|
|
@ -39,6 +71,7 @@ LL | | /// that is unused
|
|||
LL | / match x {
|
||||
LL | | /// c
|
||||
LL | | 1 => {},
|
||||
LL | | #[doc(test(attr(allow(dead_code))))]
|
||||
LL | | _ => {}
|
||||
LL | | }
|
||||
| |_____- rustdoc does not generate documentation for expressions
|
||||
|
|
@ -46,7 +79,7 @@ LL | | }
|
|||
= help: use `//` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:20:9
|
||||
--> $DIR/useless-comment.rs:25:9
|
||||
|
|
||||
LL | /// c
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -56,7 +89,17 @@ LL | 1 => {},
|
|||
= help: use `//` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:25:5
|
||||
--> $DIR/useless-comment.rs:27:9
|
||||
|
|
||||
LL | #[doc(test(attr(allow(dead_code))))]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | _ => {}
|
||||
| ------- rustdoc does not generate documentation for match arms
|
||||
|
|
||||
= help: use `//` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:31:5
|
||||
|
|
||||
LL | /// foo
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -66,7 +109,7 @@ LL | unsafe {}
|
|||
= help: use `//` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:28:5
|
||||
--> $DIR/useless-comment.rs:34:5
|
||||
|
|
||||
LL | #[doc = "foo"]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
@ -77,7 +120,7 @@ LL | 3;
|
|||
= help: use `//` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:29:5
|
||||
--> $DIR/useless-comment.rs:35:5
|
||||
|
|
||||
LL | #[doc = "bar"]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
@ -87,7 +130,17 @@ LL | 3;
|
|||
= help: use `//` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:35:13
|
||||
--> $DIR/useless-comment.rs:41:5
|
||||
|
|
||||
LL | #[doc(test(attr(allow(dead_code))))]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | let x = /** comment */ 47;
|
||||
| -------------------------- rustdoc does not generate documentation for statements
|
||||
|
|
||||
= help: use `//` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:42:13
|
||||
|
|
||||
LL | let x = /** comment */ 47;
|
||||
| ^^^^^^^^^^^^^^ -- rustdoc does not generate documentation for expressions
|
||||
|
|
@ -95,7 +148,7 @@ LL | let x = /** comment */ 47;
|
|||
= help: use `/* */` for a plain comment
|
||||
|
||||
error: unused doc comment
|
||||
--> $DIR/useless-comment.rs:37:5
|
||||
--> $DIR/useless-comment.rs:44:5
|
||||
|
|
||||
LL | /// dox
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -106,5 +159,5 @@ LL | | }
|
|||
|
|
||||
= help: use `//` for a plain comment
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
error: aborting due to 15 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -6,4 +6,46 @@
|
|||
#![doc(test(attr(deny(warnings))))]
|
||||
#![doc(test())]
|
||||
|
||||
mod test {
|
||||
#![doc(test(attr(allow(warnings))))]
|
||||
}
|
||||
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
static S: u32 = 5;
|
||||
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
const C: u32 = 5;
|
||||
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
struct A {
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
field: u32
|
||||
}
|
||||
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
union U {
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
field: u32,
|
||||
field2: u64,
|
||||
}
|
||||
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
enum Enum {
|
||||
#[doc(test(attr(allow(dead_code))))]
|
||||
Variant1,
|
||||
}
|
||||
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
impl A {
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
fn method() {}
|
||||
}
|
||||
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
trait MyTrait {
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
fn my_trait_fn();
|
||||
}
|
||||
|
||||
#[doc(test(attr(deny(dead_code))))]
|
||||
pub fn foo() {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue