Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0959802

Browse files
authoredApr 23, 2020
Rollup merge of #71408 - GuillaumeGomez:check-code-blocks-tags, r=kinnison
Check code blocks tags Fixes #71347. Explanations here: I realized recently that it was a common issue to confuse/misspell tags on code blocks. This is actually quite a big issue since it generally ends up in a code blocks being ignored since it's not being considered as a rust one. With this new warning, users will at least be notified about it. PS: some improvements can be done on the error rendering but considering how big the PR already is, I think it's better to do it afterwards. r? @ollie27 cc @rust-lang/rustdoc
2 parents 2a1cd44 + 977603c commit 0959802

File tree

13 files changed

+711
-27
lines changed

13 files changed

+711
-27
lines changed
 

‎src/librustc_lint/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ use rustc_middle::ty::query::Providers;
6161
use rustc_middle::ty::TyCtxt;
6262
use rustc_session::lint::builtin::{
6363
BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS,
64-
INTRA_DOC_LINK_RESOLUTION_FAILURE, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS,
64+
INTRA_DOC_LINK_RESOLUTION_FAILURE, INVALID_CODEBLOCK_ATTRIBUTE, MISSING_DOC_CODE_EXAMPLES,
65+
PRIVATE_DOC_TESTS,
6566
};
6667
use rustc_span::Span;
6768

@@ -299,6 +300,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
299300
add_lint_group!(
300301
"rustdoc",
301302
INTRA_DOC_LINK_RESOLUTION_FAILURE,
303+
INVALID_CODEBLOCK_ATTRIBUTE,
302304
MISSING_DOC_CODE_EXAMPLES,
303305
PRIVATE_DOC_TESTS
304306
);

‎src/librustc_session/lint/builtin.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,12 @@ declare_lint! {
386386
"failures in resolving intra-doc link targets"
387387
}
388388

389+
declare_lint! {
390+
pub INVALID_CODEBLOCK_ATTRIBUTE,
391+
Warn,
392+
"codeblock attribute looks a lot like a known one"
393+
}
394+
389395
declare_lint! {
390396
pub MISSING_CRATE_LEVEL_DOCS,
391397
Allow,
@@ -553,6 +559,7 @@ declare_lint_pass! {
553559
UNSTABLE_NAME_COLLISIONS,
554560
IRREFUTABLE_LET_PATTERNS,
555561
INTRA_DOC_LINK_RESOLUTION_FAILURE,
562+
INVALID_CODEBLOCK_ATTRIBUTE,
556563
MISSING_CRATE_LEVEL_DOCS,
557564
MISSING_DOC_CODE_EXAMPLES,
558565
PRIVATE_DOC_TESTS,

‎src/librustdoc/core.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
253253
let missing_doc_example = rustc_lint::builtin::MISSING_DOC_CODE_EXAMPLES.name;
254254
let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name;
255255
let no_crate_level_docs = rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS.name;
256+
let invalid_codeblock_attribute_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTE.name;
256257

257258
// In addition to those specific lints, we also need to whitelist those given through
258259
// 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
263264
missing_doc_example.to_owned(),
264265
private_doc_tests.to_owned(),
265266
no_crate_level_docs.to_owned(),
267+
invalid_codeblock_attribute_name.to_owned(),
266268
];
267269

268270
whitelisted_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned());
@@ -275,7 +277,10 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
275277

