diff options
| author | bors <bors@rust-lang.org> | 2023-09-12 05:38:25 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-09-12 05:38:25 +0000 |
| commit | ea488f786442aeed98ad8158d50a7017c415dec4 (patch) | |
| tree | 74180c12ca0b1d828dfdde86adfe37739c39eb51 /src/tools | |
| parent | 3bf9c5a00a29e6294fbc603cbdcb351175d9d5bf (diff) | |
| parent | 1916a4a5244b8f788170f0ec0444b9e0f6772b54 (diff) | |
| download | rust-ea488f786442aeed98ad8158d50a7017c415dec4.tar.gz rust-ea488f786442aeed98ad8158d50a7017c415dec4.zip | |
Auto merge of #3059 - rust-lang:rustup-2023-09-12, r=RalfJung
Automatic sync from rustc
Diffstat (limited to 'src/tools')
37 files changed, 386 insertions, 247 deletions
diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 96fe1c9e1aecd8f57063e3753969bb6418fd2fd +Subproject 2fc85d15a542bfb610aff7682073412cf635352 diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index c0a9f466e7b..dcd9a4adcbd 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -15,7 +15,6 @@ clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } if_chain = "1.0" itertools = "0.10.1" -pulldown-cmark = { version = "0.9", default-features = false } quine-mc_cluskey = "0.2" regex-syntax = "0.7" serde = { version = "1.0", features = ["derive"] } diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index e29ab634c97..d8f2fbcea00 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -1,18 +1,16 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::source::{first_line_of_span, snippet_with_applicability}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty}; use if_chain::if_chain; -use itertools::Itertools; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; -use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind}; -use rustc_ast::token::CommentKind; +use rustc_ast::ast::{Async, Attribute, Fn, FnRetTy, ItemKind}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; @@ -26,6 +24,9 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_parse::maybe_new_parser_from_source_str; use rustc_parse::parser::ForceCollect; +use rustc_resolve::rustdoc::{ + add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, DocFragment, +}; use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::edition::Edition; @@ -450,53 +451,16 @@ fn lint_for_missing_headers( } } -/// Cleanup documentation decoration. -/// -/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or -/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we -/// need to keep track of -/// the spans but this function is inspired from the later. -#[expect(clippy::cast_possible_truncation)] -#[must_use] -pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) { - // one-line comments lose their prefix - if comment_kind == CommentKind::Line { - let mut doc = doc.to_owned(); - doc.push('\n'); - let len = doc.len(); - // +3 skips the opening delimiter - return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]); - } +#[derive(Copy, Clone)] +struct Fragments<'a> { + doc: &'a str, + fragments: &'a [DocFragment], +} - let mut sizes = vec![]; - let mut contains_initial_stars = false; - for line in doc.lines() { - let offset = line.as_ptr() as usize - doc.as_ptr() as usize; - debug_assert_eq!(offset as u32 as usize, offset); - contains_initial_stars |= line.trim_start().starts_with('*'); - // +1 adds the newline, +3 skips the opening delimiter - sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32)))); - } - if !contains_initial_stars { - return (doc.to_string(), sizes); - } - // remove the initial '*'s if any - let mut no_stars = String::with_capacity(doc.len()); - for line in doc.lines() { - let mut chars = line.chars(); - for c in &mut chars { - if c.is_whitespace() { - no_stars.push(c); - } else { - no_stars.push(if c == '*' { ' ' } else { c }); - break; - } - } - no_stars.push_str(chars.as_str()); - no_stars.push('\n'); +impl Fragments<'_> { + fn span(self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> { + source_span_for_markdown_range(cx.tcx, &self.doc, &range, &self.fragments) } - - (no_stars, sizes) } #[derive(Copy, Clone, Default)] @@ -515,27 +479,12 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ Some(("fake".into(), "fake".into())) } + let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let mut doc = String::new(); - let mut spans = vec![]; - - for attr in attrs { - if let AttrKind::DocComment(comment_kind, comment) = attr.kind { - let (comment, current_spans) = strip_doc_comment_decoration(comment.as_str(), comment_kind, attr.span); - spans.extend_from_slice(¤t_spans); - doc.push_str(&comment); - } else if attr.has_name(sym::doc) { - // ignore mix of sugared and non-sugared doc - // don't trigger the safety or errors check - return None; - } - } - - let mut current = 0; - for &mut (ref mut offset, _) in &mut spans { - let offset_copy = *offset; - *offset = current; - current += offset_copy; + for fragment in &fragments { + add_doc_fragment(&mut doc, fragment); } + doc.pop(); if doc.is_empty() { return Some(DocHeaders::default()); @@ -543,23 +492,19 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ let mut cb = fake_broken_link_callback; - let parser = - pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter(); - // Iterate over all `Events` and combine consecutive events into one - let events = parser.coalesce(|previous, current| { - let previous_range = previous.1; - let current_range = current.1; - - match (previous.0, current.0) { - (Text(previous), Text(current)) => { - let mut previous = previous.to_string(); - previous.push_str(¤t); - Ok((Text(previous.into()), previous_range)) - }, - (previous, current) => Err(((previous, previous_range), (current, current_range))), - } - }); - Some(check_doc(cx, valid_idents, events, &spans)) + // disable smart punctuation to pick up ['link'] more easily + let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION; + let parser = pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut cb)); + + Some(check_doc( + cx, + valid_idents, + parser.into_offset_iter(), + Fragments { + fragments: &fragments, + doc: &doc, + }, + )) } const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; @@ -568,7 +513,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, events: Events, - spans: &[(usize, Span)], + fragments: Fragments<'_>, ) -> DocHeaders { // true if a safety header was found let mut headers = DocHeaders::default(); @@ -579,8 +524,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize let mut no_test = false; let mut edition = None; let mut ticks_unbalanced = false; - let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new(); - let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1; + let mut text_to_check: Vec<(CowStr<'_>, Range<usize>)> = Vec::new(); + let mut paragraph_range = 0..0; for (event, range) in events { match event { Start(CodeBlock(ref kind)) => { @@ -613,25 +558,28 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize in_heading = true; } ticks_unbalanced = false; - let (_, span) = get_current_span(spans, range.start); - paragraph_span = first_line_of_span(cx, span); + paragraph_range = range; }, End(Heading(_, _, _) | Paragraph | Item) => { if let End(Heading(_, _, _)) = event { in_heading = false; } - if ticks_unbalanced { + if ticks_unbalanced + && let Some(span) = fragments.span(cx, paragraph_range.clone()) + { span_lint_and_help( cx, DOC_MARKDOWN, - paragraph_span, + span, "backticks are unbalanced", None, "a backtick may be missing a pair", ); } else { - for (text, span) in text_to_check { - check_text(cx, valid_idents, &text, span); + for (text, range) in text_to_check { + if let Some(span) = fragments.span(cx, range) { + check_text(cx, valid_idents, &text, span); + } } } text_to_check = Vec::new(); @@ -640,8 +588,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize Html(_html) => (), // HTML is weird, just ignore it SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), FootnoteReference(text) | Text(text) => { - let (begin, span) = get_current_span(spans, range.start); - paragraph_span = paragraph_span.with_hi(span.hi()); + paragraph_range.end = range.end; ticks_unbalanced |= text.contains('`') && !in_code; if Some(&text) == in_link.as_ref() || ticks_unbalanced { // Probably a link of the form `<http://example.com>` @@ -658,19 +605,19 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize if in_code { if is_rust && !no_test { let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition()); - check_code(cx, &text, edition, span); + check_code(cx, &text, edition, range.clone(), fragments); } } else { - check_link_quotes(cx, in_link.is_some(), trimmed_text, span, &range, begin, text.len()); - // Adjust for the beginning of the current `Event` - let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin)); + if in_link.is_some() { + check_link_quotes(cx, trimmed_text, range.clone(), fragments); + } if let Some(link) = in_link.as_ref() && let Ok(url) = Url::parse(link) && (url.scheme() == "https" || url.scheme() == "http") { // Don't check the text associated with external URLs continue; } - text_to_check.push((text, span)); + text_to_check.push((text, range)); } }, } @@ -678,36 +625,21 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize headers } -fn check_link_quotes( - cx: &LateContext<'_>, - in_link: bool, - trimmed_text: &str, - span: Span, - range: &Range<usize>, - begin: usize, - text_len: usize, -) { - if in_link && trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'') { - // fix the span to only point at the text within the link - let lo = span.lo() + BytePos::from_usize(range.start - begin); +fn check_link_quotes(cx: &LateContext<'_>, trimmed_text: &str, range: Range<usize>, fragments: Fragments<'_>) { + if trimmed_text.starts_with('\'') + && trimmed_text.ends_with('\'') + && let Some(span) = fragments.span(cx, range) + { span_lint( cx, DOC_LINK_WITH_QUOTES, - span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)), + span, "possible intra-doc link using quotes instead of backticks", ); } } -fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) { - let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) { - Ok(o) => o, - Err(e) => e - 1, - }; - spans[index] -} - -fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range<usize>, fragments: Fragments<'_>) { fn has_needless_main(code: String, edition: Edition) -> bool { rustc_driver::catch_fatal_errors(|| { rustc_span::create_session_globals_then(edition, || { @@ -774,12 +706,13 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { .unwrap_or_default() } + let trailing_whitespace = text.len() - text.trim_end().len(); + // Because of the global session, we need to create a new session in a different thread with // the edition we need. let text = text.to_owned(); - if thread::spawn(move || has_needless_main(text, edition)) - .join() - .expect("thread::spawn failed") + if thread::spawn(move || has_needless_main(text, edition)).join().expect("thread::spawn failed") + && let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index ab71bfbdc58..20fdf26dc52 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -21,6 +21,7 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) +extern crate pulldown_cmark; extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; @@ -37,6 +38,7 @@ extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; extern crate rustc_parse; +extern crate rustc_resolve; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index a1ea3a495eb..da8d8ed4c0f 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -507,20 +507,18 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - return unsafe_line.sf.lines(|lines| { - if comment_start_line.line >= unsafe_line.line { - HasSafetyComment::No - } else { - match text_has_safety_comment( - src, - &lines[comment_start_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + return if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } - }); + }; } } HasSafetyComment::Maybe @@ -551,20 +549,18 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - return unsafe_line.sf.lines(|lines| { - if comment_start_line.line >= unsafe_line.line { - HasSafetyComment::No - } else { - match text_has_safety_comment( - src, - &lines[comment_start_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + return if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } - }); + }; } } HasSafetyComment::Maybe @@ -614,20 +610,18 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - unsafe_line.sf.lines(|lines| { - if macro_line.line < unsafe_line.line { - match text_has_safety_comment( - src, - &lines[macro_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } - } else { - HasSafetyComment::No + if macro_line.line < unsafe_line.line { + match text_has_safety_comment( + src, + &unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } - }) + } else { + HasSafetyComment::No + } } else { // Problem getting source text. Pretend a comment was found. HasSafetyComment::Maybe @@ -671,13 +665,11 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // Get the text from the start of function body to the unsafe block. // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } // ^-------------^ - unsafe_line.sf.lines(|lines| { - body_line.line < unsafe_line.line && text_has_safety_comment( - src, - &lines[body_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ).is_some() - }) + body_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ).is_some() } else { // Problem getting source text. Pretend a comment was found. true diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 03416d35ba4..600cd084c19 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -118,7 +118,7 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo fn line_span<T: LintContext>(cx: &T, span: Span) -> Span { let span = original_sp(span, DUMMY_SP); let SourceFileAndLine { sf, line } = cx.sess().source_map().lookup_line(span.lo()).unwrap(); - let line_start = sf.lines(|lines| lines[line]); + let line_start = sf.lines()[line]; let line_start = sf.absolute_position(line_start); span.with_lo(line_start) } diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index e329a94ff6a..c1c3d4a868f 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -130,7 +130,9 @@ fn base_config(test_dir: &str) -> (Config, Args) { }; let mut config = Config { - mode: Mode::Yolo { rustfix: RustfixMode::Everything }, + mode: Mode::Yolo { + rustfix: RustfixMode::Everything, + }, stderr_filters: vec![(Match::PathBackslash, b"/")], stdout_filters: vec![], output_conflict_handling: if bless { diff --git a/src/tools/clippy/tests/ui/allow_attributes.fixed b/src/tools/clippy/tests/ui/allow_attributes.fixed index 945ba83611c..b506a9890f5 100644 --- a/src/tools/clippy/tests/ui/allow_attributes.fixed +++ b/src/tools/clippy/tests/ui/allow_attributes.fixed @@ -22,6 +22,13 @@ struct T4; #[cfg_attr(panic = "unwind", expect(dead_code))] struct CfgT; +#[allow(clippy::allow_attributes, unused)] +struct Allowed; + +#[expect(clippy::allow_attributes)] +#[allow(unused)] +struct Expected; + fn ignore_external() { external! { #[allow(clippy::needless_borrow)] // Should not lint diff --git a/src/tools/clippy/tests/ui/allow_attributes.rs b/src/tools/clippy/tests/ui/allow_attributes.rs index 8afa61c7002..c7daa7abd9d 100644 --- a/src/tools/clippy/tests/ui/allow_attributes.rs +++ b/src/tools/clippy/tests/ui/allow_attributes.rs @@ -22,6 +22,13 @@ struct T4; #[cfg_attr(panic = "unwind", allow(dead_code))] struct CfgT; +#[allow(clippy::allow_attributes, unused)] +struct Allowed; + +#[expect(clippy::allow_attributes)] +#[allow(unused)] +struct Expected; + fn ignore_external() { external! { #[allow(clippy::needless_borrow)] // Should not lint diff --git a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr index d04ea7151c2..afb634f34b4 100644 --- a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr +++ b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr @@ -1,17 +1,12 @@ error: `clippy::restriction` is not meant to be enabled as a group - | - = note: because of the command line `--warn clippy::restriction` - = help: enable the restriction lints you need individually - = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]` - -error: `clippy::restriction` is not meant to be enabled as a group --> $DIR/blanket_clippy_restriction_lints.rs:6:9 | LL | #![warn(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | = help: enable the restriction lints you need individually + = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]` error: `clippy::restriction` is not meant to be enabled as a group --> $DIR/blanket_clippy_restriction_lints.rs:8:9 @@ -29,5 +24,10 @@ LL | #![forbid(clippy::restriction)] | = help: enable the restriction lints you need individually +error: `clippy::restriction` is not meant to be enabled as a group + | + = note: because of the command line `--warn clippy::restriction` + = help: enable the restriction lints you need individually + error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index f7c2f14a482..47b56960a00 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -198,6 +198,16 @@ fn pulldown_cmark_crash() {} /// [plain text][path::to::item] fn intra_doc_link() {} +/// Ignore escaped\_underscores +/// +/// \\[ +/// \\prod\_{x\\in X} p\_x +/// \\] +fn issue_2581() {} + +/// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` +fn lint_after_escaped_chars() {} + // issue #7033 - generic_const_exprs ICE struct S<T, const N: usize> where [(); N.checked_next_power_of_two().unwrap()]: { diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index 51961e75b84..4d9a4eafa5f 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -198,6 +198,16 @@ fn pulldown_cmark_crash() {} /// [plain text][path::to::item] fn intra_doc_link() {} +/// Ignore escaped\_underscores +/// +/// \\[ +/// \\prod\_{x\\in X} p\_x +/// \\] +fn issue_2581() {} + +/// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint +fn lint_after_escaped_chars() {} + // issue #7033 - generic_const_exprs ICE struct S<T, const N: usize> where [(); N.checked_next_power_of_two().unwrap()]: { diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr index a30ded81385..4c9ff41d918 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr @@ -308,5 +308,16 @@ help: try LL | /// An iterator over `mycrate::Collection`'s values. | ~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 28 previous errors +error: item in documentation is missing backticks + --> $DIR/doc-fixable.rs:208:34 + | +LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` + | ~~~~~~~~~~~~~~~~~ + +error: aborting due to 29 previous errors diff --git a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs index 4a1711f79a0..6f7bab72040 100644 --- a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs +++ b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs @@ -48,4 +48,4 @@ fn other_markdown() {} /// /// `lol` /// pub struct Struct; /// ``` -fn iss_7421() {} +fn issue_7421() {} diff --git a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr index 92b6f8536c8..89ad8db3916 100644 --- a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr +++ b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr @@ -1,7 +1,8 @@ error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:7:1 + --> $DIR/unbalanced_ticks.rs:7:5 | -LL | / /// This is a doc comment with `unbalanced_tick marks and several words that +LL | /// This is a doc comment with `unbalanced_tick marks and several words that + | _____^ LL | | LL | | /// should be `encompassed_by` tick marks because they `contain_underscores`. LL | | /// Because of the initial `unbalanced_tick` pair, the error message is @@ -13,10 +14,10 @@ LL | | /// very `confusing_and_misleading`. = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]` error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:14:1 + --> $DIR/unbalanced_ticks.rs:14:5 | LL | /// This paragraph has `unbalanced_tick marks and should stop_linting. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair @@ -32,10 +33,10 @@ LL | /// This paragraph is fine and `should_be` linted normally. | ~~~~~~~~~~~ error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:20:1 + --> $DIR/unbalanced_ticks.rs:20:5 | LL | /// Double unbalanced backtick from ``here to here` should lint. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair @@ -51,18 +52,18 @@ LL | /// ## `not_fine` | ~~~~~~~~~~ error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:37:1 + --> $DIR/unbalanced_ticks.rs:37:5 | LL | /// ### `unbalanced - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:40:1 + --> $DIR/unbalanced_ticks.rs:40:5 | LL | /// - This `item has unbalanced tick marks - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair diff --git a/src/tools/clippy/tests/ui/doc_errors.rs b/src/tools/clippy/tests/ui/doc_errors.rs index 86721f61d19..d661231c59e 100644 --- a/src/tools/clippy/tests/ui/doc_errors.rs +++ b/src/tools/clippy/tests/ui/doc_errors.rs @@ -85,6 +85,22 @@ impl Struct1 { async fn async_priv_method_missing_errors_header() -> Result<(), ()> { unimplemented!(); } + + /** + # Errors + A description of the errors goes here. + */ + fn block_comment() -> Result<(), ()> { + unimplemented!(); + } + + /** + * # Errors + * A description of the errors goes here. + */ + fn block_comment_leading_asterisks() -> Result<(), ()> { + unimplemented!(); + } } pub trait Trait1 { diff --git a/src/tools/clippy/tests/ui/doc_errors.stderr b/src/tools/clippy/tests/ui/doc_errors.stderr index 9cea864f1d8..28b2db2440b 100644 --- a/src/tools/clippy/tests/ui/doc_errors.stderr +++ b/src/tools/clippy/tests/ui/doc_errors.stderr @@ -38,7 +38,7 @@ LL | pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:92:5 + --> $DIR/doc_errors.rs:108:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_doc_main.rs b/src/tools/clippy/tests/ui/needless_doc_main.rs index d31b9047eaa..fee05926ce4 100644 --- a/src/tools/clippy/tests/ui/needless_doc_main.rs +++ b/src/tools/clippy/tests/ui/needless_doc_main.rs @@ -10,7 +10,7 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// With an explicit return type it should lint too /// ```edition2015 /// fn main() -> () { @@ -18,7 +18,7 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// This should, too. /// ```rust /// fn main() { @@ -26,11 +26,12 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// This one too. /// ```no_run -/// fn main() { +/// // the fn is not always the first line //~^ ERROR: needless `fn main` in doctest +/// fn main() { /// unimplemented!(); /// } /// ``` diff --git a/src/tools/clippy/tests/ui/needless_doc_main.stderr b/src/tools/clippy/tests/ui/needless_doc_main.stderr index 8dd93bb05dd..84256548671 100644 --- a/src/tools/clippy/tests/ui/needless_doc_main.stderr +++ b/src/tools/clippy/tests/ui/needless_doc_main.stderr @@ -1,29 +1,47 @@ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:7:4 + --> $DIR/needless_doc_main.rs:7:5 | -LL | /// fn main() { - | ^^^^^^^^^^^^ +LL | /// fn main() { + | _____^ +LL | | +LL | | +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ | = note: `-D clippy::needless-doctest-main` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_doctest_main)]` error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:16:4 + --> $DIR/needless_doc_main.rs:16:5 | -LL | /// fn main() -> () { - | ^^^^^^^^^^^^^^^^^^ +LL | /// fn main() -> () { + | _____^ +LL | | +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:24:4 + --> $DIR/needless_doc_main.rs:24:5 | -LL | /// fn main() { - | ^^^^^^^^^^^^ +LL | /// fn main() { + | _____^ +LL | | +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:32:4 + --> $DIR/needless_doc_main.rs:32:5 | -LL | /// fn main() { - | ^^^^^^^^^^^^ +LL | /// // the fn is not always the first line + | _____^ +LL | | +LL | | /// fn main() { +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ error: aborting due to 4 previous errors diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 3f8d9f5a724..ab73f33497e 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -aeddd2ddfd35bb385dd36476fab326f40d57a131 +366dab13f711df90a6891411458544199d159cbc diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 97718f1f4a9..1e9219d4bb2 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -59,7 +59,6 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { fn after_analysis<'tcx>( &mut self, - handler: &EarlyErrorHandler, _: &rustc_interface::interface::Compiler, queries: &'tcx rustc_interface::Queries<'tcx>, ) -> Compilation { @@ -68,7 +67,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { tcx.sess.fatal("miri cannot be run on programs that fail compilation"); } - init_late_loggers(handler, tcx); + let handler = EarlyErrorHandler::new(tcx.sess.opts.error_format); + init_late_loggers(&handler, tcx); if !tcx.crate_types().contains(&CrateType::Executable) { tcx.sess.fatal("miri only makes sense on bin crates"); } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 4f249acda1d..cb095f94f35 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -199,6 +199,7 @@ pub fn report_error<'tcx, 'mir>( e: InterpErrorInfo<'tcx>, ) -> Option<(i64, bool)> { use InterpError::*; + use UndefinedBehaviorInfo::*; let mut msg = vec![]; @@ -271,7 +272,7 @@ pub fn report_error<'tcx, 'mir>( (title, helps) } else { let title = match e.kind() { - UndefinedBehavior(UndefinedBehaviorInfo::ValidationError(validation_err)) + UndefinedBehavior(ValidationError(validation_err)) if matches!( validation_err.kind, ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer @@ -299,7 +300,7 @@ pub fn report_error<'tcx, 'mir>( let helps = match e.kind() { Unsupported(_) => vec![(None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"))], - UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. }) + UndefinedBehavior(AlignmentCheckFailed { .. }) if ecx.machine.check_alignment == AlignmentCheck::Symbolic => vec![ @@ -311,13 +312,20 @@ pub fn report_error<'tcx, 'mir>( (None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")), (None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")), ]; - if let UndefinedBehaviorInfo::PointerUseAfterFree(alloc_id, _) | UndefinedBehaviorInfo::PointerOutOfBounds { alloc_id, .. } = info { - if let Some(span) = ecx.machine.allocated_span(*alloc_id) { - helps.push((Some(span), format!("{:?} was allocated here:", alloc_id))); + match info { + PointerUseAfterFree(alloc_id, _) | PointerOutOfBounds { alloc_id, .. } => { + if let Some(span) = ecx.machine.allocated_span(*alloc_id) { + helps.push((Some(span), format!("{:?} was allocated here:", alloc_id))); + } + if let Some(span) = ecx.machine.deallocated_span(*alloc_id) { + helps.push((Some(span), format!("{:?} was deallocated here:", alloc_id))); + } } - if let Some(span) = ecx.machine.deallocated_span(*alloc_id) { - helps.push((Some(span), format!("{:?} was deallocated here:", alloc_id))); + AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => { + helps.push((None, format!("this means these two types are not *guaranteed* to be ABI-compatible across all targets"))); + helps.push((None, format!("if you think this code should be accepted anyway, please report an issue"))); } + _ => {}, } helps } @@ -339,7 +347,7 @@ pub fn report_error<'tcx, 'mir>( // We want to dump the allocation if this is `InvalidUninitBytes`. Since `format_error` consumes `e`, we compute the outut early. let mut extra = String::new(); match e.kind() { - UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some((alloc_id, access)))) => { + UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => { writeln!( extra, "Uninitialized memory occurred at {alloc_id:?}{range:?}, in this allocation:", diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr index 50d4228c111..d1ccaf89974 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr @@ -6,6 +6,8 @@ LL | g(Default::default()) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_array_vs_struct.rs:LL:CC diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr index a53126c733e..3875c2617bb 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr @@ -6,6 +6,8 @@ LL | g(42) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_int_vs_float.rs:LL:CC diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr index 6eacfeece14..6d1bdfee007 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr @@ -6,6 +6,8 @@ LL | g(&42 as *const i32) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_raw_pointer.rs:LL:CC diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr index eedc1235773..07d76c90e5e 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr @@ -6,6 +6,8 @@ LL | g() | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_return_type.rs:LL:CC diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr index bc500a90b77..7ac2bc2739f 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr @@ -6,6 +6,8 @@ LL | g(42) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_simple.rs:LL:CC diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr index 7dcca1e85b8..e082eb9e3e3 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr @@ -6,6 +6,8 @@ LL | g(Default::default()) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_vector.rs:LL:CC diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr1.rs b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_arg.rs index 6ab73569c63..6ab73569c63 100644 --- a/src/tools/miri/tests/fail/validity/cast_fn_ptr1.rs +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_arg.rs diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr1.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_arg.stderr index 133e4b2c16a..21e403b47f8 100644 --- a/src/tools/miri/tests/fail/validity/cast_fn_ptr1.stderr +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_arg.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: constructing invalid value: encountered a null reference - --> $DIR/cast_fn_ptr1.rs:LL:CC + --> $DIR/cast_fn_ptr_invalid_callee_arg.rs:LL:CC | LL | g(0usize as *const i32) | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null reference @@ -7,7 +7,7 @@ LL | g(0usize as *const i32) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `main` at $DIR/cast_fn_ptr1.rs:LL:CC + = note: inside `main` at $DIR/cast_fn_ptr_invalid_callee_arg.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs new file mode 100644 index 00000000000..7cdc15c6094 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs @@ -0,0 +1,28 @@ +#![allow(internal_features)] +#![feature(core_intrinsics, custom_mir)] + +use std::intrinsics::mir::*; +use std::num::NonZeroU32; +use std::ptr; + +// This function supposedly returns a NonZeroU32, but actually returns something invalid in a way that +// never materializes a bad NonZeroU32 value: we take a pointer to the return place and cast the pointer +// type. That way we never get an "invalid value constructed" error inside the function, it can +// only possibly be detected when the return value is passed to the caller. +#[custom_mir(dialect = "runtime", phase = "optimized")] +fn f() -> NonZeroU32 { + mir! { + { + let tmp = ptr::addr_of_mut!(RET); + let ptr = tmp as *mut u32; + *ptr = 0; + Return() + } + } +} + +fn main() { + let f: fn() -> u32 = unsafe { std::mem::transmute(f as fn() -> NonZeroU32) }; + // There's a NonZeroU32-to-u32 transmute happening here + f(); //~ERROR: expected something greater or equal to 1 +} diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr new file mode 100644 index 00000000000..ccfb8890939 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value: encountered 0, but expected something greater or equal to 1 + --> $DIR/cast_fn_ptr_invalid_callee_ret.rs:LL:CC + | +LL | f(); + | ^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/cast_fn_ptr_invalid_callee_ret.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs new file mode 100644 index 00000000000..ee80186d4b5 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs @@ -0,0 +1,34 @@ +#![allow(internal_features)] +#![feature(core_intrinsics, custom_mir)] + +use std::intrinsics::mir::*; +use std::num::NonZeroU32; +use std::ptr; + +fn f(c: u32) { + println!("{c}"); +} + +// Call that function in a bad way, with an invalid NonZeroU32, but without +// ever materializing this as a NonZeroU32 value outside the call itself. +#[custom_mir(dialect = "runtime", phase = "optimized")] +fn call(f: fn(NonZeroU32)) { + mir! { + let _res: (); + { + let c = 0; + let tmp = ptr::addr_of!(c); + let ptr = tmp as *const NonZeroU32; + // The call site now is a NonZeroU32-to-u32 transmute. + Call(_res = f(*ptr), retblock) //~ERROR: expected something greater or equal to 1 + } + retblock = { + Return() + } + } +} + +fn main() { + let f: fn(NonZeroU32) = unsafe { std::mem::transmute(f as fn(u32)) }; + call(f); +} diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr new file mode 100644 index 00000000000..234c2804008 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: constructing invalid value: encountered 0, but expected something greater or equal to 1 + --> $DIR/cast_fn_ptr_invalid_caller_arg.rs:LL:CC + | +LL | Call(_res = f(*ptr), retblock) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `call` at $DIR/cast_fn_ptr_invalid_caller_arg.rs:LL:CC +note: inside `main` + --> $DIR/cast_fn_ptr_invalid_caller_arg.rs:LL:CC + | +LL | call(f); + | ^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr2.rs b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_ret.rs index 64ddb563be5..64ddb563be5 100644 --- a/src/tools/miri/tests/fail/validity/cast_fn_ptr2.rs +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_ret.rs diff --git a/src/tools/miri/tests/fail/validity/cast_fn_ptr2.stderr b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_ret.stderr index 21001f2b460..bd9866acbd4 100644 --- a/src/tools/miri/tests/fail/validity/cast_fn_ptr2.stderr +++ b/src/tools/miri/tests/fail/validity/cast_fn_ptr_invalid_caller_ret.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: constructing invalid value: encountered a null reference - --> $DIR/cast_fn_ptr2.rs:LL:CC + --> $DIR/cast_fn_ptr_invalid_caller_ret.rs:LL:CC | LL | let _x = g(); | ^^^ constructing invalid value: encountered a null reference @@ -7,7 +7,7 @@ LL | let _x = g(); = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `main` at $DIR/cast_fn_ptr2.rs:LL:CC + = note: inside `main` at $DIR/cast_fn_ptr_invalid_caller_ret.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/pass/function_calls/abi_compat.rs b/src/tools/miri/tests/pass/function_calls/abi_compat.rs index 08be29115ca..064605d453a 100644 --- a/src/tools/miri/tests/pass/function_calls/abi_compat.rs +++ b/src/tools/miri/tests/pass/function_calls/abi_compat.rs @@ -1,10 +1,19 @@ use std::mem; use std::num; +use std::ptr; #[derive(Copy, Clone, Default)] struct Zst; -fn test_abi_compat<T: Copy, U: Copy>(t: T, u: U) { +#[repr(transparent)] +#[derive(Copy, Clone)] +struct Wrapper<T>(T); + +fn id<T>(x: T) -> T { + x +} + +fn test_abi_compat<T: Clone, U: Clone>(t: T, u: U) { fn id<T>(x: T) -> T { x } @@ -16,10 +25,10 @@ fn test_abi_compat<T: Copy, U: Copy>(t: T, u: U) { // in both directions. let f: fn(T) -> T = id; let f: fn(U) -> U = unsafe { std::mem::transmute(f) }; - let _val = f(u); + let _val = f(u.clone()); let f: fn(U) -> U = id; let f: fn(T) -> T = unsafe { std::mem::transmute(f) }; - let _val = f(t); + let _val = f(t.clone()); // And then we do the same for `extern "C"`. let f: extern "C" fn(T) -> T = id_c; @@ -34,9 +43,6 @@ fn test_abi_compat<T: Copy, U: Copy>(t: T, u: U) { fn test_abi_newtype<T: Copy + Default>() { #[repr(transparent)] #[derive(Copy, Clone)] - struct Wrapper1<T>(T); - #[repr(transparent)] - #[derive(Copy, Clone)] struct Wrapper2<T>(T, ()); #[repr(transparent)] #[derive(Copy, Clone)] @@ -46,7 +52,7 @@ fn test_abi_newtype<T: Copy + Default>() { struct Wrapper3<T>(Zst, T, [u8; 0]); let t = T::default(); - test_abi_compat(t, Wrapper1(t)); + test_abi_compat(t, Wrapper(t)); test_abi_compat(t, Wrapper2(t, ())); test_abi_compat(t, Wrapper2a((), t)); test_abi_compat(t, Wrapper3(Zst, t, [])); @@ -54,23 +60,30 @@ fn test_abi_newtype<T: Copy + Default>() { } fn main() { - // Here we check: - // - u32 vs char is allowed - // - u32 vs NonZeroU32/Option<NonZeroU32> is allowed - // - reference vs raw pointer is allowed - // - references to things of the same size and alignment are allowed - // These are very basic tests that should work on all ABIs. However it is not clear that any of - // these would be stably guaranteed. Code that relies on this is equivalent to code that relies - // on the layout of `repr(Rust)` types. They are also fragile: the same mismatches in the fields - // of a struct (even with `repr(C)`) will not always be accepted by Miri. - // Note that `bool` and `u8` are *not* compatible, at least on x86-64! - // One of them has `arg_ext: Zext`, the other does not. - // Similarly, `i32` and `u32` are not compatible on s390x due to different `arg_ext`. - test_abi_compat(0u32, 'x'); + // Here we check some of the guaranteed ABI compatibilities. + // Different integer types of the same size and sign. + if cfg!(target_pointer_width = "32") { + test_abi_compat(0usize, 0u32); + test_abi_compat(0isize, 0i32); + } else { + test_abi_compat(0usize, 0u64); + test_abi_compat(0isize, 0i64); + } test_abi_compat(42u32, num::NonZeroU32::new(1).unwrap()); - test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap())); + // Reference/pointer types with the same pointee. test_abi_compat(&0u32, &0u32 as *const u32); + test_abi_compat(&mut 0u32 as *mut u32, Box::new(0u32)); + test_abi_compat(&(), ptr::NonNull::<()>::dangling()); + // Reference/pointer types with different but sized pointees. test_abi_compat(&0u32, &([true; 4], [0u32; 0])); + // `fn` types + test_abi_compat(main as fn(), id::<i32> as fn(i32) -> i32); + // Guaranteed null-pointer-optimizations. + test_abi_compat(&0u32 as *const u32, Some(&0u32)); + test_abi_compat(main as fn(), Some(main as fn())); + test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap())); + test_abi_compat(&0u32 as *const u32, Some(Wrapper(&0u32))); + test_abi_compat(0u32, Some(Wrapper(num::NonZeroU32::new(1).unwrap()))); // These must work for *any* type, since we guarantee that `repr(transparent)` is ABI-compatible // with the wrapped field. |
