diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 8f67fe294eb73..bcd5970228959 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -61,7 +61,8 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::{ BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS, - INTRA_DOC_LINK_RESOLUTION_FAILURE, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS, + INTRA_DOC_LINK_RESOLUTION_FAILURE, INVALID_CODEBLOCK_ATTRIBUTE, MISSING_DOC_CODE_EXAMPLES, + PRIVATE_DOC_TESTS, }; use rustc_span::Span; @@ -299,6 +300,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { add_lint_group!( "rustdoc", INTRA_DOC_LINK_RESOLUTION_FAILURE, + INVALID_CODEBLOCK_ATTRIBUTE, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS ); diff --git a/src/librustc_session/lint/builtin.rs b/src/librustc_session/lint/builtin.rs index ce09fd299feca..3ace9ecbd60e5 100644 --- a/src/librustc_session/lint/builtin.rs +++ b/src/librustc_session/lint/builtin.rs @@ -386,6 +386,12 @@ declare_lint! { "failures in resolving intra-doc link targets" } +declare_lint! { + pub INVALID_CODEBLOCK_ATTRIBUTE, + Warn, + "codeblock attribute looks a lot like a known one" +} + declare_lint! { pub MISSING_CRATE_LEVEL_DOCS, Allow, @@ -553,6 +559,7 @@ declare_lint_pass! { UNSTABLE_NAME_COLLISIONS, IRREFUTABLE_LET_PATTERNS, INTRA_DOC_LINK_RESOLUTION_FAILURE, + INVALID_CODEBLOCK_ATTRIBUTE, MISSING_CRATE_LEVEL_DOCS, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index fe2727c962c76..f83bb9b1162de 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -253,6 +253,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let missing_doc_example = rustc_lint::builtin::MISSING_DOC_CODE_EXAMPLES.name; let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name; let no_crate_level_docs = rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS.name; + let invalid_codeblock_attribute_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTE.name; // In addition to those specific lints, we also need to whitelist those given through // command line, otherwise they'll get ignored and we don't want that. @@ -263,6 +264,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt missing_doc_example.to_owned(), private_doc_tests.to_owned(), no_crate_level_docs.to_owned(), + invalid_codeblock_attribute_name.to_owned(), ]; whitelisted_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned()); @@ -275,7 +277,10 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let lint_opts = lints() .filter_map(|lint| { - if lint.name == warnings_lint_name || lint.name == intra_link_resolution_failure_name { + if lint.name == warnings_lint_name + || lint.name == intra_link_resolution_failure_name + || lint.name == invalid_codeblock_attribute_name + { None } else { Some((lint.name_lower(), lint::Allow)) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 941e3a5fa985b..862c20dd16a55 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -20,7 +20,12 @@ #![allow(non_camel_case_types)] use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::DefId; +use rustc_hir::HirId; +use rustc_middle::ty::TyCtxt; +use rustc_session::lint; use rustc_span::edition::Edition; +use rustc_span::Span; use std::borrow::Cow; use std::cell::RefCell; use std::collections::VecDeque; @@ -192,7 +197,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { if let Some(Event::Start(Tag::CodeBlock(kind))) = event { let parse_result = match kind { CodeBlockKind::Fenced(ref lang) => { - LangString::parse(&lang, self.check_error_codes, false) + LangString::parse_without_check(&lang, self.check_error_codes, false) } CodeBlockKind::Indented => LangString::all_false(), }; @@ -560,6 +565,7 @@ pub fn find_testable_code( tests: &mut T, error_codes: ErrorCodes, enable_per_target_ignores: bool, + extra_info: Option<&ExtraInfo<'_, '_>>, ) { let mut parser = Parser::new(doc).into_offset_iter(); let mut prev_offset = 0; @@ -573,7 +579,12 @@ pub fn find_testable_code( if lang.is_empty() { LangString::all_false() } else { - LangString::parse(lang, error_codes, enable_per_target_ignores) + LangString::parse( + lang, + error_codes, + enable_per_target_ignores, + extra_info, + ) } } CodeBlockKind::Indented => LangString::all_false(), @@ -615,6 +626,49 @@ pub fn find_testable_code( } } +pub struct ExtraInfo<'a, 'b> { + hir_id: Option, + item_did: Option, + sp: Span, + tcx: &'a TyCtxt<'b>, +} + +impl<'a, 'b> ExtraInfo<'a, 'b> { + pub fn new(tcx: &'a TyCtxt<'b>, hir_id: HirId, sp: Span) -> ExtraInfo<'a, 'b> { + ExtraInfo { hir_id: Some(hir_id), item_did: None, sp, tcx } + } + + pub fn new_did(tcx: &'a TyCtxt<'b>, did: DefId, sp: Span) -> ExtraInfo<'a, 'b> { + ExtraInfo { hir_id: None, item_did: Some(did), sp, tcx } + } + + fn error_invalid_codeblock_attr(&self, msg: &str, help: &str) { + let hir_id = match (self.hir_id, self.item_did) { + (Some(h), _) => h, + (None, Some(item_did)) => { + match self.tcx.hir().as_local_hir_id(item_did) { + Some(hir_id) => hir_id, + None => { + // If non-local, no need to check anything. + return; + } + } + } + (None, None) => return, + }; + self.tcx.struct_span_lint_hir( + lint::builtin::INVALID_CODEBLOCK_ATTRIBUTE, + hir_id, + self.sp, + |lint| { + let mut diag = lint.build(msg); + diag.help(help); + diag.emit(); + }, + ); + } +} + #[derive(Eq, PartialEq, Clone, Debug)] pub struct LangString { original: String, @@ -652,10 +706,19 @@ impl LangString { } } + fn parse_without_check( + string: &str, + allow_error_code_check: ErrorCodes, + enable_per_target_ignores: bool, + ) -> LangString { + Self::parse(string, allow_error_code_check, enable_per_target_ignores, None) + } + fn parse( string: &str, allow_error_code_check: ErrorCodes, enable_per_target_ignores: bool, + extra: Option<&ExtraInfo<'_, '_>>, ) -> LangString { let allow_error_code_check = allow_error_code_check.as_bool(); let mut seen_rust_tags = false; @@ -715,6 +778,53 @@ impl LangString { seen_other_tags = true; } } + x if extra.is_some() => { + let s = x.to_lowercase(); + match if s == "compile-fail" || s == "compile_fail" || s == "compilefail" { + Some(( + "compile_fail", + "the code block will either not be tested if not marked as a rust one \ + or won't fail if it compiles successfully", + )) + } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" { + Some(( + "should_panic", + "the code block will either not be tested if not marked as a rust one \ + or won't fail if it doesn't panic when running", + )) + } else if s == "no-run" || s == "no_run" || s == "norun" { + Some(( + "no_run", + "the code block will either not be tested if not marked as a rust one \ + or will be run (which you might not want)", + )) + } else if s == "allow-fail" || s == "allow_fail" || s == "allowfail" { + Some(( + "allow_fail", + "the code block will either not be tested if not marked as a rust one \ + or will be run (which you might not want)", + )) + } else if s == "test-harness" || s == "test_harness" || s == "testharness" { + Some(( + "test_harness", + "the code block will either not be tested if not marked as a rust one \ + or the code will be wrapped inside a main function", + )) + } else { + None + } { + Some((flag, help)) => { + if let Some(ref extra) = extra { + extra.error_invalid_codeblock_attr( + &format!("unknown attribute `{}`. Did you mean `{}`?", x, flag), + help, + ); + } + } + None => {} + } + seen_other_tags = true; + } _ => seen_other_tags = true, } } @@ -934,7 +1044,7 @@ crate struct RustCodeBlock { /// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or /// untagged (and assumed to be rust). -crate fn rust_code_blocks(md: &str) -> Vec { +crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_, '_>) -> Vec { let mut code_blocks = vec![]; if md.is_empty() { @@ -951,7 +1061,7 @@ crate fn rust_code_blocks(md: &str) -> Vec { let lang_string = if syntax.is_empty() { LangString::all_false() } else { - LangString::parse(&*syntax, ErrorCodes::Yes, false) + LangString::parse(&*syntax, ErrorCodes::Yes, false, Some(extra_info)) }; if !lang_string.rust { continue; diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 48231ce7b73ef..c871587a0a919 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -64,7 +64,7 @@ fn test_lang_string_parse() { edition: Option, ) { assert_eq!( - LangString::parse(s, ErrorCodes::Yes, true), + LangString::parse(s, ErrorCodes::Yes, true, None), LangString { should_panic, no_run, diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 0a1b5f588158f..b4c0f0ac4c5d9 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -153,7 +153,7 @@ pub fn test(mut options: Options, diag: &rustc_errors::Handler) -> i32 { collector.set_position(DUMMY_SP); let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()); - find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores); + find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None); options.test_args.insert(0, "rustdoctest".to_string()); testing::test_main( diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 3e0ff0b3d9a67..d1f2c12ccd630 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -10,7 +10,7 @@ use crate::clean; use crate::core::DocContext; use crate::fold::DocFolder; use crate::html::markdown::{self, RustCodeBlock}; -use crate::passes::Pass; +use crate::passes::{span_of_attrs, Pass}; pub const CHECK_CODE_BLOCK_SYNTAX: Pass = Pass { name: "check-code-block-syntax", @@ -114,7 +114,9 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { impl<'a, 'tcx> DocFolder for SyntaxChecker<'a, 'tcx> { fn fold_item(&mut self, item: clean::Item) -> Option { if let Some(dox) = &item.attrs.collapsed_doc_value() { - for code_block in markdown::rust_code_blocks(&dox) { + let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); + let extra = crate::html::markdown::ExtraInfo::new_did(&self.cx.tcx, item.def_id, sp); + for code_block in markdown::rust_code_blocks(&dox, &extra) { self.check_rust_syntax(&item, &dox, code_block); } } diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 1afa9c7a26858..70366c90139c2 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -338,7 +338,7 @@ pub fn look_for_tests<'tcx>( let mut tests = Tests { found_tests: 0 }; - find_testable_code(&dox, &mut tests, ErrorCodes::No, false); + find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None); if check_missing_code && tests.found_tests == 0 { let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index afc1501d7b613..e2940efd49c0d 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -5,9 +5,11 @@ use rustc_errors::ErrorReported; use rustc_feature::UnstableFeatures; use rustc_hir as hir; use rustc_hir::intravisit; +use rustc_hir::{HirId, CRATE_HIR_ID}; use rustc_interface::interface; use rustc_middle::hir::map::Map; -use rustc_session::{self, config, DiagnosticOutput, Session}; +use rustc_middle::ty::TyCtxt; +use rustc_session::{self, config, lint, DiagnosticOutput, Session}; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; @@ -25,6 +27,7 @@ use tempfile::Builder as TempFileBuilder; use crate::clean::Attributes; use crate::config::Options; use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; +use crate::passes::span_of_attrs; #[derive(Clone, Default)] pub struct TestOptions { @@ -40,6 +43,45 @@ pub struct TestOptions { pub fn run(options: Options) -> i32 { let input = config::Input::File(options.input.clone()); + let warnings_lint_name = lint::builtin::WARNINGS.name; + let invalid_codeblock_attribute_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTE.name; + + // In addition to those specific lints, we also need to whitelist those given through + // command line, otherwise they'll get ignored and we don't want that. + let mut whitelisted_lints = + vec![warnings_lint_name.to_owned(), invalid_codeblock_attribute_name.to_owned()]; + + whitelisted_lints.extend(options.lint_opts.iter().map(|(lint, _)| lint).cloned()); + + let lints = || { + lint::builtin::HardwiredLints::get_lints() + .into_iter() + .chain(rustc_lint::SoftLints::get_lints().into_iter()) + }; + + let lint_opts = lints() + .filter_map(|lint| { + if lint.name == warnings_lint_name || lint.name == invalid_codeblock_attribute_name { + None + } else { + Some((lint.name_lower(), lint::Allow)) + } + }) + .chain(options.lint_opts.clone().into_iter()) + .collect::>(); + + let lint_caps = lints() + .filter_map(|lint| { + // We don't want to whitelist *all* lints so let's + // ignore those ones. + if whitelisted_lints.iter().any(|l| lint.name == l) { + None + } else { + Some((lint::LintId::of(lint), lint::Allow)) + } + }) + .collect(); + let crate_types = if options.proc_macro_crate { vec![config::CrateType::ProcMacro] } else { @@ -50,10 +92,11 @@ pub fn run(options: Options) -> i32 { maybe_sysroot: options.maybe_sysroot.clone(), search_paths: options.libs.clone(), crate_types, + lint_opts: if !options.display_warnings { lint_opts } else { vec![] }, + lint_cap: Some(options.lint_cap.clone().unwrap_or_else(|| lint::Forbid)), cg: options.codegen_options.clone(), externs: options.externs.clone(), unstable_features: UnstableFeatures::from_environment(), - lint_cap: Some(rustc_session::lint::Level::Allow), actually_rustdoc: true, debugging_opts: config::DebuggingOptions { ..config::basic_debugging_options() }, edition: options.edition, @@ -75,7 +118,7 @@ pub fn run(options: Options) -> i32 { diagnostic_output: DiagnosticOutput::Default, stderr: None, crate_name: options.crate_name.clone(), - lint_caps: Default::default(), + lint_caps, register_lints: None, override_queries: None, registry: rustc_driver::diagnostics_registry(), @@ -105,6 +148,7 @@ pub fn run(options: Options) -> i32 { global_ctxt.enter(|tcx| { let krate = tcx.hir().krate(); + let mut hir_collector = HirCollector { sess: compiler.session(), collector: &mut collector, @@ -112,10 +156,17 @@ pub fn run(options: Options) -> i32 { codes: ErrorCodes::from( compiler.session().opts.unstable_features.is_nightly_build(), ), + tcx, }; - hir_collector.visit_testable("".to_string(), &krate.item.attrs, |this| { - intravisit::walk_crate(this, krate); - }); + hir_collector.visit_testable( + "".to_string(), + &krate.item.attrs, + CRATE_HIR_ID, + krate.item.span, + |this| { + intravisit::walk_crate(this, krate); + }, + ); }); compiler.session().abort_if_errors(); @@ -881,18 +932,21 @@ impl Tester for Collector { } } -struct HirCollector<'a, 'hir> { +struct HirCollector<'a, 'hir, 'tcx> { sess: &'a Session, collector: &'a mut Collector, map: Map<'hir>, codes: ErrorCodes, + tcx: TyCtxt<'tcx>, } -impl<'a, 'hir> HirCollector<'a, 'hir> { +impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { fn visit_testable( &mut self, name: String, attrs: &[ast::Attribute], + hir_id: HirId, + sp: Span, nested: F, ) { let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs); @@ -918,6 +972,11 @@ impl<'a, 'hir> HirCollector<'a, 'hir> { self.collector, self.codes, self.collector.enable_per_target_ignores, + Some(&crate::html::markdown::ExtraInfo::new( + &self.tcx, + hir_id, + span_of_attrs(&attrs).unwrap_or(sp), + )), ); } @@ -929,7 +988,7 @@ impl<'a, 'hir> HirCollector<'a, 'hir> { } } -impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> { +impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> { type Map = Map<'hir>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { @@ -943,25 +1002,25 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> { item.ident.to_string() }; - self.visit_testable(name, &item.attrs, |this| { + self.visit_testable(name, &item.attrs, item.hir_id, item.span, |this| { intravisit::walk_item(this, item); }); } fn visit_trait_item(&mut self, item: &'hir hir::TraitItem) { - self.visit_testable(item.ident.to_string(), &item.attrs, |this| { + self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { intravisit::walk_trait_item(this, item); }); } fn visit_impl_item(&mut self, item: &'hir hir::ImplItem) { - self.visit_testable(item.ident.to_string(), &item.attrs, |this| { + self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { intravisit::walk_impl_item(this, item); }); } fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem) { - self.visit_testable(item.ident.to_string(), &item.attrs, |this| { + self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| { intravisit::walk_foreign_item(this, item); }); } @@ -972,19 +1031,25 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> { g: &'hir hir::Generics, item_id: hir::HirId, ) { - self.visit_testable(v.ident.to_string(), &v.attrs, |this| { + self.visit_testable(v.ident.to_string(), &v.attrs, v.id, v.span, |this| { intravisit::walk_variant(this, v, g, item_id); }); } fn visit_struct_field(&mut self, f: &'hir hir::StructField) { - self.visit_testable(f.ident.to_string(), &f.attrs, |this| { + self.visit_testable(f.ident.to_string(), &f.attrs, f.hir_id, f.span, |this| { intravisit::walk_struct_field(this, f); }); } fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef) { - self.visit_testable(macro_def.ident.to_string(), ¯o_def.attrs, |_| ()); + self.visit_testable( + macro_def.ident.to_string(), + ¯o_def.attrs, + macro_def.hir_id, + macro_def.span, + |_| (), + ); } } diff --git a/src/test/rustdoc-ui/check-attr-test.rs b/src/test/rustdoc-ui/check-attr-test.rs new file mode 100644 index 0000000000000..c4140bbb70a78 --- /dev/null +++ b/src/test/rustdoc-ui/check-attr-test.rs @@ -0,0 +1,38 @@ +// compile-flags:--test + +#![deny(invalid_codeblock_attribute)] + +/// foo +/// +/// ```compile-fail,compilefail,comPile_fail +/// boo +/// ``` +pub fn foo() {} + +/// bar +/// +/// ```should-panic,shouldpanic,shOuld_panic +/// boo +/// ``` +pub fn bar() {} + +/// foobar +/// +/// ```no-run,norun,nO_run +/// boo +/// ``` +pub fn foobar() {} + +/// barfoo +/// +/// ```allow-fail,allowfail,allOw_fail +/// boo +/// ``` +pub fn barfoo() {} + +/// b +/// +/// ```test-harness,testharness,tesT_harness +/// boo +/// ``` +pub fn b() {} diff --git a/src/test/rustdoc-ui/check-attr-test.stderr b/src/test/rustdoc-ui/check-attr-test.stderr new file mode 100644 index 0000000000000..45a2d6ec15e7d --- /dev/null +++ b/src/test/rustdoc-ui/check-attr-test.stderr @@ -0,0 +1,187 @@ +error: unknown attribute `compile-fail`. Did you mean `compile_fail`? + --> $DIR/check-attr-test.rs:5:1 + | +5 | / /// foo +6 | | /// +7 | | /// ```compile-fail,compilefail,comPile_fail +8 | | /// boo +9 | | /// ``` + | |_______^ + | +note: the lint level is defined here + --> $DIR/check-attr-test.rs:3:9 + | +3 | #![deny(invalid_codeblock_attribute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `compilefail`. Did you mean `compile_fail`? + --> $DIR/check-attr-test.rs:5:1 + | +5 | / /// foo +6 | | /// +7 | | /// ```compile-fail,compilefail,comPile_fail +8 | | /// boo +9 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `comPile_fail`. Did you mean `compile_fail`? + --> $DIR/check-attr-test.rs:5:1 + | +5 | / /// foo +6 | | /// +7 | | /// ```compile-fail,compilefail,comPile_fail +8 | | /// boo +9 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `should-panic`. Did you mean `should_panic`? + --> $DIR/check-attr-test.rs:12:1 + | +12 | / /// bar +13 | | /// +14 | | /// ```should-panic,shouldpanic,shOuld_panic +15 | | /// boo +16 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `shouldpanic`. Did you mean `should_panic`? + --> $DIR/check-attr-test.rs:12:1 + | +12 | / /// bar +13 | | /// +14 | | /// ```should-panic,shouldpanic,shOuld_panic +15 | | /// boo +16 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `shOuld_panic`. Did you mean `should_panic`? + --> $DIR/check-attr-test.rs:12:1 + | +12 | / /// bar +13 | | /// +14 | | /// ```should-panic,shouldpanic,shOuld_panic +15 | | /// boo +16 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `no-run`. Did you mean `no_run`? + --> $DIR/check-attr-test.rs:19:1 + | +19 | / /// foobar +20 | | /// +21 | | /// ```no-run,norun,nO_run +22 | | /// boo +23 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `norun`. Did you mean `no_run`? + --> $DIR/check-attr-test.rs:19:1 + | +19 | / /// foobar +20 | | /// +21 | | /// ```no-run,norun,nO_run +22 | | /// boo +23 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `nO_run`. Did you mean `no_run`? + --> $DIR/check-attr-test.rs:19:1 + | +19 | / /// foobar +20 | | /// +21 | | /// ```no-run,norun,nO_run +22 | | /// boo +23 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `allow-fail`. Did you mean `allow_fail`? + --> $DIR/check-attr-test.rs:26:1 + | +26 | / /// barfoo +27 | | /// +28 | | /// ```allow-fail,allowfail,allOw_fail +29 | | /// boo +30 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `allowfail`. Did you mean `allow_fail`? + --> $DIR/check-attr-test.rs:26:1 + | +26 | / /// barfoo +27 | | /// +28 | | /// ```allow-fail,allowfail,allOw_fail +29 | | /// boo +30 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `allOw_fail`. Did you mean `allow_fail`? + --> $DIR/check-attr-test.rs:26:1 + | +26 | / /// barfoo +27 | | /// +28 | | /// ```allow-fail,allowfail,allOw_fail +29 | | /// boo +30 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `test-harness`. Did you mean `test_harness`? + --> $DIR/check-attr-test.rs:33:1 + | +33 | / /// b +34 | | /// +35 | | /// ```test-harness,testharness,tesT_harness +36 | | /// boo +37 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: unknown attribute `testharness`. Did you mean `test_harness`? + --> $DIR/check-attr-test.rs:33:1 + | +33 | / /// b +34 | | /// +35 | | /// ```test-harness,testharness,tesT_harness +36 | | /// boo +37 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: unknown attribute `tesT_harness`. Did you mean `test_harness`? + --> $DIR/check-attr-test.rs:33:1 + | +33 | / /// b +34 | | /// +35 | | /// ```test-harness,testharness,tesT_harness +36 | | /// boo +37 | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: aborting due to 15 previous errors + diff --git a/src/test/rustdoc-ui/check-attr.rs b/src/test/rustdoc-ui/check-attr.rs new file mode 100644 index 0000000000000..a93ec29131907 --- /dev/null +++ b/src/test/rustdoc-ui/check-attr.rs @@ -0,0 +1,51 @@ +#![deny(invalid_codeblock_attribute)] + +/// foo +//~^ ERROR +//~^^ ERROR +//~^^^ ERROR +/// +/// ```compile-fail,compilefail,comPile_fail +/// boo +/// ``` +pub fn foo() {} + +/// bar +//~^ ERROR +//~^^ ERROR +//~^^^ ERROR +/// +/// ```should-panic,shouldpanic,sHould_panic +/// boo +/// ``` +pub fn bar() {} + +/// foobar +//~^ ERROR +//~^^ ERROR +//~^^^ ERROR +/// +/// ```no-run,norun,no_Run +/// boo +/// ``` +pub fn foobar() {} + +/// barfoo +//~^ ERROR +//~^^ ERROR +//~^^^ ERROR +/// +/// ```allow-fail,allowfail,alLow_fail +/// boo +/// ``` +pub fn barfoo() {} + +/// b +//~^ ERROR +//~^^ ERROR +//~^^^ ERROR +/// +/// ```test-harness,testharness,teSt_harness +/// boo +/// ``` +pub fn b() {} diff --git a/src/test/rustdoc-ui/check-attr.stderr b/src/test/rustdoc-ui/check-attr.stderr new file mode 100644 index 0000000000000..5d6939bd09205 --- /dev/null +++ b/src/test/rustdoc-ui/check-attr.stderr @@ -0,0 +1,217 @@ +error: unknown attribute `compile-fail`. Did you mean `compile_fail`? + --> $DIR/check-attr.rs:3:1 + | +LL | / /// foo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | +note: the lint level is defined here + --> $DIR/check-attr.rs:1:9 + | +LL | #![deny(invalid_codeblock_attribute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `compilefail`. Did you mean `compile_fail`? + --> $DIR/check-attr.rs:3:1 + | +LL | / /// foo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `comPile_fail`. Did you mean `compile_fail`? + --> $DIR/check-attr.rs:3:1 + | +LL | / /// foo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully + +error: unknown attribute `should-panic`. Did you mean `should_panic`? + --> $DIR/check-attr.rs:13:1 + | +LL | / /// bar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `shouldpanic`. Did you mean `should_panic`? + --> $DIR/check-attr.rs:13:1 + | +LL | / /// bar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `sHould_panic`. Did you mean `should_panic`? + --> $DIR/check-attr.rs:13:1 + | +LL | / /// bar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running + +error: unknown attribute `no-run`. Did you mean `no_run`? + --> $DIR/check-attr.rs:23:1 + | +LL | / /// foobar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `norun`. Did you mean `no_run`? + --> $DIR/check-attr.rs:23:1 + | +LL | / /// foobar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `no_Run`. Did you mean `no_run`? + --> $DIR/check-attr.rs:23:1 + | +LL | / /// foobar +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `allow-fail`. Did you mean `allow_fail`? + --> $DIR/check-attr.rs:33:1 + | +LL | / /// barfoo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `allowfail`. Did you mean `allow_fail`? + --> $DIR/check-attr.rs:33:1 + | +LL | / /// barfoo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `alLow_fail`. Did you mean `allow_fail`? + --> $DIR/check-attr.rs:33:1 + | +LL | / /// barfoo +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want) + +error: unknown attribute `test-harness`. Did you mean `test_harness`? + --> $DIR/check-attr.rs:43:1 + | +LL | / /// b +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: unknown attribute `testharness`. Did you mean `test_harness`? + --> $DIR/check-attr.rs:43:1 + | +LL | / /// b +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: unknown attribute `teSt_harness`. Did you mean `test_harness`? + --> $DIR/check-attr.rs:43:1 + | +LL | / /// b +LL | | +LL | | +LL | | +... | +LL | | /// boo +LL | | /// ``` + | |_______^ + | + = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function + +error: aborting due to 15 previous errors +