Merge from rustc

This commit is contained in:
Ralf Jung 2025-04-10 14:24:19 +02:00
commit f69ea4d82f
872 changed files with 7959 additions and 5020 deletions

View file

@ -45,6 +45,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
pats.iter().all(unary_pattern)
}
match &pat.kind {
PatKind::Missing => unreachable!(),
PatKind::Slice(_, _, _)
| PatKind::Range(_, _, _)
| PatKind::Binding(..)

View file

@ -253,6 +253,7 @@ fn iter_matching_struct_fields<'a>(
impl<'a> NormalizedPat<'a> {
fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self {
match pat.kind {
PatKind::Missing => unreachable!(),
PatKind::Wild | PatKind::Binding(.., None) => Self::Wild,
PatKind::Binding(.., Some(pat))
| PatKind::Box(pat)

View file

@ -406,6 +406,7 @@ impl<'a> PatState<'a> {
pats.iter().map(|p| p.pat),
),
PatKind::Missing => unreachable!(),
PatKind::Wild
| PatKind::Binding(_, _, _, None)
| PatKind::Expr(_)

View file

@ -224,6 +224,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us
// We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`.
let changed = match &mut focus_kind {
Missing => unreachable!(),
// These pattern forms are "leafs" and do not have sub-patterns.
// Therefore they are not some form of constructor `C`,
// with which a pattern `C(p_0)` may be formed,

View file

@ -676,6 +676,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
}
match pat.value.kind {
PatKind::Missing => unreachable!(),
PatKind::Wild => kind!("Wild"),
PatKind::Never => kind!("Never"),
PatKind::Binding(ann, _, name, sub) => {

View file

@ -33,6 +33,7 @@ pub fn eq_id(l: Ident, r: Ident) -> bool {
pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
use PatKind::*;
match (&l.kind, &r.kind) {
(Missing, _) | (_, Missing) => unreachable!(),
(Paren(l), _) => eq_pat(l, r),
(_, Paren(r)) => eq_pat(l, r),
(Wild, Wild) | (Rest, Rest) => true,

View file

@ -1124,6 +1124,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
pub fn hash_pat(&mut self, pat: &Pat<'_>) {
std::mem::discriminant(&pat.kind).hash(&mut self.s);
match pat.kind {
PatKind::Missing => unreachable!(),
PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => {
std::mem::discriminant(&by_ref).hash(&mut self.s);
std::mem::discriminant(&mutability).hash(&mut self.s);

View file

@ -1858,6 +1858,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
}
match pat.kind {
PatKind::Missing => unreachable!(),
PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable.
PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),

View file

@ -7,25 +7,26 @@ edition = "2021"
doctest = false
[dependencies]
# tidy-alphabetical-start
anstyle-svg = "0.1.3"
build_helper = { path = "../../build_helper" }
colored = "2"
diff = "0.1.10"
unified-diff = "0.2.1"
getopts = "0.2"
glob = "0.3.0"
home = "0.5.5"
indexmap = "2.0.0"
miropt-test-tools = { path = "../miropt-test-tools" }
build_helper = { path = "../../build_helper" }
tracing = "0.1"
tracing-subscriber = { version = "0.3.3", default-features = false, features = ["ansi", "env-filter", "fmt", "parking_lot", "smallvec"] }
regex = "1.0"
rustfix = "0.8.1"
semver = { version = "1.0.23", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
rustfix = "0.8.1"
tracing = "0.1"
tracing-subscriber = { version = "0.3.3", default-features = false, features = ["ansi", "env-filter", "fmt", "parking_lot", "smallvec"] }
unified-diff = "0.2.1"
walkdir = "2"
glob = "0.3.0"
anyhow = "1"
home = "0.5.5"
# tidy-alphabetical-end
[target.'cfg(unix)'.dependencies]
libc = "0.2"

View file

@ -275,9 +275,6 @@ pub struct Config {
/// Explicitly enable or disable running.
pub run: Option<bool>,
/// Write out a parseable log of tests that were run
pub logfile: Option<PathBuf>,
/// A command line to prefix program execution with,
/// for running under valgrind for example.
///

View file

@ -22,6 +22,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"dont-check-compiler-stderr",
"dont-check-compiler-stdout",
"dont-check-failure-status",
"dont-require-annotations",
"edition",
"error-pattern",
"exact-llvm-major-version",

View file

@ -3,13 +3,12 @@ use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::Path;
use std::str::FromStr;
use std::sync::OnceLock;
use regex::Regex;
use tracing::*;
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum ErrorKind {
Help,
Error,
@ -18,30 +17,48 @@ pub enum ErrorKind {
Warning,
}
impl FromStr for ErrorKind {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_uppercase();
let part0: &str = s.split(':').next().unwrap();
match part0 {
"HELP" => Ok(ErrorKind::Help),
"ERROR" => Ok(ErrorKind::Error),
"NOTE" => Ok(ErrorKind::Note),
"SUGGESTION" => Ok(ErrorKind::Suggestion),
"WARN" | "WARNING" => Ok(ErrorKind::Warning),
_ => Err(()),
impl ErrorKind {
pub fn from_compiler_str(s: &str) -> ErrorKind {
match s {
"help" => ErrorKind::Help,
"error" | "error: internal compiler error" => ErrorKind::Error,
"note" | "failure-note" => ErrorKind::Note,
"warning" => ErrorKind::Warning,
_ => panic!("unexpected compiler diagnostic kind `{s}`"),
}
}
/// Either the canonical uppercase string, or some additional versions for compatibility.
/// FIXME: consider keeping only the canonical versions here.
fn from_user_str(s: &str) -> Option<ErrorKind> {
Some(match s {
"HELP" | "help" => ErrorKind::Help,
"ERROR" | "error" => ErrorKind::Error,
"NOTE" | "note" => ErrorKind::Note,
"SUGGESTION" => ErrorKind::Suggestion,
"WARN" | "WARNING" | "warn" | "warning" => ErrorKind::Warning,
_ => return None,
})
}
pub fn expect_from_user_str(s: &str) -> ErrorKind {
ErrorKind::from_user_str(s).unwrap_or_else(|| {
panic!(
"unexpected diagnostic kind `{s}`, expected \
`ERROR`, `WARN`, `NOTE`, `HELP` or `SUGGESTION`"
)
})
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ErrorKind::Help => write!(f, "help message"),
ErrorKind::Error => write!(f, "error"),
ErrorKind::Note => write!(f, "note"),
ErrorKind::Suggestion => write!(f, "suggestion"),
ErrorKind::Warning => write!(f, "warning"),
ErrorKind::Help => write!(f, "HELP"),
ErrorKind::Error => write!(f, "ERROR"),
ErrorKind::Note => write!(f, "NOTE"),
ErrorKind::Suggestion => write!(f, "SUGGESTION"),
ErrorKind::Warning => write!(f, "WARN"),
}
}
}
@ -53,6 +70,10 @@ pub struct Error {
/// `None` if not specified or unknown message kind.
pub kind: Option<ErrorKind>,
pub msg: String,
/// For some `Error`s, like secondary lines of multi-line diagnostics, line annotations
/// are not mandatory, even if they would otherwise be mandatory for primary errors.
/// Only makes sense for "actual" errors, not for "expected" errors.
pub require_annotation: bool,
}
impl Error {
@ -60,7 +81,7 @@ impl Error {
use colored::Colorize;
format!(
"{: <10}line {: >3}: {}",
self.kind.map(|kind| kind.to_string()).unwrap_or_default().to_uppercase(),
self.kind.map(|kind| kind.to_string()).unwrap_or_default(),
self.line_num_str(),
self.msg.cyan(),
)
@ -150,18 +171,12 @@ fn parse_expected(
}
// Get the part of the comment after the sigil (e.g. `~^^` or ~|).
let whole_match = captures.get(0).unwrap();
let (_, mut msg) = line.split_at(whole_match.end());
let first_word = msg.split_whitespace().next().expect("Encountered unexpected empty comment");
// If we find `//~ ERROR foo` or something like that, skip the first word.
let kind = first_word.parse::<ErrorKind>().ok();
if kind.is_some() {
msg = &msg.trim_start().split_at(first_word.len()).1;
}
let msg = msg.trim().to_owned();
let tag = captures.get(0).unwrap();
let rest = line[tag.end()..].trim_start();
let (kind_str, _) = rest.split_once(|c: char| !c.is_ascii_alphabetic()).unwrap_or((rest, ""));
let kind = ErrorKind::from_user_str(kind_str);
let untrimmed_msg = if kind.is_some() { &rest[kind_str.len()..] } else { rest };
let msg = untrimmed_msg.strip_prefix(':').unwrap_or(untrimmed_msg).trim().to_owned();
let line_num_adjust = &captures["adjust"];
let (follow_prev, line_num) = if line_num_adjust == "|" {
@ -177,12 +192,12 @@ fn parse_expected(
debug!(
"line={:?} tag={:?} follow_prev={:?} kind={:?} msg={:?}",
line_num,
whole_match.as_str(),
tag.as_str(),
follow_prev,
kind,
msg
);
Some((follow_prev, Error { line_num, kind, msg }))
Some((follow_prev, Error { line_num, kind, msg, require_annotation: true }))
}
#[cfg(test)]

View file

@ -138,7 +138,7 @@ fn test_opts(config: &Config) -> test::TestOpts {
filter_exact: config.filter_exact,
run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
format: config.format.to_libtest(),
logfile: config.logfile.clone(),
logfile: None,
run_tests: true,
bench_benchmarks: true,
nocapture: config.nocapture,

View file

@ -1,4 +1,4 @@
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::env;
use std::fs::File;
use std::io::BufReader;
@ -11,6 +11,7 @@ use tracing::*;
use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
use crate::debuggers::{extract_cdb_version, extract_gdb_version};
use crate::errors::ErrorKind;
use crate::executor::{CollectedTestDesc, ShouldPanic};
use crate::header::auxiliary::{AuxProps, parse_and_update_aux};
use crate::header::needs::CachedNeedsConditions;
@ -196,6 +197,8 @@ pub struct TestProps {
/// Build and use `minicore` as `core` stub for `no_core` tests in cross-compilation scenarios
/// that don't otherwise want/need `-Z build-std`.
pub add_core_stubs: bool,
/// Whether line annotatins are required for the given error kind.
pub require_annotations: HashMap<ErrorKind, bool>,
}
mod directives {
@ -212,6 +215,7 @@ mod directives {
pub const CHECK_RUN_RESULTS: &'static str = "check-run-results";
pub const DONT_CHECK_COMPILER_STDOUT: &'static str = "dont-check-compiler-stdout";
pub const DONT_CHECK_COMPILER_STDERR: &'static str = "dont-check-compiler-stderr";
pub const DONT_REQUIRE_ANNOTATIONS: &'static str = "dont-require-annotations";
pub const NO_PREFER_DYNAMIC: &'static str = "no-prefer-dynamic";
pub const PRETTY_MODE: &'static str = "pretty-mode";
pub const PRETTY_COMPARE_ONLY: &'static str = "pretty-compare-only";
@ -297,6 +301,13 @@ impl TestProps {
no_auto_check_cfg: false,
has_enzyme: false,
add_core_stubs: false,
require_annotations: HashMap::from([
(ErrorKind::Help, true),
(ErrorKind::Note, true),
(ErrorKind::Error, true),
(ErrorKind::Warning, true),
(ErrorKind::Suggestion, false),
]),
}
}
@ -570,6 +581,13 @@ impl TestProps {
config.set_name_directive(ln, NO_AUTO_CHECK_CFG, &mut self.no_auto_check_cfg);
self.update_add_core_stubs(ln, config);
if let Some(err_kind) =
config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS)
{
self.require_annotations
.insert(ErrorKind::expect_from_user_str(&err_kind), false);
}
},
);

View file

@ -459,9 +459,6 @@ fn profiler_runtime() {
#[test]
fn asm_support() {
let asms = [
#[cfg(bootstrap)]
("avr-unknown-gnu-atmega328", false),
#[cfg(not(bootstrap))]
("avr-none", false),
("i686-unknown-netbsd", true),
("riscv32gc-unknown-linux-gnu", true),

View file

@ -1,7 +1,6 @@
//! These structs are a subset of the ones found in `rustc_errors::json`.
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::OnceLock;
use regex::Regex;
@ -142,43 +141,34 @@ pub fn extract_rendered(output: &str) -> String {
}
pub fn parse_output(file_name: &str, output: &str, proc_res: &ProcRes) -> Vec<Error> {
output.lines().flat_map(|line| parse_line(file_name, line, output, proc_res)).collect()
}
fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) -> Vec<Error> {
// The compiler sometimes intermingles non-JSON stuff into the
// output. This hack just skips over such lines. Yuck.
if line.starts_with('{') {
match serde_json::from_str::<Diagnostic>(line) {
Ok(diagnostic) => {
let mut expected_errors = vec![];
push_expected_errors(&mut expected_errors, &diagnostic, &[], file_name);
expected_errors
}
Err(error) => {
// Ignore the future compat report message - this is handled
// by `extract_rendered`
if serde_json::from_str::<FutureIncompatReport>(line).is_ok() {
vec![]
} else {
proc_res.fatal(
let mut errors = Vec::new();
for line in output.lines() {
// The compiler sometimes intermingles non-JSON stuff into the
// output. This hack just skips over such lines. Yuck.
if line.starts_with('{') {
match serde_json::from_str::<Diagnostic>(line) {
Ok(diagnostic) => push_actual_errors(&mut errors, &diagnostic, &[], file_name),
Err(error) => {
// Ignore the future compat report message - this is handled
// by `extract_rendered`
if serde_json::from_str::<FutureIncompatReport>(line).is_err() {
proc_res.fatal(
Some(&format!(
"failed to decode compiler output as json: \
`{}`\nline: {}\noutput: {}",
"failed to decode compiler output as json: `{}`\nline: {}\noutput: {}",
error, line, output
)),
|| (),
);
}
}
}
}
} else {
vec![]
}
errors
}
fn push_expected_errors(
expected_errors: &mut Vec<Error>,
fn push_actual_errors(
errors: &mut Vec<Error>,
diagnostic: &Diagnostic,
default_spans: &[&DiagnosticSpan],
file_name: &str,
@ -236,44 +226,47 @@ fn push_expected_errors(
}
};
// Convert multi-line messages into multiple expected
// errors. We expect to replace these with something
// more structured shortly anyhow.
// Convert multi-line messages into multiple errors.
// We expect to replace these with something more structured anyhow.
let mut message_lines = diagnostic.message.lines();
if let Some(first_line) = message_lines.next() {
let ignore = |s| {
static RE: OnceLock<Regex> = OnceLock::new();
RE.get_or_init(|| {
Regex::new(r"aborting due to \d+ previous errors?|\d+ warnings? emitted").unwrap()
})
.is_match(s)
};
if primary_spans.is_empty() && !ignore(first_line) {
let msg = with_code(None, first_line);
let kind = ErrorKind::from_str(&diagnostic.level).ok();
expected_errors.push(Error { line_num: None, kind, msg });
} else {
for span in primary_spans {
let msg = with_code(Some(span), first_line);
let kind = ErrorKind::from_str(&diagnostic.level).ok();
expected_errors.push(Error { line_num: Some(span.line_start), kind, msg });
}
let kind = Some(ErrorKind::from_compiler_str(&diagnostic.level));
let first_line = message_lines.next().unwrap_or(&diagnostic.message);
if primary_spans.is_empty() {
static RE: OnceLock<Regex> = OnceLock::new();
let re_init =
|| Regex::new(r"aborting due to \d+ previous errors?|\d+ warnings? emitted").unwrap();
errors.push(Error {
line_num: None,
kind,
msg: with_code(None, first_line),
require_annotation: diagnostic.level != "failure-note"
&& !RE.get_or_init(re_init).is_match(first_line),
});
} else {
for span in primary_spans {
errors.push(Error {
line_num: Some(span.line_start),
kind,
msg: with_code(Some(span), first_line),
require_annotation: true,
});
}
}
for next_line in message_lines {
if primary_spans.is_empty() {
expected_errors.push(Error {
errors.push(Error {
line_num: None,
kind: None,
kind,
msg: with_code(None, next_line),
require_annotation: false,
});
} else {
for span in primary_spans {
expected_errors.push(Error {
errors.push(Error {
line_num: Some(span.line_start),
kind: None,
kind,
msg: with_code(Some(span), next_line),
require_annotation: false,
});
}
}
@ -283,10 +276,11 @@ fn push_expected_errors(
for span in primary_spans {
if let Some(ref suggested_replacement) = span.suggested_replacement {
for (index, line) in suggested_replacement.lines().enumerate() {
expected_errors.push(Error {
errors.push(Error {
line_num: Some(span.line_start + index),
kind: Some(ErrorKind::Suggestion),
msg: line.to_string(),
require_annotation: true,
});
}
}
@ -295,39 +289,41 @@ fn push_expected_errors(
// Add notes for the backtrace
for span in primary_spans {
if let Some(frame) = &span.expansion {
push_backtrace(expected_errors, frame, file_name);
push_backtrace(errors, frame, file_name);
}
}
// Add notes for any labels that appear in the message.
for span in spans_in_this_file.iter().filter(|span| span.label.is_some()) {
expected_errors.push(Error {
errors.push(Error {
line_num: Some(span.line_start),
kind: Some(ErrorKind::Note),
msg: span.label.clone().unwrap(),
require_annotation: true,
});
}
// Flatten out the children.
for child in &diagnostic.children {
push_expected_errors(expected_errors, child, primary_spans, file_name);
push_actual_errors(errors, child, primary_spans, file_name);
}
}
fn push_backtrace(
expected_errors: &mut Vec<Error>,
errors: &mut Vec<Error>,
expansion: &DiagnosticSpanMacroExpansion,
file_name: &str,
) {
if Path::new(&expansion.span.file_name) == Path::new(&file_name) {
expected_errors.push(Error {
errors.push(Error {
line_num: Some(expansion.span.line_start),
kind: Some(ErrorKind::Note),
msg: format!("in this expansion of {}", expansion.macro_decl_name),
require_annotation: true,
});
}
if let Some(previous_expansion) = &expansion.span.expansion {
push_backtrace(expected_errors, previous_expansion, file_name);
push_backtrace(errors, previous_expansion, file_name);
}
}

View file

@ -139,7 +139,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
.optflag("", "quiet", "print one character per test instead of one line")
.optopt("", "color", "coloring: auto, always, never", "WHEN")
.optflag("", "json", "emit json output instead of plaintext output")
.optopt("", "logfile", "file to log test execution to", "FILE")
.optopt("", "target", "the target to build for", "TARGET")
.optopt("", "host", "the host to build for", "HOST")
.optopt("", "cdb", "path to CDB to use for CDB debuginfo tests", "PATH")
@ -378,7 +377,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
"never" => Some(false),
_ => panic!("unknown `--run` option `{}` given", mode),
}),
logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
runner: matches.opt_str("runner"),
host_rustcflags: matches.opt_strs("host-rustcflags"),
target_rustcflags: matches.opt_strs("target-rustcflags"),

View file

@ -10,7 +10,6 @@ use std::process::{Child, Command, ExitStatus, Output, Stdio};
use std::sync::Arc;
use std::{env, iter, str};
use anyhow::Context;
use colored::Colorize;
use regex::{Captures, Regex};
use tracing::*;
@ -143,11 +142,11 @@ pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) {
}
let cx = TestCx { config: &config, props: &props, testpaths, revision };
create_dir_all(&cx.output_base_dir())
.with_context(|| {
format!("failed to create output base directory {}", cx.output_base_dir().display())
})
.unwrap();
if let Err(e) = create_dir_all(&cx.output_base_dir()) {
panic!("failed to create output base directory {}: {e}", cx.output_base_dir().display());
}
if props.incremental {
cx.init_incremental_test();
}
@ -710,10 +709,6 @@ impl<'test> TestCx<'test> {
self.testpaths.file.display().to_string()
};
// If the testcase being checked contains at least one expected "help"
// message, then we'll ensure that all "help" messages are expected.
// Otherwise, all "help" messages reported by the compiler will be ignored.
// This logic also applies to "note" messages.
let expect_help = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Help));
let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note));
@ -801,22 +796,24 @@ impl<'test> TestCx<'test> {
}
/// Returns `true` if we should report an error about `actual_error`,
/// which did not match any of the expected error. We always require
/// errors/warnings to be explicitly listed, but only require
/// helps/notes if there are explicit helps/notes given.
/// which did not match any of the expected error.
fn is_unexpected_compiler_message(
&self,
actual_error: &Error,
expect_help: bool,
expect_note: bool,
) -> bool {
!actual_error.msg.is_empty()
&& match actual_error.kind {
Some(ErrorKind::Help) => expect_help,
Some(ErrorKind::Note) => expect_note,
Some(ErrorKind::Error) | Some(ErrorKind::Warning) => true,
Some(ErrorKind::Suggestion) | None => false,
}
actual_error.require_annotation
&& actual_error.kind.map_or(false, |err_kind| {
// If the test being checked doesn't contain any "help" or "note" annotations, then
// we don't require annotating "help" or "note" (respecively) diagnostics at all.
let default_require_annotations = self.props.require_annotations[&err_kind];
match err_kind {
ErrorKind::Help => expect_help && default_require_annotations,
ErrorKind::Note => expect_note && default_require_annotations,
_ => default_require_annotations,
}
})
}
fn should_emit_metadata(&self, pm: Option<PassMode>) -> Emit {

View file

@ -8,8 +8,8 @@ description = "Produces a manifest of all the copyrighted materials in the Rust
[dependencies]
anyhow = "1.0.65"
askama = "0.13.0"
cargo_metadata = "0.18.1"
rinja = "0.3.0"
serde = { version = "1.0.147", features = ["derive"] }
serde_json = "1.0.85"
thiserror = "1"

View file

@ -2,7 +2,7 @@ use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use anyhow::Error;
use rinja::Template;
use askama::Template;
mod cargo_metadata;
@ -117,7 +117,7 @@ struct Metadata {
}
/// Describes one node in our metadata tree
#[derive(serde::Deserialize, rinja::Template, Clone, Debug, PartialEq, Eq)]
#[derive(serde::Deserialize, Template, Clone, Debug, PartialEq, Eq)]
#[serde(rename_all = "kebab-case", tag = "type")]
#[template(path = "Node.html")]
pub(crate) enum Node {

View file

@ -6882,109 +6882,14 @@ pub fn foo() {}
#[test]
fn hover_feature() {
check(
r#"#![feature(intrinsics$0)]"#,
expect![[r#"
*intrinsics*
```
intrinsics
```
___
# `intrinsics`
The tracking issue for this feature is: None.
Intrinsics are rarely intended to be stable directly, but are usually
exported in some sort of stable manner. Prefer using the stable interfaces to
the intrinsic directly when you can.
------------------------
## Intrinsics with fallback logic
Many intrinsics can be written in pure rust, albeit inefficiently or without supporting
some features that only exist on some backends. Backends can simply not implement those
intrinsics without causing any code miscompilations or failures to compile.
All intrinsic fallback bodies are automatically made cross-crate inlineable (like `#[inline]`)
by the codegen backend, but not the MIR inliner.
```rust
#![feature(intrinsics)]
#![allow(internal_features)]
#[rustc_intrinsic]
const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
```
Since these are just regular functions, it is perfectly ok to create the intrinsic twice:
```rust
#![feature(intrinsics)]
#![allow(internal_features)]
#[rustc_intrinsic]
const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
mod foo {
#[rustc_intrinsic]
const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {
panic!("noisy const dealloc")
}
}
```
The behaviour on backends that override the intrinsic is exactly the same. On other
backends, the intrinsic behaviour depends on which implementation is called, just like
with any regular function.
## Intrinsics lowered to MIR instructions
Various intrinsics have native MIR operations that they correspond to. Instead of requiring
backends to implement both the intrinsic and the MIR operation, the `lower_intrinsics` pass
will convert the calls to the MIR operation. Backends do not need to know about these intrinsics
at all. These intrinsics only make sense without a body, and can either be declared as a "rust-intrinsic"
or as a `#[rustc_intrinsic]`. The body is never used, as calls to the intrinsic do not exist
anymore after MIR analyses.
## Intrinsics without fallback logic
These must be implemented by all backends.
### `#[rustc_intrinsic]` declarations
These are written like intrinsics with fallback bodies, but the body is irrelevant.
Use `loop {}` for the body or call the intrinsic recursively and add
`#[rustc_intrinsic_must_be_overridden]` to the function to ensure that backends don't
invoke the body.
### Legacy extern ABI based intrinsics
These are imported as if they were FFI functions, with the special
`rust-intrinsic` ABI. For example, if one was in a freestanding
context, but wished to be able to `transmute` between types, and
perform efficient pointer arithmetic, one would import those functions
via a declaration like
```rust
#![feature(intrinsics)]
#![allow(internal_features)]
# fn main() {}
extern "rust-intrinsic" {
fn transmute<T, U>(x: T) -> U;
fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
}
```
As with any other FFI functions, these are by default always `unsafe` to call.
You can add `#[rustc_safe_intrinsic]` to the intrinsic to make it safe to call.
"#]],
)
let (analysis, position) = fixture::position(r#"#![feature(intrinsics$0)]"#);
analysis
.hover(
&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG },
FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
)
.unwrap()
.unwrap();
}
#[test]

@ -1 +1 @@
Subproject commit e22e08623a349b13a3296971a05394d44f769744
Subproject commit c0f3b53c8e5de87714d18a5f42998859302ae03a

View file

@ -2442,11 +2442,7 @@ pub(crate) fn span_hi_for_param(context: &RewriteContext<'_>, param: &ast::Param
}
pub(crate) fn is_named_param(param: &ast::Param) -> bool {
if let ast::PatKind::Ident(_, ident, _) = param.pat.kind {
ident.name != symbol::kw::Empty
} else {
true
}
!matches!(param.pat.kind, ast::PatKind::Missing)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]

View file

@ -42,6 +42,7 @@ pub(crate) fn is_short_pattern(
fn is_short_pattern_inner(context: &RewriteContext<'_>, pat: &ast::Pat) -> bool {
match &pat.kind {
ast::PatKind::Missing => unreachable!(),
ast::PatKind::Rest | ast::PatKind::Never | ast::PatKind::Wild | ast::PatKind::Err(_) => {
true
}
@ -100,6 +101,7 @@ impl Rewrite for Pat {
fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
match self.kind {
PatKind::Missing => unreachable!(),
PatKind::Or(ref pats) => {
let pat_strs = pats
.iter()

View file

@ -164,7 +164,6 @@ const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[
("brotli-decompressor", "BSD-3-Clause/MIT"),
("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"),
("inferno", "CDDL-1.0"),
("instant", "BSD-3-Clause"),
("ring", NON_STANDARD_LICENSE), // see EXCEPTIONS_NON_STANDARD_LICENSE_DEPS for more.
("ryu", "Apache-2.0 OR BSL-1.0"),
("snap", "BSD-3-Clause"),