diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index ea87268877fb9..24597c3aca312 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1243,6 +1243,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
def_id.to_def_id(),
span_of_fragments(&attrs.doc_strings).unwrap_or(sp),
)),
+ self.tcx.features().custom_code_classes_in_docs,
);
}
diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs
index f0ebb8e5a3905..b34b69b1f1510 100644
--- a/src/librustdoc/externalfiles.rs
+++ b/src/librustdoc/externalfiles.rs
@@ -46,6 +46,8 @@ impl ExternalHtml {
edition,
playground,
heading_offset: HeadingOffset::H2,
+ // For external files, it'll be disabled until the feature is enabled by default.
+ custom_code_classes_in_docs: false,
}
.into_string()
);
@@ -61,6 +63,8 @@ impl ExternalHtml {
edition,
playground,
heading_offset: HeadingOffset::H2,
+ // For external files, it'll be disabled until the feature is enabled by default.
+ custom_code_classes_in_docs: false,
}
.into_string()
);
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 177fb1a9426f9..59958fbaef918 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -20,6 +20,7 @@
//! edition: Edition::Edition2015,
//! playground: &None,
//! heading_offset: HeadingOffset::H2,
+//! custom_code_classes_in_docs: true,
//! };
//! let html = md.into_string();
//! // ... something using html
@@ -95,6 +96,8 @@ pub struct Markdown<'a> {
/// Offset at which we render headings.
/// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `
`.
pub heading_offset: HeadingOffset,
+ /// `true` if the `custom_code_classes_in_docs` feature is enabled.
+ pub custom_code_classes_in_docs: bool,
}
/// A struct like `Markdown` that renders the markdown with a table of contents.
pub(crate) struct MarkdownWithToc<'a> {
@@ -103,6 +106,8 @@ pub(crate) struct MarkdownWithToc<'a> {
pub(crate) error_codes: ErrorCodes,
pub(crate) edition: Edition,
pub(crate) playground: &'a Option,
+ /// `true` if the `custom_code_classes_in_docs` feature is enabled.
+ pub(crate) custom_code_classes_in_docs: bool,
}
/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags
/// and includes no paragraph tags.
@@ -203,6 +208,7 @@ struct CodeBlocks<'p, 'a, I: Iterator- >> {
// Information about the playground if a URL has been specified, containing an
// optional crate name and the URL.
playground: &'p Option,
+ custom_code_classes_in_docs: bool,
}
impl<'p, 'a, I: Iterator
- >> CodeBlocks<'p, 'a, I> {
@@ -211,8 +217,15 @@ impl<'p, 'a, I: Iterator
- >> CodeBlocks<'p, 'a, I> {
error_codes: ErrorCodes,
edition: Edition,
playground: &'p Option,
+ custom_code_classes_in_docs: bool,
) -> Self {
- CodeBlocks { inner: iter, check_error_codes: error_codes, edition, playground }
+ CodeBlocks {
+ inner: iter,
+ check_error_codes: error_codes,
+ edition,
+ playground,
+ custom_code_classes_in_docs,
+ }
}
}
@@ -242,8 +255,12 @@ impl<'a, I: Iterator
- >> Iterator for CodeBlocks<'_, 'a, I> {
let parse_result = match kind {
CodeBlockKind::Fenced(ref lang) => {
- let parse_result =
- LangString::parse_without_check(lang, self.check_error_codes, false);
+ let parse_result = LangString::parse_without_check(
+ lang,
+ self.check_error_codes,
+ false,
+ self.custom_code_classes_in_docs,
+ );
if !parse_result.rust {
let added_classes = parse_result.added_classes;
let lang_string = if let Some(lang) = parse_result.unknown.first() {
@@ -725,8 +742,17 @@ pub(crate) fn find_testable_code(
error_codes: ErrorCodes,
enable_per_target_ignores: bool,
extra_info: Option<&ExtraInfo<'_>>,
+ custom_code_classes_in_docs: bool,
) {
- find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false)
+ find_codes(
+ doc,
+ tests,
+ error_codes,
+ enable_per_target_ignores,
+ extra_info,
+ false,
+ custom_code_classes_in_docs,
+ )
}
pub(crate) fn find_codes(
@@ -736,6 +762,7 @@ pub(crate) fn find_codes(
enable_per_target_ignores: bool,
extra_info: Option<&ExtraInfo<'_>>,
include_non_rust: bool,
+ custom_code_classes_in_docs: bool,
) {
let mut parser = Parser::new(doc).into_offset_iter();
let mut prev_offset = 0;
@@ -754,6 +781,7 @@ pub(crate) fn find_codes(
error_codes,
enable_per_target_ignores,
extra_info,
+ custom_code_classes_in_docs,
)
}
}
@@ -1153,8 +1181,15 @@ impl LangString {
string: &str,
allow_error_code_check: ErrorCodes,
enable_per_target_ignores: bool,
+ custom_code_classes_in_docs: bool,
) -> Self {
- Self::parse(string, allow_error_code_check, enable_per_target_ignores, None)
+ Self::parse(
+ string,
+ allow_error_code_check,
+ enable_per_target_ignores,
+ None,
+ custom_code_classes_in_docs,
+ )
}
fn parse(
@@ -1162,6 +1197,7 @@ impl LangString {
allow_error_code_check: ErrorCodes,
enable_per_target_ignores: bool,
extra: Option<&ExtraInfo<'_>>,
+ custom_code_classes_in_docs: bool,
) -> Self {
let allow_error_code_check = allow_error_code_check.as_bool();
let mut seen_rust_tags = false;
@@ -1197,7 +1233,11 @@ impl LangString {
seen_rust_tags = true;
}
LangStringToken::LangToken("custom") => {
- seen_custom_tag = true;
+ if custom_code_classes_in_docs {
+ seen_custom_tag = true;
+ } else {
+ seen_other_tags = true;
+ }
}
LangStringToken::LangToken("test_harness") => {
data.test_harness = true;
@@ -1268,11 +1308,16 @@ impl LangString {
data.unknown.push(x.to_owned());
}
LangStringToken::KeyValueAttribute(key, value) => {
- if key == "class" {
- data.added_classes.push(value.to_owned());
- } else if let Some(extra) = extra {
- extra
- .error_invalid_codeblock_attr(format!("unsupported attribute `{key}`"));
+ if custom_code_classes_in_docs {
+ if key == "class" {
+ data.added_classes.push(value.to_owned());
+ } else if let Some(extra) = extra {
+ extra.error_invalid_codeblock_attr(format!(
+ "unsupported attribute `{key}`"
+ ));
+ }
+ } else {
+ seen_other_tags = true;
}
}
LangStringToken::ClassAttribute(class) => {
@@ -1302,6 +1347,7 @@ impl Markdown<'_> {
edition,
playground,
heading_offset,
+ custom_code_classes_in_docs,
} = self;
// This is actually common enough to special-case
@@ -1324,7 +1370,7 @@ impl Markdown<'_> {
let p = Footnotes::new(p);
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
let p = TableWrapper::new(p);
- let p = CodeBlocks::new(p, codes, edition, playground);
+ let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs);
html::push_html(&mut s, p);
s
@@ -1333,7 +1379,14 @@ impl Markdown<'_> {
impl MarkdownWithToc<'_> {
pub(crate) fn into_string(self) -> String {
- let MarkdownWithToc { content: md, ids, error_codes: codes, edition, playground } = self;
+ let MarkdownWithToc {
+ content: md,
+ ids,
+ error_codes: codes,
+ edition,
+ playground,
+ custom_code_classes_in_docs,
+ } = self;
let p = Parser::new_ext(md, main_body_opts()).into_offset_iter();
@@ -1345,7 +1398,7 @@ impl MarkdownWithToc<'_> {
let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1);
let p = Footnotes::new(p);
let p = TableWrapper::new(p.map(|(ev, _)| ev));
- let p = CodeBlocks::new(p, codes, edition, playground);
+ let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs);
html::push_html(&mut s, p);
}
@@ -1786,7 +1839,11 @@ pub(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).
-pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec {
+pub(crate) fn rust_code_blocks(
+ md: &str,
+ extra_info: &ExtraInfo<'_>,
+ custom_code_classes_in_docs: bool,
+) -> Vec {
let mut code_blocks = vec![];
if md.is_empty() {
@@ -1803,7 +1860,13 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec::new();
- find_testable_code(input, &mut lines, ErrorCodes::No, false, None);
+ find_testable_code(input, &mut lines, ErrorCodes::No, false, None, true);
assert_eq!(lines, expect);
}
@@ -458,6 +460,7 @@ fn test_ascii_with_prepending_hashtag() {
edition: DEFAULT_EDITION,
playground: &None,
heading_offset: HeadingOffset::H2,
+ custom_code_classes_in_docs: true,
}
.into_string();
assert_eq!(output, expect, "original: {}", input);
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 5adbecd6d0411..f70f59d3be389 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -403,7 +403,8 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
error_codes: shared.codes,
edition: shared.edition(),
playground: &shared.playground,
- heading_offset: HeadingOffset::H1
+ heading_offset: HeadingOffset::H1,
+ custom_code_classes_in_docs: false,
}
.into_string()
)
@@ -437,6 +438,7 @@ fn render_markdown<'a, 'cx: 'a>(
heading_offset: HeadingOffset,
) -> impl fmt::Display + 'a + Captures<'cx> {
display_fn(move |f| {
+ let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs;
write!(
f,
"
{}
",
@@ -448,6 +450,7 @@ fn render_markdown<'a, 'cx: 'a>(
edition: cx.shared.edition(),
playground: &cx.shared.playground,
heading_offset,
+ custom_code_classes_in_docs,
}
.into_string()
)
@@ -1778,6 +1781,7 @@ fn render_impl(
",
);
}
+ let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs;
write!(
w,
"{}
",
@@ -1788,7 +1792,8 @@ fn render_impl(
error_codes: cx.shared.codes,
edition: cx.shared.edition(),
playground: &cx.shared.playground,
- heading_offset: HeadingOffset::H4
+ heading_offset: HeadingOffset::H4,
+ custom_code_classes_in_docs,
}
.into_string()
);
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 526eea30478ab..b74a9392f270e 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -80,6 +80,8 @@ pub(crate) fn render>(
error_codes,
edition,
playground: &playground,
+ // For markdown files, it'll be disabled until the feature is enabled by default.
+ custom_code_classes_in_docs: false,
}
.into_string()
} else {
@@ -91,6 +93,8 @@ pub(crate) fn render>(
edition,
playground: &playground,
heading_offset: HeadingOffset::H1,
+ // For markdown files, it'll be disabled until the feature is enabled by default.
+ custom_code_classes_in_docs: false,
}
.into_string()
};
@@ -154,7 +158,15 @@ pub(crate) fn test(options: Options) -> Result<(), String> {
collector.set_position(DUMMY_SP);
let codes = ErrorCodes::from(options.unstable_features.is_nightly_build());
- find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None);
+ // For markdown files, custom code classes will be disabled until the feature is enabled by default.
+ find_testable_code(
+ &input_str,
+ &mut collector,
+ codes,
+ options.enable_per_target_ignores,
+ None,
+ false,
+ );
crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests);
Ok(())
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 592dd0a145cf5..60def40588aaa 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -208,7 +208,14 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
let has_docs = !i.attrs.doc_strings.is_empty();
let mut tests = Tests { found_tests: 0 };
- find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, false, None);
+ find_testable_code(
+ &i.doc_value(),
+ &mut tests,
+ ErrorCodes::No,
+ false,
+ None,
+ self.ctx.tcx.features().custom_code_classes_in_docs,
+ );
let has_doc_example = tests.found_tests != 0;
let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap();
diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs
index 73e80372e4a41..1a703a4e96755 100644
--- a/src/librustdoc/passes/check_custom_code_classes.rs
+++ b/src/librustdoc/passes/check_custom_code_classes.rs
@@ -9,7 +9,9 @@ use crate::core::DocContext;
use crate::fold::DocFolder;
use crate::html::markdown::{find_codes, ErrorCodes, LangString};
-use rustc_session::parse::feature_err;
+use rustc_errors::StashKey;
+use rustc_feature::GateIssue;
+use rustc_session::parse::add_feature_diagnostics_for_issue;
use rustc_span::symbol::sym;
pub(crate) const CHECK_CUSTOM_CODE_CLASSES: Pass = Pass {
@@ -55,23 +57,32 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item)
let mut tests = TestsWithCustomClasses { custom_classes_found: vec![] };
let dox = item.attrs.doc_value();
- find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true);
+ find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true, true);
if !tests.custom_classes_found.is_empty() && !cx.tcx.features().custom_code_classes_in_docs {
- feature_err(
- &cx.tcx.sess.parse_sess,
+ let span = item.attr_span(cx.tcx);
+ let sess = &cx.tcx.sess.parse_sess;
+ let mut err = sess
+ .span_diagnostic
+ .struct_span_warn(span, "custom classes in code blocks will change behaviour");
+ add_feature_diagnostics_for_issue(
+ &mut err,
+ sess,
sym::custom_code_classes_in_docs,
- item.attr_span(cx.tcx),
- "custom classes in code blocks are unstable",
- )
- .note(
+ GateIssue::Language,
+ false,
+ );
+
+ err.note(
// This will list the wrong items to make them more easily searchable.
// To ensure the most correct hits, it adds back the 'class:' that was stripped.
format!(
"found these custom classes: class={}",
tests.custom_classes_found.join(",class=")
),
- )
- .emit();
+ );
+
+ // A later feature_err call can steal and cancel this warning.
+ err.stash(span, StashKey::EarlySyntaxWarning);
}
}
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index 96224d7c6e2e2..d1c4cc1f5952d 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -113,7 +113,14 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item
let mut tests = Tests { found_tests: 0 };
- find_testable_code(dox, &mut tests, ErrorCodes::No, false, None);
+ find_testable_code(
+ dox,
+ &mut tests,
+ ErrorCodes::No,
+ false,
+ None,
+ cx.tcx.features().custom_code_classes_in_docs,
+ );
if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples {
if should_have_doc_example(cx, item) {
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index 316b1a41c7da2..ac8a75a4f18c9 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -20,7 +20,9 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) {
if let Some(dox) = &item.opt_doc_value() {
let sp = item.attr_span(cx.tcx);
let extra = crate::html::markdown::ExtraInfo::new(cx.tcx, item.item_id.expect_def_id(), sp);
- for code_block in markdown::rust_code_blocks(dox, &extra) {
+ for code_block in
+ markdown::rust_code_blocks(dox, &extra, cx.tcx.features().custom_code_classes_in_docs)
+ {
check_rust_syntax(cx, item, dox, code_block);
}
}
diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
index 8aa13b2d5d10c..3f0f8b5b9f997 100644
--- a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
+++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
@@ -1,5 +1,10 @@
+// check-pass
+
/// ```{class=language-c}
/// int main(void) { return 0; }
/// ```
-//~^^^ ERROR 1:1: 3:8: custom classes in code blocks are unstable [E0658]
+//~^^^ WARNING custom classes in code blocks will change behaviour
+//~| NOTE found these custom classes: class=language-c
+//~| NOTE see issue #79483
+//~| HELP add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable
pub struct Bar;
diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr
index c41ebfc807334..1a2360d9b3004 100644
--- a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr
+++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr
@@ -1,5 +1,5 @@
-error[E0658]: custom classes in code blocks are unstable
- --> $DIR/feature-gate-custom_code_classes_in_docs.rs:1:1
+warning: custom classes in code blocks will change behaviour
+ --> $DIR/feature-gate-custom_code_classes_in_docs.rs:3:1
|
LL | / /// ```{class=language-c}
LL | | /// int main(void) { return 0; }
@@ -10,6 +10,5 @@ LL | | /// ```
= help: add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable
= note: found these custom classes: class=language-c
-error: aborting due to previous error
+warning: 1 warning emitted
-For more information about this error, try `rustc --explain E0658`.