276278
let lint_opts = lints()
277279
.filter_map(|lint| {
278-
if lint.name == warnings_lint_name || lint.name == intra_link_resolution_failure_name {
280+
if lint.name == warnings_lint_name
281+
|| lint.name == intra_link_resolution_failure_name
282+
|| lint.name == invalid_codeblock_attribute_name
283+
{
279284
None
280285
} else {
281286
Some((lint.name_lower(), lint::Allow))

‎src/librustdoc/html/markdown.rs

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@
2020
#![allow(non_camel_case_types)]
2121

2222
use rustc_data_structures::fx::FxHashMap;
23+
use rustc_hir::def_id::DefId;
24+
use rustc_hir::HirId;
25+
use rustc_middle::ty::TyCtxt;
26+
use rustc_session::lint;
2327
use rustc_span::edition::Edition;
28+
use rustc_span::Span;
2429
use std::borrow::Cow;
2530
use std::cell::RefCell;
2631
use std::collections::VecDeque;
@@ -192,7 +197,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
192197
if let Some(Event::Start(Tag::CodeBlock(kind))) = event {
193198
let parse_result = match kind {
194199
CodeBlockKind::Fenced(ref lang) => {
195-
LangString::parse(&lang, self.check_error_codes, false)
200+
LangString::parse_without_check(&lang, self.check_error_codes, false)
196201
}
197202
CodeBlockKind::Indented => LangString::all_false(),
198203
};
@@ -560,6 +565,7 @@ pub fn find_testable_code<T: test::Tester>(
560565
tests: &mut T,
561566
error_codes: ErrorCodes,
562567
enable_per_target_ignores: bool,
568+
extra_info: Option<&ExtraInfo<'_, '_>>,
563569
) {
564570
let mut parser = Parser::new(doc).into_offset_iter();
565571
let mut prev_offset = 0;
@@ -573,7 +579,12 @@ pub fn find_testable_code<T: test::Tester>(
573579
if lang.is_empty() {
574580
LangString::all_false()
575581
} else {
576-
LangString::parse(lang, error_codes, enable_per_target_ignores)
582+
LangString::parse(
583+
lang,
584+
error_codes,
585+
enable_per_target_ignores,
586+
extra_info,
587+
)
577588
}
578589
}
579590
CodeBlockKind::Indented => LangString::all_false(),
@@ -615,6 +626,49 @@ pub fn find_testable_code<T: test::Tester>(
615626
}
616627
}
617628

629+
pub struct ExtraInfo<'a, 'b> {
630+
hir_id: Option<HirId>,
631+
item_did: Option<DefId>,
632+
sp: Span,
633+
tcx: &'a TyCtxt<'b>,
634+
}
635+
636+
impl<'a, 'b> ExtraInfo<'a, 'b> {
637+
pub fn new(tcx: &'a TyCtxt<'b>, hir_id: HirId, sp: Span) -> ExtraInfo<'a, 'b> {
638+
ExtraInfo { hir_id: Some(hir_id), item_did: None, sp, tcx }
639+
}
640+
641+
pub fn new_did(tcx: &'a TyCtxt<'b>, did: DefId, sp: Span) -> ExtraInfo<'a, 'b> {
642+
ExtraInfo { hir_id: None, item_did: Some(did), sp, tcx }
643+
}
644+
645+
fn error_invalid_codeblock_attr(&self, msg: &str, help: &str) {
646+
let hir_id = match (self.hir_id, self.item_did) {
647+
(Some(h), _) => h,
648+
(None, Some(item_did)) => {
649+
match self.tcx.hir().as_local_hir_id(item_did) {
650+
Some(hir_id) => hir_id,
651+
None => {
652+
// If non-local, no need to check anything.
653+
return;
654+
}
655+
}
656+
}
657+
(None, None) => return,
658+
};
659+
self.tcx.struct_span_lint_hir(
660+
lint::builtin::INVALID_CODEBLOCK_ATTRIBUTE,
661+
hir_id,
662+
self.sp,
663+
|lint| {
664+
let mut diag = lint.build(msg);
665+
diag.help(help);
666+
diag.emit();
667+
},
668+
);
669+
}
670+
}
671+
618672
#[derive(Eq, PartialEq, Clone, Debug)]
619673
pub struct LangString {
620674
original: String,
@@ -652,10 +706,19 @@ impl LangString {
652706
}
653707
}
654708

709+
fn parse_without_check(
710+
string: &str,
711+
allow_error_code_check: ErrorCodes,
712+
enable_per_target_ignores: bool,
713+
) -> LangString {
714+
Self::parse(string, allow_error_code_check, enable_per_target_ignores, None)
715+
}
716+
655717
fn parse(
656718
string: &str,
657719
allow_error_code_check: ErrorCodes,
658720
enable_per_target_ignores: bool,
721+
extra: Option<&ExtraInfo<'_, '_>>,
659722
) -> LangString {
660723
let allow_error_code_check = allow_error_code_check.as_bool();
661724
let mut seen_rust_tags = false;
@@ -715,6 +778,53 @@ impl LangString {
715778
seen_other_tags = true;
716779
}
717780
}
781+
x if extra.is_some() => {
782+
let s = x.to_lowercase();
783+
match if s == "compile-fail" || s == "compile_fail" || s == "compilefail" {
784+
Some((
785+
"compile_fail",
786+
"the code block will either not be tested if not marked as a rust one \
787+
or won't fail if it compiles successfully",
788+
))
789+
} else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
790+
Some((
791+
"should_panic",
792+
"the code block will either not be tested if not marked as a rust one \
793+
or won't fail if it doesn't panic when running",
794+
))
795+
} else if s == "no-run" || s == "no_run" || s == "norun" {
796+
Some((
797+
"no_run",
798+
"the code block will either not be tested if not marked as a rust one \
799+
or will be run (which you might not want)",
800+
))
801+
} else if s == "allow-fail" || s == "allow_fail" || s == "allowfail" {
802+
Some((
803+
"allow_fail",
804+
"the code block will either not be tested if not marked as a rust one \
805+
or will be run (which you might not want)",
806+
))
807+
} else if s == "test-harness" || s == "test_harness" || s == "testharness" {
808+
Some((
809+
"test_harness",
810+
"the code block will either not be tested if not marked as a rust one \
811+
or the code will be wrapped inside a main function",
812+
))
813+
} else {
814+
None
815+
} {
816+
Some((flag, help)) => {
817+
if let Some(ref extra) = extra {
818+
extra.error_invalid_codeblock_attr(
819+
&format!("unknown attribute `{}`. Did you mean `{}`?", x, flag),
820+
help,
821+
);
822+
}
823+
}
824+
None => {}
825+
}
826+
seen_other_tags = true;
827+
}
718828
_ => seen_other_tags = true,
719829
}
720830
}
@@ -934,7 +1044,7 @@ crate struct RustCodeBlock {
9341044

9351045
/// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or
9361046
/// untagged (and assumed to be rust).
937-
crate fn rust_code_blocks(md: &str) -> Vec<RustCodeBlock> {
1047+
crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_, '_>) -> Vec<RustCodeBlock> {
9381048
let mut code_blocks = vec![];
9391049

9401050
if md.is_empty() {
@@ -951,7 +1061,7 @@ crate fn rust_code_blocks(md: &str) -> Vec<RustCodeBlock> {
9511061
let lang_string = if syntax.is_empty() {
9521062
LangString::all_false()
9531063
} else {
954-
LangString::parse(&*syntax, ErrorCodes::Yes, false)
1064+
LangString::parse(&*syntax, ErrorCodes::Yes, false, Some(extra_info))
9551065
};
9561066
if !lang_string.rust {
9571067
continue;

‎src/librustdoc/html/markdown/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ fn test_lang_string_parse() {
6464
edition: Option<Edition>,
6565
) {
6666
assert_eq!(
67-
LangString::parse(s, ErrorCodes::Yes, true),
67+
LangString::parse(s, ErrorCodes::Yes, true, None),
6868
LangString {
6969
should_panic,
7070
no_run,

‎src/librustdoc/markdown.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ pub fn test(mut options: Options, diag: &rustc_errors::Handler) -> i32 {
153153
collector.set_position(DUMMY_SP);
154154
let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
155155

156-
find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores);
156+
find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None);
157157

158158
options.test_args.insert(0, "rustdoctest".to_string());
159159
testing::test_main(

‎src/librustdoc/passes/check_code_block_syntax.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::clean;
1010
use crate::core::DocContext;
1111
use crate::fold::DocFolder;
1212
use crate::html::markdown::{self, RustCodeBlock};
13-
use crate::passes::Pass;
13+
use crate::passes::{span_of_attrs, Pass};
1414

1515
pub const CHECK_CODE_BLOCK_SYNTAX: Pass = Pass {
1616
name: "check-code-block-syntax",
@@ -114,7 +114,9 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
114114
impl<'a, 'tcx> DocFolder for SyntaxChecker<'a, 'tcx> {
115115
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
116116
if let Some(dox) = &item.attrs.collapsed_doc_value() {
117-
for code_block in markdown::rust_code_blocks(&dox) {
117+
let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span());
118+
let extra = crate::html::markdown::ExtraInfo::new_did(&self.cx.tcx, item.def_id, sp);
119+
for code_block in markdown::rust_code_blocks(&dox, &extra) {
118120
self.check_rust_syntax(&item, &dox, code_block);
119121
}
120122
}

‎src/librustdoc/passes/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ pub fn look_for_tests<'tcx>(
338338

339339
let mut tests = Tests { found_tests: 0 };
340340

341-
find_testable_code(&dox, &mut tests, ErrorCodes::No, false);
341+
find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None);
342342

343343
if check_missing_code && tests.found_tests == 0 {
344344
let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span());

‎src/librustdoc/test.rs

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ use rustc_errors::ErrorReported;
55
use rustc_feature::UnstableFeatures;
66
use rustc_hir as hir;
77
use rustc_hir::intravisit;
8+
use rustc_hir::{HirId, CRATE_HIR_ID};
89
use rustc_interface::interface;
910
use rustc_middle::hir::map::Map;
10-
use rustc_session::{self, config, DiagnosticOutput, Session};
11+
use rustc_middle::ty::TyCtxt;
12+
use rustc_session::{self, config, lint, DiagnosticOutput, Session};
1113
use rustc_span::edition::Edition;
1214
use rustc_span::source_map::SourceMap;
1315
use rustc_span::symbol::sym;
@@ -25,6 +27,7 @@ use tempfile::Builder as TempFileBuilder;
2527
use crate::clean::Attributes;
2628
use crate::config::Options;
2729
use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
30+
use crate::passes::span_of_attrs;
2831

2932
#[derive(Clone, Default)]
3033
pub struct TestOptions {
@@ -40,6 +43,45 @@ pub struct TestOptions {
4043
pub fn run(options: Options) -> i32 {
4144
let input = config::Input::File(options.input.clone());
4245

46+
let warnings_lint_name = lint::builtin::WARNINGS.name;
47+
let invalid_codeblock_attribute_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTE.name;
48+
49+
// In addition to those specific lints, we also need to whitelist those given through
50+
// command line, otherwise they'll get ignored and we don't want that.
51+
let mut whitelisted_lints =
52+
vec![warnings_lint_name.to_owned(), invalid_codeblock_attribute_name.to_owned()];
53+
54+
whitelisted_lints.extend(options.lint_opts.iter().map(|(lint, _)| lint).cloned());
55+
56+
let lints = || {
57+
lint::builtin::HardwiredLints::get_lints()
58+
.into_iter()
59+
.chain(rustc_lint::SoftLints::get_lints().into_iter())
60+
};
61+
62+
let lint_opts = lints()
63+
.filter_map(|lint| {
64+
if lint.name == warnings_lint_name || lint.name == invalid_codeblock_attribute_name {
65+
None
66+
} else {
67+
Some((lint.name_lower(), lint::Allow))
68+
}
69+
})
70+
.chain(options.lint_opts.clone().into_iter())
71+
.collect::<Vec<_>>();
72+
73+
let lint_caps = lints()
74+
.filter_map(|lint| {
75+
// We don't want to whitelist *all* lints so let's
76+
// ignore those ones.
77+
if whitelisted_lints.iter().any(|l| lint.name == l) {
78+
None
79+
} else {
80+
Some((lint::LintId::of(lint), lint::Allow))
81+
}
82+
})
83+
.collect();
84+
4385
let crate_types = if options.proc_macro_crate {
4486
vec![config::CrateType::ProcMacro]
4587
} else {
@@ -50,10 +92,11 @@ pub fn run(options: Options) -> i32 {
5092
maybe_sysroot: options.maybe_sysroot.clone(),
5193
search_paths: options.libs.clone(),
5294
crate_types,
95+
lint_opts: if !options.display_warnings { lint_opts } else { vec![] },
96+
lint_cap: Some(options.lint_cap.clone().unwrap_or_else(|| lint::Forbid)),
5397
cg: options.codegen_options.clone(),
5498
externs: options.externs.clone(),
5599
unstable_features: UnstableFeatures::from_environment(),
56-
lint_cap: Some(rustc_session::lint::Level::Allow),
57100
actually_rustdoc: true,
58101
debugging_opts: config::DebuggingOptions { ..config::basic_debugging_options() },
59102
edition: options.edition,
@@ -75,7 +118,7 @@ pub fn run(options: Options) -> i32 {
75118
diagnostic_output: DiagnosticOutput::Default,
76119
stderr: None,
77120
crate_name: options.crate_name.clone(),
78-
lint_caps: Default::default(),
121+
lint_caps,
79122
register_lints: None,
80123
override_queries: None,
81124
registry: rustc_driver::diagnostics_registry(),
@@ -105,17 +148,25 @@ pub fn run(options: Options) -> i32 {
105148

106149
global_ctxt.enter(|tcx| {
107150
let krate = tcx.hir().krate();
151+
108152
let mut hir_collector = HirCollector {
109153
sess: compiler.session(),
110154
collector: &mut collector,
111155
map: tcx.hir(),
112156
codes: ErrorCodes::from(
113157
compiler.session().opts.unstable_features.is_nightly_build(),
114158
),
159+
tcx,
115160
};
116-
hir_collector.visit_testable("".to_string(), &krate.item.attrs, |this| {
117-
intravisit::walk_crate(this, krate);
118-
});
161+
hir_collector.visit_testable(
162+
"".to_string(),
163+
&krate.item.attrs,
164+
CRATE_HIR_ID,
165+
krate.item.span,
166+
|this| {
167+
intravisit::walk_crate(this, krate);
168+
},
169+
);
119170
});
120171
compiler.session().abort_if_errors();
121172

@@ -881,18 +932,21 @@ impl Tester for Collector {
881932
}
882933
}
883934

884-
struct HirCollector<'a, 'hir> {
935+
struct HirCollector<'a, 'hir, 'tcx> {
885936
sess: &'a Session,
886937
collector: &'a mut Collector,
887938
map: Map<'hir>,
888939
codes: ErrorCodes,
940+
tcx: TyCtxt<'tcx>,
889941
}
890942

891-
impl<'a, 'hir> HirCollector<'a, 'hir> {
943+
impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
892944
fn visit_testable<F: FnOnce(&mut Self)>(
893945
&mut self,
894946
name: String,
895947
attrs: &[ast::Attribute],
948+
hir_id: HirId,
949+
sp: Span,
896950
nested: F,
897951
) {
898952
let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs);
@@ -918,6 +972,11 @@ impl<'a, 'hir> HirCollector<'a, 'hir> {
918972
self.collector,
919973
self.codes,
920974
self.collector.enable_per_target_ignores,
975+
Some(&crate::html::markdown::ExtraInfo::new(
976+
&self.tcx,
977+
hir_id,
978+
span_of_attrs(&attrs).unwrap_or(sp),
979+
)),
921980
);
922981
}
923982

@@ -929,7 +988,7 @@ impl<'a, 'hir> HirCollector<'a, 'hir> {
929988
}
930989
}
931990

932-
impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> {
991+
impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> {
933992
type Map = Map<'hir>;
934993

935994
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
@@ -943,25 +1002,25 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> {
9431002
item.ident.to_string()
9441003
};
9451004

946-
self.visit_testable(name, &item.attrs, |this| {
1005+
self.visit_testable(name, &item.attrs, item.hir_id, item.span, |this| {
9471006
intravisit::walk_item(this, item);
9481007
});
9491008
}
9501009

9511010
fn visit_trait_item(&mut self, item: &'hir hir::TraitItem) {
952-
self.visit_testable(item.ident.to_string(), &item.attrs, |this| {
1011+
self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| {
9531012
intravisit::walk_trait_item(this, item);
9541013
});
9551014
}
9561015

9571016
fn visit_impl_item(&mut self, item: &'hir hir::ImplItem) {
958-
self.visit_testable(item.ident.to_string(), &item.attrs, |this| {
1017+
self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| {
9591018
intravisit::walk_impl_item(this, item);
9601019
});
9611020
}
9621021

9631022
fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem) {
964-
self.visit_testable(item.ident.to_string(), &item.attrs, |this| {
1023+
self.visit_testable(item.ident.to_string(), &item.attrs, item.hir_id, item.span, |this| {
9651024
intravisit::walk_foreign_item(this, item);
9661025
});
9671026
}
@@ -972,19 +1031,25 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> {
9721031
g: &'hir hir::Generics,
9731032
item_id: hir::HirId,
9741033
) {
975-
self.visit_testable(v.ident.to_string(), &v.attrs, |this| {
1034+
self.visit_testable(v.ident.to_string(), &v.attrs, v.id, v.span, |this| {
9761035
intravisit::walk_variant(this, v, g, item_id);
9771036
});
9781037
}
9791038

9801039
fn visit_struct_field(&mut self, f: &'hir hir::StructField) {
981-
self.visit_testable(f.ident.to_string(), &f.attrs, |this| {
1040+
self.visit_testable(f.ident.to_string(), &f.attrs, f.hir_id, f.span, |this| {
9821041
intravisit::walk_struct_field(this, f);
9831042
});
9841043
}
9851044

9861045
fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef) {
987-
self.visit_testable(macro_def.ident.to_string(), &macro_def.attrs, |_| ());
1046+
self.visit_testable(
1047+
macro_def.ident.to_string(),
1048+
&macro_def.attrs,
1049+
macro_def.hir_id,
1050+
macro_def.span,
1051+
|_| (),
1052+
);
9881053
}
9891054
}
9901055

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// compile-flags:--test
2+
3+
#![deny(invalid_codeblock_attribute)]
4+
5+
/// foo
6+
///
7+
/// ```compile-fail,compilefail,comPile_fail
8+
/// boo
9+
/// ```
10+
pub fn foo() {}
11+
12+
/// bar
13+
///
14+
/// ```should-panic,shouldpanic,shOuld_panic
15+
/// boo
16+
/// ```
17+
pub fn bar() {}
18+
19+
/// foobar
20+
///
21+
/// ```no-run,norun,nO_run
22+
/// boo
23+
/// ```
24+
pub fn foobar() {}
25+
26+
/// barfoo
27+
///
28+
/// ```allow-fail,allowfail,allOw_fail
29+
/// boo
30+
/// ```
31+
pub fn barfoo() {}
32+
33+
/// b
34+
///
35+
/// ```test-harness,testharness,tesT_harness
36+
/// boo
37+
/// ```
38+
pub fn b() {}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
error: unknown attribute `compile-fail`. Did you mean `compile_fail`?
2+
--> $DIR/check-attr-test.rs:5:1
3+
|
4+
5 | / /// foo
5+
6 | | ///
6+
7 | | /// ```compile-fail,compilefail,comPile_fail
7+
8 | | /// boo
8+
9 | | /// ```
9+
| |_______^
10+
|
11+
note: the lint level is defined here
12+
--> $DIR/check-attr-test.rs:3:9
13+
|
14+
3 | #![deny(invalid_codeblock_attribute)]
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
= help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully
17+
18+
error: unknown attribute `compilefail`. Did you mean `compile_fail`?
19+
--> $DIR/check-attr-test.rs:5:1
20+
|
21+
5 | / /// foo
22+
6 | | ///
23+
7 | | /// ```compile-fail,compilefail,comPile_fail
24+
8 | | /// boo
25+
9 | | /// ```
26+
| |_______^
27+
|
28+
= help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully
29+
30+
error: unknown attribute `comPile_fail`. Did you mean `compile_fail`?
31+
--> $DIR/check-attr-test.rs:5:1
32+
|
33+
5 | / /// foo
34+
6 | | ///
35+
7 | | /// ```compile-fail,compilefail,comPile_fail
36+
8 | | /// boo
37+
9 | | /// ```
38+
| |_______^
39+
|
40+
= help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully
41+
42+
error: unknown attribute `should-panic`. Did you mean `should_panic`?
43+
--> $DIR/check-attr-test.rs:12:1
44+
|
45+
12 | / /// bar
46+
13 | | ///
47+
14 | | /// ```should-panic,shouldpanic,shOuld_panic
48+
15 | | /// boo
49+
16 | | /// ```
50+
| |_______^
51+
|
52+
= 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
53+
54+
error: unknown attribute `shouldpanic`. Did you mean `should_panic`?
55+
--> $DIR/check-attr-test.rs:12:1
56+
|
57+
12 | / /// bar
58+
13 | | ///
59+
14 | | /// ```should-panic,shouldpanic,shOuld_panic
60+
15 | | /// boo
61+
16 | | /// ```
62+
| |_______^
63+
|
64+
= 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
65+
66+
error: unknown attribute `shOuld_panic`. Did you mean `should_panic`?
67+
--> $DIR/check-attr-test.rs:12:1
68+
|
69+
12 | / /// bar
70+
13 | | ///
71+
14 | | /// ```should-panic,shouldpanic,shOuld_panic
72+
15 | | /// boo
73+
16 | | /// ```
74+
| |_______^
75+
|
76+
= 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
77+
78+
error: unknown attribute `no-run`. Did you mean `no_run`?
79+
--> $DIR/check-attr-test.rs:19:1
80+
|
81+
19 | / /// foobar
82+
20 | | ///
83+
21 | | /// ```no-run,norun,nO_run
84+
22 | | /// boo
85+
23 | | /// ```
86+
| |_______^
87+
|
88+
= 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)
89+
90+
error: unknown attribute `norun`. Did you mean `no_run`?
91+
--> $DIR/check-attr-test.rs:19:1
92+
|
93+
19 | / /// foobar
94+
20 | | ///
95+
21 | | /// ```no-run,norun,nO_run
96+
22 | | /// boo
97+
23 | | /// ```
98+
| |_______^
99+
|
100+
= 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)
101+
102+
error: unknown attribute `nO_run`. Did you mean `no_run`?
103+
--> $DIR/check-attr-test.rs:19:1
104+
|
105+
19 | / /// foobar
106+
20 | | ///
107+
21 | | /// ```no-run,norun,nO_run
108+
22 | | /// boo
109+
23 | | /// ```
110+
| |_______^
111+
|
112+
= 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)
113+
114+
error: unknown attribute `allow-fail`. Did you mean `allow_fail`?
115+
--> $DIR/check-attr-test.rs:26:1
116+
|
117+
26 | / /// barfoo
118+
27 | | ///
119+
28 | | /// ```allow-fail,allowfail,allOw_fail
120+
29 | | /// boo
121+
30 | | /// ```
122+
| |_______^
123+
|
124+
= 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)
125+
126+
error: unknown attribute `allowfail`. Did you mean `allow_fail`?
127+
--> $DIR/check-attr-test.rs:26:1
128+
|
129+
26 | / /// barfoo
130+
27 | | ///
131+
28 | | /// ```allow-fail,allowfail,allOw_fail
132+
29 | | /// boo
133+
30 | | /// ```
134+
| |_______^
135+
|
136+
= 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)
137+
138+
error: unknown attribute `allOw_fail`. Did you mean `allow_fail`?
139+
--> $DIR/check-attr-test.rs:26:1
140+
|
141+
26 | / /// barfoo
142+
27 | | ///
143+
28 | | /// ```allow-fail,allowfail,allOw_fail
144+
29 | | /// boo
145+
30 | | /// ```
146+
| |_______^
147+
|
148+
= 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)
149+
150+
error: unknown attribute `test-harness`. Did you mean `test_harness`?
151+
--> $DIR/check-attr-test.rs:33:1
152+
|
153+
33 | / /// b
154+
34 | | ///
155+
35 | | /// ```test-harness,testharness,tesT_harness
156+
36 | | /// boo
157+
37 | | /// ```
158+
| |_______^
159+
|
160+
= 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
161+
162+
error: unknown attribute `testharness`. Did you mean `test_harness`?
163+
--> $DIR/check-attr-test.rs:33:1
164+
|
165+
33 | / /// b
166+
34 | | ///
167+
35 | | /// ```test-harness,testharness,tesT_harness
168+
36 | | /// boo
169+
37 | | /// ```
170+
| |_______^
171+
|
172+
= 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
173+
174+
error: unknown attribute `tesT_harness`. Did you mean `test_harness`?
175+
--> $DIR/check-attr-test.rs:33:1
176+
|
177+
33 | / /// b
178+
34 | | ///
179+
35 | | /// ```test-harness,testharness,tesT_harness
180+
36 | | /// boo
181+
37 | | /// ```
182+
| |_______^
183+
|
184+
= 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
185+
186+
error: aborting due to 15 previous errors
187+

‎src/test/rustdoc-ui/check-attr.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#![deny(invalid_codeblock_attribute)]
2+
3+
/// foo
4+
//~^ ERROR
5+
//~^^ ERROR
6+
//~^^^ ERROR
7+
///
8+
/// ```compile-fail,compilefail,comPile_fail
9+
/// boo
10+
/// ```
11+
pub fn foo() {}
12+
13+
/// bar
14+
//~^ ERROR
15+
//~^^ ERROR
16+
//~^^^ ERROR
17+
///
18+
/// ```should-panic,shouldpanic,sHould_panic
19+
/// boo
20+
/// ```
21+
pub fn bar() {}
22+
23+
/// foobar
24+
//~^ ERROR
25+
//~^^ ERROR
26+
//~^^^ ERROR
27+
///
28+
/// ```no-run,norun,no_Run
29+
/// boo
30+
/// ```
31+
pub fn foobar() {}
32+
33+
/// barfoo
34+
//~^ ERROR
35+
//~^^ ERROR
36+
//~^^^ ERROR
37+
///
38+
/// ```allow-fail,allowfail,alLow_fail
39+
/// boo
40+
/// ```
41+
pub fn barfoo() {}
42+
43+
/// b
44+
//~^ ERROR
45+
//~^^ ERROR
46+
//~^^^ ERROR
47+
///
48+
/// ```test-harness,testharness,teSt_harness
49+
/// boo
50+
/// ```
51+
pub fn b() {}

‎src/test/rustdoc-ui/check-attr.stderr

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
error: unknown attribute `compile-fail`. Did you mean `compile_fail`?
2+
--> $DIR/check-attr.rs:3:1
3+
|
4+
LL | / /// foo
5+
LL | |
6+
LL | |
7+
LL | |
8+
... |
9+
LL | | /// boo
10+
LL | | /// ```
11+
| |_______^
12+
|
13+
note: the lint level is defined here
14+
--> $DIR/check-attr.rs:1:9
15+
|
16+
LL | #![deny(invalid_codeblock_attribute)]
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
= help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully
19+
20+
error: unknown attribute `compilefail`. Did you mean `compile_fail`?
21+
--> $DIR/check-attr.rs:3:1
22+
|
23+
LL | / /// foo
24+
LL | |
25+
LL | |
26+
LL | |
27+
... |
28+
LL | | /// boo
29+
LL | | /// ```
30+
| |_______^
31+
|
32+
= help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully
33+
34+
error: unknown attribute `comPile_fail`. Did you mean `compile_fail`?
35+
--> $DIR/check-attr.rs:3:1
36+
|
37+
LL | / /// foo
38+
LL | |
39+
LL | |
40+
LL | |
41+
... |
42+
LL | | /// boo
43+
LL | | /// ```
44+
| |_______^
45+
|
46+
= help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully
47+
48+
error: unknown attribute `should-panic`. Did you mean `should_panic`?
49+
--> $DIR/check-attr.rs:13:1
50+
|
51+
LL | / /// bar
52+
LL | |
53+
LL | |
54+
LL | |
55+
... |
56+
LL | | /// boo
57+
LL | | /// ```
58+
| |_______^
59+
|
60+
= 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
61+
62+
error: unknown attribute `shouldpanic`. Did you mean `should_panic`?
63+
--> $DIR/check-attr.rs:13:1
64+
|
65+
LL | / /// bar
66+
LL | |
67+
LL | |
68+
LL | |
69+
... |
70+
LL | | /// boo
71+
LL | | /// ```
72+
| |_______^
73+
|
74+
= 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
75+
76+
error: unknown attribute `sHould_panic`. Did you mean `should_panic`?
77+
--> $DIR/check-attr.rs:13:1
78+
|
79+
LL | / /// bar
80+
LL | |
81+
LL | |
82+
LL | |
83+
... |
84+
LL | | /// boo
85+
LL | | /// ```
86+
| |_______^
87+
|
88+
= 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
89+
90+
error: unknown attribute `no-run`. Did you mean `no_run`?
91+
--> $DIR/check-attr.rs:23:1
92+
|
93+
LL | / /// foobar
94+
LL | |
95+
LL | |
96+
LL | |
97+
... |
98+
LL | | /// boo
99+
LL | | /// ```
100+
| |_______^
101+
|
102+
= 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)
103+
104+
error: unknown attribute `norun`. Did you mean `no_run`?
105+
--> $DIR/check-attr.rs:23:1
106+
|
107+
LL | / /// foobar
108+
LL | |
109+
LL | |
110+
LL | |
111+
... |
112+
LL | | /// boo
113+
LL | | /// ```
114+
| |_______^
115+
|
116+
= 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)
117+
118+
error: unknown attribute `no_Run`. Did you mean `no_run`?
119+
--> $DIR/check-attr.rs:23:1
120+
|
121+
LL | / /// foobar
122+
LL | |
123+
LL | |
124+
LL | |
125+
... |
126+
LL | | /// boo
127+
LL | | /// ```
128+
| |_______^
129+
|
130+
= 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)
131+
132+
error: unknown attribute `allow-fail`. Did you mean `allow_fail`?
133+
--> $DIR/check-attr.rs:33:1
134+
|
135+
LL | / /// barfoo
136+
LL | |
137+
LL | |
138+
LL | |
139+
... |
140+
LL | | /// boo
141+
LL | | /// ```
142+
| |_______^
143+
|
144+
= 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)
145+
146+
error: unknown attribute `allowfail`. Did you mean `allow_fail`?
147+
--> $DIR/check-attr.rs:33:1
148+
|
149+
LL | / /// barfoo
150+
LL | |
151+
LL | |
152+
LL | |
153+
... |
154+
LL | | /// boo
155+
LL | | /// ```
156+
| |_______^
157+
|
158+
= 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)
159+
160+
error: unknown attribute `alLow_fail`. Did you mean `allow_fail`?
161+
--> $DIR/check-attr.rs:33:1
162+
|
163+
LL | / /// barfoo
164+
LL | |
165+
LL | |
166+
LL | |
167+
... |
168+
LL | | /// boo
169+
LL | | /// ```
170+
| |_______^
171+
|
172+
= 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)
173+
174+
error: unknown attribute `test-harness`. Did you mean `test_harness`?
175+
--> $DIR/check-attr.rs:43:1
176+
|
177+
LL | / /// b
178+
LL | |
179+
LL | |
180+
LL | |
181+
... |
182+
LL | | /// boo
183+
LL | | /// ```
184+
| |_______^
185+
|
186+
= 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
187+
188+
error: unknown attribute `testharness`. Did you mean `test_harness`?
189+
--> $DIR/check-attr.rs:43:1
190+
|
191+
LL | / /// b
192+
LL | |
193+
LL | |
194+
LL | |
195+
... |
196+
LL | | /// boo
197+
LL | | /// ```
198+
| |_______^
199+
|
200+
= 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
201+
202+
error: unknown attribute `teSt_harness`. Did you mean `test_harness`?
203+
--> $DIR/check-attr.rs:43:1
204+
|
205+
LL | / /// b
206+
LL | |
207+
LL | |
208+
LL | |
209+
... |
210+
LL | | /// boo
211+
LL | | /// ```
212+
| |_______^
213+
|
214+
= 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
215+
216+
error: aborting due to 15 previous errors
217+

0 commit comments

Comments
 (0)
Please sign in to comment.