diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index a3b61f1f4a589..2f50234b6d582 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -73,7 +73,7 @@ pub struct VecDeque { /// It produces the following sequence of matching slices: /// /// ([0 1], [a b]) -/// ([2], [c]) +/// (\[2\], \[c\]) /// ([3 4], [d e]) /// /// and the uneven remainder of either A or B is skipped. diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index e106b4354e4e9..fb9f5faa018a4 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -1024,7 +1024,7 @@ unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { } impl Rc<[T]> { - /// Copy elements from slice into newly allocated Rc<[T]> + /// Copy elements from slice into newly allocated Rc<\[T\]> /// /// Unsafe because the caller must either take ownership or bind `T: Copy` unsafe fn copy_from_slice(v: &[T]) -> Rc<[T]> { diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 54df2b6085780..cde412bee78d9 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -902,7 +902,7 @@ unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { } impl Arc<[T]> { - /// Copy elements from slice into newly allocated Arc<[T]> + /// Copy elements from slice into newly allocated Arc<\[T\]> /// /// Unsafe because the caller must either take ownership or bind `T: Copy`. unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { diff --git a/src/libcore/alloc/mod.rs b/src/libcore/alloc/mod.rs index 86a6fa7f8ba3c..1346fbd481003 100644 --- a/src/libcore/alloc/mod.rs +++ b/src/libcore/alloc/mod.rs @@ -364,4 +364,51 @@ pub unsafe trait AllocRef { } } } + + /// Creates a "by reference" adaptor for this instance of `AllocRef`. + /// + /// The returned adaptor also implements `AllocRef` and will simply borrow this. + #[inline(always)] + fn by_ref(&mut self) -> &mut Self { + self + } +} + +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl AllocRef for &mut A +where + A: AllocRef + ?Sized, +{ + #[inline] + fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result { + (**self).alloc(layout, init) + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + (**self).dealloc(ptr, layout) + } + + #[inline] + unsafe fn grow( + &mut self, + ptr: NonNull, + layout: Layout, + new_size: usize, + placement: ReallocPlacement, + init: AllocInit, + ) -> Result { + (**self).grow(ptr, layout, new_size, placement, init) + } + + #[inline] + unsafe fn shrink( + &mut self, + ptr: NonNull, + layout: Layout, + new_size: usize, + placement: ReallocPlacement, + ) -> Result { + (**self).shrink(ptr, layout, new_size, placement) + } } diff --git a/src/libcore/ffi.rs b/src/libcore/ffi.rs index 6277da4f123f2..7bc2866dc2e67 100644 --- a/src/libcore/ffi.rs +++ b/src/libcore/ffi.rs @@ -282,7 +282,7 @@ impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { mod sealed_trait { /// Trait which whitelists the allowed types to be used with [VaList::arg] /// - /// [VaList::va_arg]: struct.VaList.html#method.arg + /// [VaList::arg]: ../struct.VaList.html#method.arg #[unstable( feature = "c_variadic", reason = "the `c_variadic` feature has not been properly tested on \ diff --git a/src/libcore/iter/adapters/chain.rs b/src/libcore/iter/adapters/chain.rs index 2dd405ced20e1..6700ef017bde4 100644 --- a/src/libcore/iter/adapters/chain.rs +++ b/src/libcore/iter/adapters/chain.rs @@ -18,6 +18,9 @@ pub struct Chain { // adapter because its specialization for `FusedIterator` unconditionally descends into the // iterator, and that could be expensive to keep revisiting stuff like nested chains. It also // hurts compiler performance to add more iterator layers to `Chain`. + // + // Only the "first" iterator is actually set `None` when exhausted, depending on whether you + // iterate forward or backward. If you mix directions, then both sides may be `None`. a: Option, b: Option, } @@ -43,6 +46,17 @@ macro_rules! fuse { }; } +/// Try an iterator method without fusing, +/// like an inline `.as_mut().and_then(...)` +macro_rules! maybe { + ($self:ident . $iter:ident . $($call:tt)+) => { + match $self.$iter { + Some(ref mut iter) => iter.$($call)+, + None => None, + } + }; +} + #[stable(feature = "rust1", since = "1.0.0")] impl Iterator for Chain where @@ -54,7 +68,7 @@ where #[inline] fn next(&mut self) -> Option { match fuse!(self.a.next()) { - None => fuse!(self.b.next()), + None => maybe!(self.b.next()), item => item, } } @@ -85,7 +99,7 @@ where } if let Some(ref mut b) = self.b { acc = b.try_fold(acc, f)?; - self.b = None; + // we don't fuse the second iterator } Try::from_ok(acc) } @@ -114,7 +128,7 @@ where } self.a = None; } - fuse!(self.b.nth(n)) + maybe!(self.b.nth(n)) } #[inline] @@ -123,7 +137,7 @@ where P: FnMut(&Self::Item) -> bool, { match fuse!(self.a.find(&mut predicate)) { - None => fuse!(self.b.find(predicate)), + None => maybe!(self.b.find(predicate)), item => item, } } @@ -174,7 +188,7 @@ where #[inline] fn next_back(&mut self) -> Option { match fuse!(self.b.next_back()) { - None => fuse!(self.a.next_back()), + None => maybe!(self.a.next_back()), item => item, } } @@ -190,7 +204,7 @@ where } self.b = None; } - fuse!(self.a.nth_back(n)) + maybe!(self.a.nth_back(n)) } #[inline] @@ -199,7 +213,7 @@ where P: FnMut(&Self::Item) -> bool, { match fuse!(self.b.rfind(&mut predicate)) { - None => fuse!(self.a.rfind(predicate)), + None => maybe!(self.a.rfind(predicate)), item => item, } } @@ -216,7 +230,7 @@ where } if let Some(ref mut a) = self.a { acc = a.try_rfold(acc, f)?; - self.a = None; + // we don't fuse the second iterator } Try::from_ok(acc) } @@ -236,8 +250,6 @@ where } // Note: *both* must be fused to handle double-ended iterators. -// Now that we "fuse" both sides, we *could* implement this unconditionally, -// but we should be cautious about committing to that in the public API. #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Chain where diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index 3fa2b7a2d042c..7fcfbf108144c 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -924,7 +924,12 @@ pub fn drop(_x: T) {} #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn transmute_copy(src: &T) -> U { - ptr::read_unaligned(src as *const T as *const U) + // If U has a higher alignment requirement, src may not be suitably aligned. + if align_of::() > align_of::() { + ptr::read_unaligned(src as *const T as *const U) + } else { + ptr::read(src as *const T as *const U) + } } /// Opaque type representing the discriminant of an enum. diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 1a1dbcd7b871a..7da02b11676ab 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -207,50 +207,64 @@ fn test_iterator_chain_find() { assert_eq!(iter.next(), None); } -#[test] -fn test_iterator_chain_size_hint() { - struct Iter { - is_empty: bool, - } +struct Toggle { + is_empty: bool, +} - impl Iterator for Iter { - type Item = (); +impl Iterator for Toggle { + type Item = (); - // alternates between `None` and `Some(())` - fn next(&mut self) -> Option { - if self.is_empty { - self.is_empty = false; - None - } else { - self.is_empty = true; - Some(()) - } + // alternates between `None` and `Some(())` + fn next(&mut self) -> Option { + if self.is_empty { + self.is_empty = false; + None + } else { + self.is_empty = true; + Some(()) } + } - fn size_hint(&self) -> (usize, Option) { - if self.is_empty { (0, Some(0)) } else { (1, Some(1)) } - } + fn size_hint(&self) -> (usize, Option) { + if self.is_empty { (0, Some(0)) } else { (1, Some(1)) } } +} - impl DoubleEndedIterator for Iter { - fn next_back(&mut self) -> Option { - self.next() - } +impl DoubleEndedIterator for Toggle { + fn next_back(&mut self) -> Option { + self.next() } +} +#[test] +fn test_iterator_chain_size_hint() { // this chains an iterator of length 0 with an iterator of length 1, // so after calling `.next()` once, the iterator is empty and the // state is `ChainState::Back`. `.size_hint()` should now disregard // the size hint of the left iterator - let mut iter = Iter { is_empty: true }.chain(once(())); + let mut iter = Toggle { is_empty: true }.chain(once(())); assert_eq!(iter.next(), Some(())); assert_eq!(iter.size_hint(), (0, Some(0))); - let mut iter = once(()).chain(Iter { is_empty: true }); + let mut iter = once(()).chain(Toggle { is_empty: true }); assert_eq!(iter.next_back(), Some(())); assert_eq!(iter.size_hint(), (0, Some(0))); } +#[test] +fn test_iterator_chain_unfused() { + // Chain shouldn't be fused in its second iterator, depending on direction + let mut iter = NonFused::new(empty()).chain(Toggle { is_empty: true }); + iter.next().unwrap_none(); + iter.next().unwrap(); + iter.next().unwrap_none(); + + let mut iter = Toggle { is_empty: true }.chain(NonFused::new(empty())); + iter.next_back().unwrap_none(); + iter.next_back().unwrap(); + iter.next_back().unwrap_none(); +} + #[test] fn test_zip_nth() { let xs = [0, 1, 2, 4, 5]; diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 05f958cbe81fe..e7d36d327cd89 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -42,6 +42,7 @@ #![feature(unwrap_infallible)] #![feature(leading_trailing_ones)] #![feature(const_forget)] +#![feature(option_unwrap_none)] extern crate test; 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_middle/mir/interpret/value.rs b/src/librustc_middle/mir/interpret/value.rs index 25fa3e5e8e0e3..f3c1c87dad484 100644 --- a/src/librustc_middle/mir/interpret/value.rs +++ b/src/librustc_middle/mir/interpret/value.rs @@ -188,6 +188,11 @@ impl<'tcx, Tag> Scalar { } } + #[inline] + pub fn null_ptr(cx: &impl HasDataLayout) -> Self { + Scalar::Raw { data: 0, size: cx.data_layout().pointer_size.bytes() as u8 } + } + #[inline] pub fn zst() -> Self { Scalar::Raw { data: 0, size: 0 } diff --git a/src/librustc_parse/lexer/tokentrees.rs b/src/librustc_parse/lexer/tokentrees.rs index b65b894172844..c08659ec9f648 100644 --- a/src/librustc_parse/lexer/tokentrees.rs +++ b/src/librustc_parse/lexer/tokentrees.rs @@ -1,6 +1,6 @@ use super::{StringReader, UnmatchedBrace}; -use rustc_ast::token::{self, Token}; +use rustc_ast::token::{self, DelimToken, Token}; use rustc_ast::tokenstream::{ DelimSpan, IsJoint::{self, *}, @@ -22,6 +22,7 @@ impl<'a> StringReader<'a> { matching_delim_spans: Vec::new(), last_unclosed_found_span: None, last_delim_empty_block_spans: FxHashMap::default(), + matching_block_spans: Vec::new(), }; let res = tt_reader.parse_all_token_trees(); (res, tt_reader.unmatched_braces) @@ -42,6 +43,9 @@ struct TokenTreesReader<'a> { last_unclosed_found_span: Option, /// Collect empty block spans that might have been auto-inserted by editors. last_delim_empty_block_spans: FxHashMap, + /// Collect the spans of braces (Open, Close). Used only + /// for detecting if blocks are empty and only braces. + matching_block_spans: Vec<(Span, Span)>, } impl<'a> TokenTreesReader<'a> { @@ -77,6 +81,7 @@ impl<'a> TokenTreesReader<'a> { fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> { let sm = self.string_reader.sess.source_map(); + match self.token.kind { token::Eof => { let msg = "this file contains an unclosed delimiter"; @@ -146,6 +151,14 @@ impl<'a> TokenTreesReader<'a> { } } + match (open_brace, delim) { + //only add braces + (DelimToken::Brace, DelimToken::Brace) => { + self.matching_block_spans.push((open_brace_span, close_brace_span)); + } + _ => {} + } + if self.open_braces.is_empty() { // Clear up these spans to avoid suggesting them as we've found // properly matched delimiters so far for an entire block. @@ -164,6 +177,7 @@ impl<'a> TokenTreesReader<'a> { token::CloseDelim(other) => { let mut unclosed_delimiter = None; let mut candidate = None; + if self.last_unclosed_found_span != Some(self.token.span) { // do not complain about the same unclosed delimiter multiple times self.last_unclosed_found_span = Some(self.token.span); @@ -224,12 +238,27 @@ impl<'a> TokenTreesReader<'a> { let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg); - if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) { - err.span_label( - span, - "this block is empty, you might have not meant to close it", - ); + // Braces are added at the end, so the last element is the biggest block + if let Some(parent) = self.matching_block_spans.last() { + if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) { + // Check if the (empty block) is in the last properly closed block + if (parent.0.to(parent.1)).contains(span) { + err.span_label( + span, + "block is empty, you might have not meant to close it", + ); + } else { + err.span_label(parent.0, "this opening brace..."); + + err.span_label(parent.1, "...matches this closing brace"); + } + } else { + err.span_label(parent.0, "this opening brace..."); + + err.span_label(parent.1, "...matches this closing brace"); + } } + err.span_label(self.token.span, "unexpected closing delimiter"); Err(err) } 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 + diff --git a/src/test/ui/parser/issue-70583-block-is-empty-1.rs b/src/test/ui/parser/issue-70583-block-is-empty-1.rs new file mode 100644 index 0000000000000..f560f68f61368 --- /dev/null +++ b/src/test/ui/parser/issue-70583-block-is-empty-1.rs @@ -0,0 +1,20 @@ +pub enum ErrorHandled { + Reported, + TooGeneric, +} + +impl ErrorHandled { + pub fn assert_reported(self) { + match self { + ErrorHandled::Reported => {} + ErrorHandled::TooGeneric => panic!(), + } + } +} + +fn struct_generic(x: Vec) { + for v in x { + println!("{}", v); + } + } +} //~ ERROR unexpected closing delimiter: `}` diff --git a/src/test/ui/parser/issue-70583-block-is-empty-1.stderr b/src/test/ui/parser/issue-70583-block-is-empty-1.stderr new file mode 100644 index 0000000000000..39bf113ef83de --- /dev/null +++ b/src/test/ui/parser/issue-70583-block-is-empty-1.stderr @@ -0,0 +1,13 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-70583-block-is-empty-1.rs:20:1 + | +LL | fn struct_generic(x: Vec) { + | - this opening brace... +... +LL | } + | - ...matches this closing brace +LL | } + | ^ unexpected closing delimiter + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-70583-block-is-empty-2.rs b/src/test/ui/parser/issue-70583-block-is-empty-2.rs new file mode 100644 index 0000000000000..80f53338a689e --- /dev/null +++ b/src/test/ui/parser/issue-70583-block-is-empty-2.rs @@ -0,0 +1,14 @@ +pub enum ErrorHandled { + Reported, + TooGeneric, +} + +impl ErrorHandled { + pub fn assert_reported(self) { + match self { + ErrorHandled::Reported => {}} + //^~ ERROR block is empty, you might have not meant to close it + ErrorHandled::TooGeneric => panic!(), + } + } +} //~ ERROR unexpected closing delimiter: `}` diff --git a/src/test/ui/parser/issue-70583-block-is-empty-2.stderr b/src/test/ui/parser/issue-70583-block-is-empty-2.stderr new file mode 100644 index 0000000000000..5d37b216427f6 --- /dev/null +++ b/src/test/ui/parser/issue-70583-block-is-empty-2.stderr @@ -0,0 +1,11 @@ +error: unexpected closing delimiter: `}` + --> $DIR/issue-70583-block-is-empty-2.rs:14:1 + | +LL | ErrorHandled::Reported => {}} + | -- block is empty, you might have not meant to close it +... +LL | } + | ^ unexpected closing delimiter + +error: aborting due to previous error + diff --git a/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr b/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr index 042142ac35033..424c7a60c196f 100644 --- a/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr +++ b/src/test/ui/parser/macro-mismatched-delim-paren-brace.stderr @@ -1,6 +1,11 @@ error: unexpected closing delimiter: `}` --> $DIR/macro-mismatched-delim-paren-brace.rs:5:1 | +LL | fn main() { + | - this opening brace... +... +LL | } + | - ...matches this closing brace LL | } | ^ unexpected closing delimiter diff --git a/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr b/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr index f1be5dc5ba7fa..c871e549c9ec3 100644 --- a/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr +++ b/src/test/ui/parser/mismatched-delim-brace-empty-block.stderr @@ -1,6 +1,12 @@ error: unexpected closing delimiter: `}` --> $DIR/mismatched-delim-brace-empty-block.rs:5:1 | +LL | fn main() { + | - this opening brace... +LL | +LL | } + | - ...matches this closing brace +LL | let _ = (); LL | } | ^ unexpected closing delimiter