Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-05-10 05:01:09 +00:00
commit a4f765dcad
1554 changed files with 19575 additions and 10807 deletions

View file

@ -64,7 +64,7 @@ dependencies = [
"tracing-subscriber",
"tracing-tree",
"walkdir",
"windows",
"windows 0.57.0",
"xz2",
]
@ -158,12 +158,6 @@ dependencies = [
"cc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
version = "0.2.15"
@ -440,6 +434,25 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "objc2-core-foundation"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
dependencies = [
"bitflags",
]
[[package]]
name = "objc2-io-kit"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a"
dependencies = [
"libc",
"objc2-core-foundation",
]
[[package]]
name = "object"
version = "0.36.5"
@ -700,15 +713,16 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.33.0"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "948512566b1895f93b1592c7574baeb2de842f224f2aab158799ecadb8ebbb46"
checksum = "b897c8ea620e181c7955369a31be5f48d9a9121cb59fd33ecef9ff2a34323422"
dependencies = [
"core-foundation-sys",
"libc",
"memchr",
"ntapi",
"windows",
"objc2-core-foundation",
"objc2-io-kit",
"windows 0.61.1",
]
[[package]]
@ -927,22 +941,67 @@ version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
dependencies = [
"windows-core",
"windows-core 0.57.0",
"windows-targets",
]
[[package]]
name = "windows"
version = "0.61.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
dependencies = [
"windows-collections",
"windows-core 0.61.0",
"windows-future",
"windows-link",
"windows-numerics",
]
[[package]]
name = "windows-collections"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core 0.61.0",
]
[[package]]
name = "windows-core"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-implement 0.57.0",
"windows-interface 0.57.0",
"windows-result 0.1.2",
"windows-targets",
]
[[package]]
name = "windows-core"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement 0.60.0",
"windows-interface 0.59.1",
"windows-link",
"windows-result 0.3.2",
"windows-strings",
]
[[package]]
name = "windows-future"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
dependencies = [
"windows-core 0.61.0",
"windows-link",
]
[[package]]
name = "windows-implement"
version = "0.57.0"
@ -954,6 +1013,17 @@ dependencies = [
"syn",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.57.0"
@ -965,6 +1035,33 @@ dependencies = [
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-numerics"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core 0.61.0",
"windows-link",
]
[[package]]
name = "windows-result"
version = "0.1.2"
@ -974,6 +1071,24 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "windows-result"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.52.0"

View file

@ -58,7 +58,7 @@ walkdir = "2.4"
xz2 = "0.1"
# Dependencies needed by the build-metrics feature
sysinfo = { version = "0.33.0", default-features = false, optional = true, features = ["system"] }
sysinfo = { version = "0.35.0", default-features = false, optional = true, features = ["system"] }
# Dependencies needed by the `tracing` feature
tracing = { version = "0.1", optional = true, features = ["attributes"] }

View file

@ -163,7 +163,7 @@ fn check_version(config: &Config) -> Option<String> {
msg.push_str("WARNING: The `change-id` is missing in the `bootstrap.toml`. This means that you will not be able to track the major changes made to the bootstrap configurations.\n");
msg.push_str("NOTE: to silence this warning, ");
msg.push_str(&format!(
"add `change-id = {latest_change_id}` or change-id = \"ignore\" at the top of `bootstrap.toml`"
"add `change-id = {latest_change_id}` or `change-id = \"ignore\"` at the top of `bootstrap.toml`"
));
return Some(msg);
}
@ -195,7 +195,7 @@ fn check_version(config: &Config) -> Option<String> {
msg.push_str("NOTE: to silence this warning, ");
msg.push_str(&format!(
"update `bootstrap.toml` to use `change-id = {latest_change_id}` or change-id = \"ignore\" instead"
"update `bootstrap.toml` to use `change-id = {latest_change_id}` or `change-id = \"ignore\"` instead"
));
if io::stdout().is_terminal() {

View file

@ -527,3 +527,70 @@ tool_check_step!(Bootstrap { path: "src/bootstrap", default: false });
// `run-make-support` will be built as part of suitable run-make compiletest test steps, but support
// check to make it easier to work on.
tool_check_step!(RunMakeSupport { path: "src/tools/run-make-support", default: false });
/// Check step for the `coverage-dump` bootstrap tool. The coverage-dump tool
/// is used internally by coverage tests.
///
/// FIXME(Zalathar): This is temporarily separate from the other tool check
/// steps so that it can use the stage 0 compiler instead of `top_stage`,
/// without introducing conflicts with the stage 0 redesign (#119899).
///
/// After the stage 0 redesign lands, we can look into using the stage 0
/// compiler to check all bootstrap tools (#139170).
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct CoverageDump;
impl CoverageDump {
const PATH: &str = "src/tools/coverage-dump";
}
impl Step for CoverageDump {
type Output = ();
/// Most contributors won't care about coverage-dump, so don't make their
/// check builds slower unless they opt in and check it explicitly.
const DEFAULT: bool = false;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path(Self::PATH)
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Self {});
}
fn run(self, builder: &Builder<'_>) -> Self::Output {
// Make sure we haven't forgotten any fields, if there are any.
let Self {} = self;
let display_name = "coverage-dump";
let host = builder.config.build;
let target = host;
let mode = Mode::ToolBootstrap;
let compiler = builder.compiler(0, host);
let cargo = prepare_tool_cargo(
builder,
compiler,
mode,
target,
builder.kind,
Self::PATH,
SourceType::InTree,
&[],
);
let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, target))
.with_prefix(&format!("{display_name}-check"));
let _guard = builder.msg_tool(
builder.kind,
mode,
display_name,
compiler.stage,
&compiler.host,
&target,
);
run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
}
}

View file

@ -9,7 +9,7 @@ use std::sync::mpsc::SyncSender;
use build_helper::git::get_git_modified_files;
use ignore::WalkBuilder;
use crate::core::builder::Builder;
use crate::core::builder::{Builder, Kind};
use crate::utils::build_stamp::BuildStamp;
use crate::utils::exec::command;
use crate::utils::helpers::{self, t};
@ -122,6 +122,12 @@ fn print_paths(verb: &str, adjective: Option<&str>, paths: &[String]) {
}
pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) {
if build.kind == Kind::Format && build.top_stage != 0 {
eprintln!("ERROR: `x fmt` only supports stage 0.");
eprintln!("HELP: Use `x run rustfmt` to run in-tree rustfmt.");
crate::exit!(1);
}
if !paths.is_empty() {
eprintln!(
"fmt error: path arguments are no longer accepted; use `--all` to format everything"

View file

@ -392,3 +392,84 @@ impl Step for CyclicStep {
builder.ensure(CyclicStep { n: self.n.saturating_sub(1) })
}
}
/// Step to manually run the coverage-dump tool (`./x run coverage-dump`).
///
/// The coverage-dump tool is an internal detail of coverage tests, so this run
/// step is only needed when testing coverage-dump manually.
#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
pub struct CoverageDump;
impl Step for CoverageDump {
type Output = ();
const DEFAULT: bool = false;
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/coverage-dump")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Self {});
}
fn run(self, builder: &Builder<'_>) {
let mut cmd = builder.tool_cmd(Tool::CoverageDump);
cmd.args(&builder.config.free_args);
cmd.run(builder);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Rustfmt;
impl Step for Rustfmt {
type Output = ();
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.path("src/tools/rustfmt")
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Rustfmt);
}
fn run(self, builder: &Builder<'_>) {
let host = builder.build.build;
// `x run` uses stage 0 by default but rustfmt does not work well with stage 0.
// Change the stage to 1 if it's not set explicitly.
let stage = if builder.config.is_explicit_stage() || builder.top_stage >= 1 {
builder.top_stage
} else {
1
};
if stage == 0 {
eprintln!("rustfmt cannot be run at stage 0");
eprintln!("HELP: Use `x fmt` to use stage 0 rustfmt.");
std::process::exit(1);
}
let compiler = builder.compiler(stage, host);
let rustfmt_build = builder.ensure(tool::Rustfmt { compiler, target: host });
let mut rustfmt = tool::prepare_tool_cargo(
builder,
rustfmt_build.build_compiler,
Mode::ToolRustc,
host,
Kind::Run,
"src/tools/rustfmt",
SourceType::InTree,
&[],
);
rustfmt.args(["--bin", "rustfmt", "--"]);
rustfmt.args(builder.config.args());
rustfmt.into_cmd().run(builder);
}
}

View file

@ -54,6 +54,7 @@ impl Step for CrateBootstrap {
run.path("src/tools/jsondoclint")
.path("src/tools/suggest-tests")
.path("src/tools/replace-version-placeholder")
.path("src/tools/coverage-dump")
// We want `./x test tidy` to _run_ the tidy tool, not its tests.
// So we need a separate alias to test the tidy tool itself.
.alias("tidyselftest")

View file

@ -112,9 +112,8 @@ impl Cargo {
let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind);
match cmd_kind {
// No need to configure the target linker for these command types,
// as they don't invoke rustc at all.
Kind::Clean | Kind::Suggest | Kind::Format | Kind::Setup => {}
// No need to configure the target linker for these command types.
Kind::Clean | Kind::Check | Kind::Suggest | Kind::Format | Kind::Setup => {}
_ => {
cargo.configure_linker(builder);
}
@ -205,6 +204,8 @@ impl Cargo {
self
}
// FIXME(onur-ozkan): Add coverage to make sure modifications to this function
// doesn't cause cache invalidations (e.g., #130108).
fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo {
let target = self.target;
let compiler = self.compiler;

View file

@ -961,6 +961,7 @@ impl<'a> Builder<'a> {
check::RunMakeSupport,
check::Compiletest,
check::FeaturesStatusDump,
check::CoverageDump,
),
Kind::Test => describe!(
crate::core::build_steps::toolstate::ToolStateCheck,
@ -1114,6 +1115,8 @@ impl<'a> Builder<'a> {
run::UnicodeTableGenerator,
run::FeaturesStatusDump,
run::CyclicStep,
run::CoverageDump,
run::Rustfmt,
),
Kind::Setup => {
describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor)
@ -1534,7 +1537,7 @@ impl<'a> Builder<'a> {
let out = step.clone().run(self);
let dur = start.elapsed();
let deps = self.time_spent_on_dependencies.replace(parent + dur);
(out, dur - deps)
(out, dur.saturating_sub(deps))
};
if self.config.print_step_timings && !self.config.dry_run() {

View file

@ -53,6 +53,7 @@ use tracing::{instrument, span};
pub use utils::change_tracker::{
CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes,
};
pub use utils::helpers::PanicTracker;
use crate::core::build_steps::vendor::VENDOR_DIR;

View file

@ -96,6 +96,7 @@ pub fn find(build: &Build) {
let targets: HashSet<_> = match build.config.cmd {
// We don't need to check cross targets for these commands.
crate::Subcommand::Clean { .. }
| crate::Subcommand::Check { .. }
| crate::Subcommand::Suggest { .. }
| crate::Subcommand::Format { .. }
| crate::Subcommand::Setup { .. } => {

View file

@ -181,7 +181,7 @@ fn test_language_clang() {
#[test]
fn test_new_cc_build() {
let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
let cfg = new_cc_build(&build, target.clone());
let compiler = cfg.get_compiler();
@ -190,7 +190,7 @@ fn test_new_cc_build() {
#[test]
fn test_default_compiler_wasi() {
let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
let target = TargetSelection::from_user("wasm32-wasi");
let wasi_sdk = PathBuf::from("/wasi-sdk");
// SAFETY: bootstrap tests run on a single thread
@ -215,7 +215,7 @@ fn test_default_compiler_wasi() {
#[test]
fn test_default_compiler_fallback() {
let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
let build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
let mut cfg = cc::Build::new();
let result = default_compiler(&mut cfg, Language::C, target, &build);
@ -224,7 +224,7 @@ fn test_default_compiler_fallback() {
#[test]
fn test_find_target_with_config() {
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
let mut target_config = Target::default();
target_config.cc = Some(PathBuf::from("dummy-cc"));
@ -249,7 +249,7 @@ fn test_find_target_with_config() {
#[test]
fn test_find_target_without_config() {
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
build.config.target_config.clear();
find_target(&build, target.clone());
@ -262,7 +262,7 @@ fn test_find_target_without_config() {
#[test]
fn test_find() {
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["build".to_owned()])) });
let target1 = TargetSelection::from_user("x86_64-unknown-linux-gnu");
let target2 = TargetSelection::from_user("x86_64-unknown-openbsd");
build.targets.push(target1.clone());

View file

@ -406,4 +406,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
severity: ChangeSeverity::Info,
summary: "Added a new option `rust.debug-assertions-tools` to control debug asssertions for tools.",
},
ChangeInfo {
change_id: 140732,
severity: ChangeSeverity::Info,
summary: "`./x run` now supports running in-tree `rustfmt`, e.g., `./x run rustfmt -- --check /path/to/file.rs`.",
},
];

View file

@ -7,8 +7,9 @@ use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::OnceLock;
use std::thread::panicking;
use std::time::{Instant, SystemTime, UNIX_EPOCH};
use std::{env, fs, io, str};
use std::{env, fs, io, panic, str};
use build_helper::util::fail;
use object::read::archive::ArchiveFile;
@ -22,6 +23,23 @@ pub use crate::utils::shared_helpers::{dylib_path, dylib_path_var};
#[cfg(test)]
mod tests;
/// A wrapper around `std::panic::Location` used to track the location of panics
/// triggered by `t` macro usage.
pub struct PanicTracker<'a>(pub &'a panic::Location<'a>);
impl Drop for PanicTracker<'_> {
fn drop(&mut self) {
if panicking() {
eprintln!(
"Panic was initiated from {}:{}:{}",
self.0.file(),
self.0.line(),
self.0.column()
);
}
}
}
/// A helper macro to `unwrap` a result except also print out details like:
///
/// * The file/line of the panic
@ -32,19 +50,21 @@ mod tests;
/// using a `Result` with `try!`, but this may change one day...
#[macro_export]
macro_rules! t {
($e:expr) => {
($e:expr) => {{
let _panic_guard = $crate::PanicTracker(std::panic::Location::caller());
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
}
};
}};
// it can show extra info in the second parameter
($e:expr, $extra:expr) => {
($e:expr, $extra:expr) => {{
let _panic_guard = $crate::PanicTracker(std::panic::Location::caller());
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {} ({:?})", stringify!($e), e, $extra),
}
};
}};
}
pub use t;

@ -1 +1 @@
Subproject commit 3bf3402aea982b876eb56c87da17b0685c6461d5
Subproject commit 387392674d74656f7cb437c05a96f0c52ea8e601

@ -1 +1 @@
Subproject commit 0d7964d5b22cf920237ef1282d869564b4883b88
Subproject commit 8a8918c698534547fa8a1a693cb3e7277f0bfb2f

View file

@ -148,8 +148,7 @@ whereas this code uses [`ResultsCursor`]:
```rust,ignore
let mut results = MyAnalysis::new()
.into_engine(tcx, body, def_id)
.iterate_to_fixpoint()
.iterate_to_fixpoint(tcx, body, None);
.into_results_cursor(body);
// Inspect the fixpoint state immediately before each `Drop` terminator.

View file

@ -135,12 +135,16 @@ There are several use-cases for try builds:
- Run a specific CI job (e.g. Windows tests) on a PR, to quickly test if it
passes the test suite executed by that job.
You can select which CI jobs will
be executed in the try build by adding lines containing `try-job:
<job pattern>` to the PR description. All such specified jobs will be executed
in the try build once the `@bors try` command is used on the PR. If no try
jobs are specified in this way, the jobs defined in the `try` section of
[`jobs.yml`] will be executed by default.
By default, if you send a comment with `@bors try`, the jobs defined in the `try` section of
[`jobs.yml`] will be executed. We call this mode a "fast try build". Such a try build
will not execute any tests, and it will allow compilation warnings. It is useful when you want to
get an optimized toolchain as fast as possible, for a crater run or performance benchmarks,
even if it might not be working fully correctly.
If you want to run a custom CI job in a try build and make sure that it passes all tests and does
not produce any compilation warnings, you can select CI jobs to be executed by adding lines
containing `try-job: <job pattern>` to the PR description. All such specified jobs will be executed
in the try build once the `@bors try` command is used on the PR.
Each pattern can either be an exact name of a job or a glob pattern that matches multiple jobs,
for example `*msvc*` or `*-alt`. You can start at most 20 jobs in a single try build. When using

View file

@ -344,8 +344,7 @@ For checking runtime output, `//@ check-run-results` may be preferable.
Only use `error-pattern` if none of the above works.
Line annotations `//~` are still checked in tests using `error-pattern`.
In exceptional cases, use `//@ compile-flags: --error-format=human` to opt out of these checks.
Line annotations `//~` and `error-pattern` are compatible and can be used in the same test.
### Diagnostic kinds (error levels)
@ -356,9 +355,12 @@ The diagnostic kinds that you can have are:
- `NOTE`
- `HELP`
- `SUGGESTION`
- `RAW`
The `SUGGESTION` kind is used for specifying what the expected replacement text
should be for a diagnostic suggestion.
The `RAW` kind can be used for matching on lines from non-structured output sometimes emitted
by the compiler instead of or in addition to structured json.
`ERROR` and `WARN` kinds are required to be exhaustively covered by line annotations
`//~` by default.

View file

@ -58,6 +58,7 @@
- [thumbv7m-none-eabi](./platform-support/thumbv7m-none-eabi.md)
- [thumbv8m.base-none-eabi](./platform-support/thumbv8m.base-none-eabi.md)
- [thumbv8m.main-none-eabi\*](./platform-support/thumbv8m.main-none-eabi.md)
- [armv5te-unknown-linux-gnueabi](platform-support/armv5te-unknown-linux-gnueabi.md)
- [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md)
- [armv7-rtems-eabihf](platform-support/armv7-rtems-eabihf.md)
- [armv7-sony-vita-newlibeabihf](platform-support/armv7-sony-vita-newlibeabihf.md)

View file

@ -156,7 +156,7 @@ target | std | notes
[`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ✓ | Arm64EC Windows MSVC
[`armebv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian
[`armebv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian, hardfloat
`armv5te-unknown-linux-gnueabi` | ✓ | Armv5TE Linux (kernel 4.4, glibc 2.23)
[`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4, glibc 2.23)
`armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3
[`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android
`armv7-unknown-linux-gnueabi` | ✓ | Armv7-A Linux (kernel 4.15, glibc 2.27)

View file

@ -0,0 +1,29 @@
# `armv5te-unknown-linux-gnueabi`
**Tier: 2**
This target supports Linux programs with glibc on ARMv5TE CPUs without
floating-point units.
## Target maintainers
[@koalatux](https://github.com/koalatux)
## Requirements
The target is for cross-compilation only. Host tools are not supported.
std is fully supported.
## Building the target
Because this target is tier 2, artifacts are available from rustup.
## Building Rust programs
For building rust programs, you might want to specify GCC as linker in
`.cargo/config.toml` as follows:
```toml
[target.armv5te-unknown-linux-gnueabi]
linker = "arm-linux-gnueabi-gcc"
```

View file

@ -8,7 +8,6 @@ RISC Zero's Zero Knowledge Virtual Machine (zkVM) implementing the RV32IM instru
[@flaub](https://github.com/flaub)
[@jbruestle](https://github.com/jbruestle)
[@SchmErik](https://github.com/SchmErik)
## Background

View file

@ -26,6 +26,10 @@ edition).
Not all Rust editions have corresponding changes to the Rust style. For
instance, Rust 2015, Rust 2018, and Rust 2021 all use the same style edition.
## Rust next style edition
- Never break within a nullary function call `func()` or a unit literal `()`.
## Rust 2024 style edition
This style guide describes the Rust 2024 style edition. The Rust 2024 style

View file

@ -183,6 +183,10 @@ let f = Foo {
};
```
## Unit literals
Never break between the opening and closing parentheses of the `()` unit literal.
## Tuple literals
Use a single-line form where possible. Do not put spaces between the opening
@ -377,6 +381,11 @@ Do put a space between an argument, and the comma which precedes it.
Prefer not to break a line in the callee expression.
For a function call with no arguments (a nullary function call like `func()`),
never break within the parentheses, and never put a space between the
parentheses. Always write a nullary function call as a single-line call, never
a multi-line call.
### Single-line calls
Do not put a space between the function name and open paren, between the open

View file

@ -5,15 +5,3 @@ This chapter documents style and formatting for nightly-only syntax. The rest of
Style and formatting for nightly-only syntax should be removed from this chapter and integrated into the appropriate sections of the style guide at the time of stabilization.
There is no guarantee of the stability of this chapter in contrast to the rest of the style guide. Refer to the style team policy for nightly formatting procedure regarding breaking changes to this chapter.
### `feature(precise_capturing)`
A `use<'a, T>` precise capturing bound is formatted as if it were a single path segment with non-turbofished angle-bracketed args, like a trait bound whose identifier is `use`.
```
fn foo() -> impl Sized + use<'a> {}
// is formatted analogously to:
fn foo() -> impl Sized + Use<'a> {}
```

View file

@ -59,3 +59,15 @@ Box<
+ Debug
>
```
## Precise capturing bounds
A `use<'a, T>` precise capturing bound is formatted as if it were a single path segment with non-turbofished angle-bracketed args, like a trait bound whose identifier is `use`.
```rust
fn foo() -> impl Sized + use<'a> {}
// is formatted analogously to:
fn foo() -> impl Sized + Use<'a> {}
```

View file

@ -2,7 +2,7 @@
The tracking issue for this feature is: [#44874]
[#38788]: https://github.com/rust-lang/rust/issues/44874
[#44874]: https://github.com/rust-lang/rust/issues/44874
------------------------

View file

@ -60,20 +60,30 @@ Like [`box_patterns`], deref patterns may move out of boxes:
# #![feature(deref_patterns)]
# #![allow(incomplete_features)]
struct NoCopy;
// Match exhaustiveness analysis is not yet implemented.
let deref!(x) = Box::new(NoCopy) else { unreachable!() };
let deref!(x) = Box::new(NoCopy);
drop::<NoCopy>(x);
```
Additionally, when `deref_patterns` is enabled, string literal patterns may be written where `str`
is expected. Likewise, byte string literal patterns may be written where `[u8]` or `[u8; _]` is
expected. This lets them be used in `deref!(_)` patterns:
Additionally, `deref_patterns` implements changes to string and byte string literal patterns,
allowing then to be used in deref patterns:
```rust
# #![feature(deref_patterns)]
# #![allow(incomplete_features)]
match ("test".to_string(), b"test".to_vec()) {
(deref!("test"), deref!(b"test")) => {}
match ("test".to_string(), Box::from("test"), b"test".to_vec()) {
("test", "test", b"test") => {}
_ => panic!(),
}
// This works through multiple layers of reference and smart pointer:
match (&Box::new(&"test".to_string()), &&&"test") {
("test", "test") => {}
_ => panic!(),
}
// `deref!("...")` syntax may also be used:
match "test".to_string() {
deref!("test") => {}
_ => panic!(),
}
@ -82,10 +92,16 @@ match *"test" {
"test" => {}
_ => panic!(),
}
match *b"test" {
b"test" => {}
_ => panic!(),
}
match *(b"test" as &[u8]) {
b"test" => {}
_ => panic!(),
}
```
Implicit deref pattern syntax is not yet supported for string or byte string literals.
[`box_patterns`]: ./box-patterns.md
[`string_deref_patterns`]: ./string-deref-patterns.md
[smart pointers in the standard library]: https://doc.rust-lang.org/std/ops/trait.DerefPure.html#implementors

View file

@ -6,4 +6,4 @@ The tracking issue for this feature is: [#116909]
---
Enable the `f128` type for IEEE 128-bit floating numbers (quad precision).
Enable the `f128` type for IEEE 128-bit floating numbers (quad precision).

View file

@ -6,4 +6,4 @@ The tracking issue for this feature is: [#116909]
---
Enable the `f16` type for IEEE 16-bit floating numbers (half precision).
Enable the `f16` type for IEEE 16-bit floating numbers (half precision).

View file

@ -0,0 +1,25 @@
# `frontmatter`
The tracking issue for this feature is: [#136889]
------
The `frontmatter` feature allows an extra metadata block at the top of files for consumption by
external tools. For example, it can be used by [`cargo-script`] files to specify dependencies.
```rust
#!/usr/bin/env -S cargo -Zscript
---
[dependencies]
libc = "0.2.172"
---
#![feature(frontmatter)]
# mod libc { pub type c_int = i32; }
fn main() {
let x: libc::c_int = 1i32;
}
```
[#136889]: https://github.com/rust-lang/rust/issues/136889
[`cargo-script`]: https://rust-lang.github.io/rfcs/3502-cargo-script.html

View file

@ -14,7 +14,6 @@ base64 = "0.21.7"
itertools = "0.12"
indexmap = "2"
minifier = { version = "0.3.5", default-features = false }
pulldown-cmark-old = { version = "0.9.6", package = "pulldown-cmark", default-features = false }
pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] }
regex = "1"
rustdoc-json-types = { path = "../rustdoc-json-types" }

View file

@ -167,7 +167,7 @@ fn print_tts(printer: &mut Printer<'_>, tts: &TokenStream) {
}
fn usually_needs_space_between_keyword_and_open_delim(symbol: Symbol, span: Span) -> bool {
let ident = Ident { name: symbol, span };
let ident = Ident::new(symbol, span);
let is_keyword = ident.is_used_keyword() || ident.is_unused_keyword();
if !is_keyword {
// An identifier that is not a keyword usually does not need a space

View file

@ -786,7 +786,11 @@ impl Item {
// because it isn't public API.
None
}
_ => Some(rustc_hir_pretty::attribute_to_string(&tcx, attr)),
_ => Some({
let mut s = rustc_hir_pretty::attribute_to_string(&tcx, attr);
assert_eq!(s.pop(), Some('\n'));
s
}),
}
} else if attr.has_any_name(ALLOWED_ATTRIBUTES) {
Some(

View file

@ -262,11 +262,21 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
Ok(None) => return,
Err(error) => {
eprintln!("{error}");
// Since some files in the temporary folder are still owned and alive, we need
// to manually remove the folder.
let _ = std::fs::remove_dir_all(temp_dir.path());
std::process::exit(1);
}
};
run_tests(opts, &rustdoc_options, &unused_extern_reports, standalone_tests, mergeable_tests);
run_tests(
opts,
&rustdoc_options,
&unused_extern_reports,
standalone_tests,
mergeable_tests,
Some(temp_dir),
);
let compiling_test_count = compiling_test_count.load(Ordering::SeqCst);
@ -316,6 +326,8 @@ pub(crate) fn run_tests(
unused_extern_reports: &Arc<Mutex<Vec<UnusedExterns>>>,
mut standalone_tests: Vec<test::TestDescAndFn>,
mergeable_tests: FxIndexMap<Edition, Vec<(DocTestBuilder, ScrapedDocTest)>>,
// We pass this argument so we can drop it manually before using `exit`.
mut temp_dir: Option<TempDir>,
) {
let mut test_args = Vec::with_capacity(rustdoc_options.test_args.len() + 1);
test_args.insert(0, "rustdoctest".to_string());
@ -382,9 +394,14 @@ pub(crate) fn run_tests(
// `running 0 tests...`.
if ran_edition_tests == 0 || !standalone_tests.is_empty() {
standalone_tests.sort_by(|a, b| a.desc.name.as_slice().cmp(b.desc.name.as_slice()));
test::test_main(&test_args, standalone_tests, None);
test::test_main_with_exit_callback(&test_args, standalone_tests, None, || {
// We ensure temp dir destructor is called.
std::mem::drop(temp_dir.take());
});
}
if nb_errors != 0 {
// We ensure temp dir destructor is called.
std::mem::drop(temp_dir);
// libtest::ERROR_EXIT_CODE is not public but it's the same value.
std::process::exit(101);
}
@ -450,7 +467,7 @@ enum TestFailure {
}
enum DirState {
Temp(tempfile::TempDir),
Temp(TempDir),
Perm(PathBuf),
}

View file

@ -116,6 +116,7 @@ pub(crate) fn test(input: &Input, options: Options) -> Result<(), String> {
&Arc::new(Mutex::new(Vec::new())),
standalone_tests,
mergeable_tests,
None,
);
Ok(())
}

View file

@ -9,7 +9,7 @@ use std::collections::VecDeque;
use std::fmt::{Display, Write};
use rustc_data_structures::fx::FxIndexMap;
use rustc_lexer::{Cursor, LiteralKind, TokenKind};
use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind};
use rustc_span::edition::Edition;
use rustc_span::symbol::Symbol;
use rustc_span::{BytePos, DUMMY_SP, Span};
@ -638,7 +638,8 @@ impl<'src> Classifier<'src> {
/// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
/// file span which will be used later on by the `span_correspondence_map`.
fn new(src: &'src str, file_span: Span, decoration_info: Option<&DecorationInfo>) -> Self {
let tokens = PeekIter::new(TokenIter { src, cursor: Cursor::new(src) });
let tokens =
PeekIter::new(TokenIter { src, cursor: Cursor::new(src, FrontmatterAllowed::Yes) });
let decorations = decoration_info.map(Decorations::new);
Classifier {
tokens,
@ -884,6 +885,7 @@ impl<'src> Classifier<'src> {
| TokenKind::At
| TokenKind::Tilde
| TokenKind::Colon
| TokenKind::Frontmatter { .. }
| TokenKind::Unknown => return no_highlight(sink),
TokenKind::Question => Class::QuestionMark,

View file

@ -55,6 +55,9 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\
--collapse-arrow-image: url('data:image/svg+xml,<svg width="16" height="16" viewBox="0 0 16 16" \
enable-background="new 0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="none" \
d="M3,8l4,4l4,-4m-4,4M3,4l4,4l4,-4" stroke="black" stroke-width="2"/></svg>');
--hamburger-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
viewBox="0 0 22 22" fill="none" stroke="black">\
<path d="M3,5h16M3,11h16M3,17h16" stroke-width="2.75"/></svg>');
}
:root.sans-serif {
@ -2001,9 +2004,11 @@ a.tooltip:hover::after {
display: flex;
margin-right: 4px;
position: fixed;
left: 6px;
height: 34px;
width: 34px;
}
.hide-sidebar #sidebar-button {
left: 6px;
background-color: var(--main-background-color);
z-index: 1;
}
@ -2019,6 +2024,8 @@ a.tooltip:hover::after {
align-items: center;
justify-content: center;
flex-direction: column;
}
#settings-menu > a, #help-button > a, button#toggle-all-docs {
border: 1px solid transparent;
border-radius: var(--button-border-radius);
color: var(--main-color);
@ -2031,14 +2038,15 @@ a.tooltip:hover::after {
min-width: 0;
}
#sidebar-button > a {
background-color: var(--button-background-color);
border-color: var(--border-color);
background-color: var(--sidebar-background-color);
width: 33px;
}
#sidebar-button > a:hover, #sidebar-button > a:focus-visible {
background-color: var(--main-background-color);
}
#settings-menu > a:hover, #settings-menu > a:focus-visible,
#help-button > a:hover, #help-button > a:focus-visible,
#sidebar-button > a:hover, #sidebar-button > a:focus-visible,
button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible {
border-color: var(--settings-button-border-focus);
text-decoration: none;
@ -2405,10 +2413,9 @@ However, it's not needed with smaller screen width because the doc/code block is
use hamburger button */
.src #sidebar-button > a::before, .sidebar-menu-toggle::before {
/* hamburger button image */
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
viewBox="0 0 22 22" fill="none" stroke="black">\
<path d="M3,5h16M3,11h16M3,17h16" stroke-width="2.75"/></svg>');
content: var(--hamburger-image);
opacity: 0.75;
filter: var(--mobile-sidebar-menu-filter);
}
.sidebar-menu-toggle:hover::before,
.sidebar-menu-toggle:active::before,
@ -2416,17 +2423,6 @@ However, it's not needed with smaller screen width because the doc/code block is
opacity: 1;
}
/* src sidebar button opens a folder view */
.src #sidebar-button > a::before {
/* folder image */
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
viewBox="0 0 22 22" fill="none" stroke="black">\
<path d="M16,9v-4h-6v-1l-2,-2h-4l-2,2v16h13L21,9h-15L2,19" stroke-width="1.25"/>\
<path d="M15,7h-11v3" stroke-width="0.75"/>\
<path d="M3.75,10v1.25" stroke-width="0.375"/></svg>');
opacity: 0.75;
}
/* Media Queries */
/* Make sure all the buttons line wrap at the same time */
@ -2611,9 +2607,6 @@ in src-script.js and main.js
width: 22px;
height: 22px;
}
.sidebar-menu-toggle::before {
filter: var(--mobile-sidebar-menu-filter);
}
.sidebar-menu-toggle:hover {
background: var(--main-background-color);
}
@ -2671,6 +2664,14 @@ in src-script.js and main.js
margin: 0 0 -25px 0;
padding: var(--nav-sub-mobile-padding);
}
html:not(.src-sidebar-expanded) .src #sidebar-button > a {
background-color: var(--main-background-color);
}
html:not(.src-sidebar-expanded) .src #sidebar-button > a:hover,
html:not(.src-sidebar-expanded) .src #sidebar-button > a:focus-visible {
background-color: var(--sidebar-background-color);
}
}

View file

@ -170,12 +170,28 @@ pub fn main() {
// NOTE: this compiles both versions of tracing unconditionally, because
// - The compile time hit is not that bad, especially compared to rustdoc's incremental times, and
// - Otherwise, there's no warning that logging is being ignored when `download-rustc` is enabled
// NOTE: The reason this doesn't show double logging when `download-rustc = false` and
// `debug_logging = true` is because all rustc logging goes to its version of tracing (the one
// in the sysroot), and all of rustdoc's logging goes to its version (the one in Cargo.toml).
init_logging(&early_dcx);
rustc_driver::init_logger(&early_dcx, rustc_log::LoggerConfig::from_env("RUSTDOC_LOG"));
crate::init_logging(&early_dcx);
match rustc_log::init_logger(rustc_log::LoggerConfig::from_env("RUSTDOC_LOG")) {
Ok(()) => {}
// With `download-rustc = true` there are definitely 2 distinct tracing crates in the
// dependency graph: one in the downloaded sysroot and one built just now as a dependency of
// rustdoc. So the sysroot's tracing is definitely not yet initialized here.
//
// But otherwise, depending on link style, there may or may not be 2 tracing crates in play.
// The one we just initialized in `crate::init_logging` above is rustdoc's direct dependency
// on tracing. When rustdoc is built by x.py using Cargo, rustc_driver's and rustc_log's
// tracing dependency is distinct from this one and also needs to be initialized (using the
// same RUSTDOC_LOG environment variable for both). Other build systems may use just a
// single tracing crate throughout the rustc and rustdoc build.
//
// The reason initializing 2 tracings does not show double logging when `download-rustc =
// false` and `debug_logging = true` is because all rustc logging goes only to its version
// of tracing (the one in the sysroot) and all of rustdoc's logging only goes to its version
// (the one in Cargo.toml).
Err(rustc_log::Error::AlreadyInit(_)) => {}
Err(error) => early_dcx.early_fatal(error.to_string()),
}
let exit_code = rustc_driver::catch_with_exit_code(|| {
let at_args = rustc_driver::args::raw_args(&early_dcx);

View file

@ -196,14 +196,6 @@ declare_rustdoc_lint! {
"detects redundant explicit links in doc comments"
}
declare_rustdoc_lint! {
/// This compatibility lint checks for Markdown syntax that works in the old engine but not
/// the new one.
UNPORTABLE_MARKDOWN,
Warn,
"detects markdown that is interpreted differently in different parser"
}
pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
vec![
BROKEN_INTRA_DOC_LINKS,
@ -217,7 +209,6 @@ pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
MISSING_CRATE_LEVEL_DOCS,
UNESCAPED_BACKTICKS,
REDUNDANT_EXPLICIT_LINKS,
UNPORTABLE_MARKDOWN,
]
});
@ -241,4 +232,5 @@ pub(crate) fn register_lints(_sess: &Session, lint_store: &mut LintStore) {
.register_renamed("intra_doc_link_resolution_failure", "rustdoc::broken_intra_doc_links");
lint_store.register_renamed("non_autolinks", "rustdoc::bare_urls");
lint_store.register_renamed("rustdoc::non_autolinks", "rustdoc::bare_urls");
lint_store.register_removed("rustdoc::unportable_markdown", "old parser removed");
}

View file

@ -496,12 +496,21 @@ impl<'tcx> LinkCollector<'_, 'tcx> {
// Try looking for methods and associated items.
// NB: `path_root` could be empty when resolving in the root namespace (e.g. `::std`).
let (path_root, item_str) = path_str.rsplit_once("::").ok_or_else(|| {
// If there's no `::`, it's not an associated item.
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
debug!("found no `::`, assuming {path_str} was correctly not in scope");
UnresolvedPath { item_id, module_id, partial_res: None, unresolved: path_str.into() }
})?;
let (path_root, item_str) = match path_str.rsplit_once("::") {
Some(res @ (_path_root, item_str)) if !item_str.is_empty() => res,
_ => {
// If there's no `::`, or the `::` is at the end (e.g. `String::`) it's not an
// associated item. So we can be sure that `rustc_resolve` was accurate when it
// said it wasn't resolved.
debug!("`::` missing or at end, assuming {path_str} was not in scope");
return Err(UnresolvedPath {
item_id,
module_id,
partial_res: None,
unresolved: path_str.into(),
});
}
};
let item_name = Symbol::intern(item_str);
// FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support

View file

@ -6,7 +6,6 @@ mod check_code_block_syntax;
mod html_tags;
mod redundant_explicit_links;
mod unescaped_backticks;
mod unportable_markdown;
use super::Pass;
use crate::clean::*;
@ -49,9 +48,6 @@ impl DocVisitor<'_> for Linter<'_, '_> {
}
if may_have_block_comment_or_html {
html_tags::visit_item(self.cx, item, hir_id, &dox);
unportable_markdown::visit_item(self.cx, item, hir_id, &dox);
} else if may_have_link {
unportable_markdown::visit_item(self.cx, item, hir_id, &dox);
}
}

View file

@ -1,145 +0,0 @@
//! Detects specific markdown syntax that's different between pulldown-cmark
//! 0.9 and 0.11.
//!
//! This is a mitigation for old parser bugs that affected some
//! real crates' docs. The old parser claimed to comply with CommonMark,
//! but it did not. These warnings will eventually be removed,
//! though some of them may become Clippy lints.
//!
//! <https://github.com/rust-lang/rust/pull/121659#issuecomment-1992752820>
//!
//! <https://rustc-dev-guide.rust-lang.org/bug-fix-procedure.html#add-the-lint-to-the-list-of-removed-lists>
use std::collections::{BTreeMap, BTreeSet};
use rustc_hir::HirId;
use rustc_lint_defs::Applicability;
use rustc_resolve::rustdoc::source_span_for_markdown_range;
use {pulldown_cmark as cmarkn, pulldown_cmark_old as cmarko};
use crate::clean::Item;
use crate::core::DocContext;
pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &str) {
let tcx = cx.tcx;
// P1: unintended strikethrough was fixed by requiring single-tildes to flank
// the same way underscores do, so nothing is done here
// P2: block quotes without following space parsed wrong
//
// This is the set of starting points for block quotes with no space after
// the `>`. It is populated by the new parser, and if the old parser fails to
// clear it out, it'll produce a warning.
let mut spaceless_block_quotes = BTreeSet::new();
// P3: missing footnote references
//
// This is populated by listening for FootnoteReference from
// the new parser and old parser.
let mut missing_footnote_references = BTreeMap::new();
let mut found_footnote_references = BTreeSet::new();
// populate problem cases from new parser
{
pub fn main_body_opts_new() -> cmarkn::Options {
cmarkn::Options::ENABLE_TABLES
| cmarkn::Options::ENABLE_FOOTNOTES
| cmarkn::Options::ENABLE_STRIKETHROUGH
| cmarkn::Options::ENABLE_TASKLISTS
| cmarkn::Options::ENABLE_SMART_PUNCTUATION
}
let parser_new = cmarkn::Parser::new_ext(dox, main_body_opts_new()).into_offset_iter();
for (event, span) in parser_new {
if let cmarkn::Event::Start(cmarkn::Tag::BlockQuote(_)) = event {
if !dox[span.clone()].starts_with("> ") {
spaceless_block_quotes.insert(span.start);
}
}
if let cmarkn::Event::FootnoteReference(_) = event {
found_footnote_references.insert(span.start + 1);
}
}
}
// remove cases where they don't actually differ
{
pub fn main_body_opts_old() -> cmarko::Options {
cmarko::Options::ENABLE_TABLES
| cmarko::Options::ENABLE_FOOTNOTES
| cmarko::Options::ENABLE_STRIKETHROUGH
| cmarko::Options::ENABLE_TASKLISTS
| cmarko::Options::ENABLE_SMART_PUNCTUATION
}
let parser_old = cmarko::Parser::new_ext(dox, main_body_opts_old()).into_offset_iter();
for (event, span) in parser_old {
if let cmarko::Event::Start(cmarko::Tag::BlockQuote) = event
&& !dox[span.clone()].starts_with("> ")
{
spaceless_block_quotes.remove(&span.start);
}
if let cmarko::Event::FootnoteReference(_) = event
&& !found_footnote_references.contains(&(span.start + 1))
{
missing_footnote_references.insert(span.start + 1, span);
}
}
}
for start in spaceless_block_quotes {
let (span, precise) =
source_span_for_markdown_range(tcx, dox, &(start..start + 1), &item.attrs.doc_strings)
.map(|span| (span, true))
.unwrap_or_else(|| (item.attr_span(tcx), false));
tcx.node_span_lint(crate::lint::UNPORTABLE_MARKDOWN, hir_id, span, |lint| {
lint.primary_message("unportable markdown");
lint.help("confusing block quote with no space after the `>` marker".to_string());
if precise {
lint.span_suggestion(
span.shrink_to_hi(),
"if the quote is intended, add a space",
" ",
Applicability::MaybeIncorrect,
);
lint.span_suggestion(
span.shrink_to_lo(),
"if it should not be a quote, escape it",
"\\",
Applicability::MaybeIncorrect,
);
}
});
}
for (_caret, span) in missing_footnote_references {
let (ref_span, precise) =
source_span_for_markdown_range(tcx, dox, &span, &item.attrs.doc_strings)
.map(|span| (span, true))
.unwrap_or_else(|| (item.attr_span(tcx), false));
tcx.node_span_lint(crate::lint::UNPORTABLE_MARKDOWN, hir_id, ref_span, |lint| {
lint.primary_message("unportable markdown");
if precise {
lint.span_suggestion(
ref_span.shrink_to_lo(),
"if it should not be a footnote, escape it",
"\\",
Applicability::MaybeIncorrect,
);
}
if dox.as_bytes().get(span.end) == Some(&b'[') {
lint.help("confusing footnote reference and link");
if precise {
lint.span_suggestion(
ref_span.shrink_to_hi(),
"if the footnote is intended, add a space",
" ",
Applicability::MaybeIncorrect,
);
} else {
lint.help("there should be a space between the link and the footnote");
}
}
});
}
}

@ -1 +1 @@
Subproject commit a9865ceca08101071e25f3bba97bba8bf0ea9719
Subproject commit 8448283b4bd34ea00d76fd4f18ec730b549d6e1d

View file

@ -263,7 +263,7 @@ impl<'tcx> NonCopyConst<'tcx> {
fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
let def_id = body_id.hir_id.owner.to_def_id();
let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id);
let instance = ty::Instance::new(def_id, args);
let instance = ty::Instance::new_raw(def_id, args);
let cid = GlobalId {
instance,
promoted: None,

View file

@ -154,7 +154,7 @@ impl LateLintPass<'_> for WildcardImports {
(span, false)
};
let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord();
let mut imports: Vec<_> = used_imports.iter().map(ToString::to_string).collect();
let imports_string = if imports.len() == 1 {
imports.pop().unwrap()
} else if braced_glob {

View file

@ -20,8 +20,8 @@ use rustc_middle::traits::EvaluationResult;
use rustc_middle::ty::layout::ValidityRequirement;
use rustc_middle::ty::{
self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
};
use rustc_span::symbol::Ident;
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
@ -915,7 +915,7 @@ pub fn for_each_top_level_late_bound_region<B>(
ControlFlow::Continue(())
}
}
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
self.index += 1;
let res = t.super_visit_with(self);
self.index -= 1;

View file

@ -84,6 +84,19 @@ fn issue_14139() {
}
fn drop_order() {
struct DropDeIterator(std::vec::IntoIter<S>);
impl Iterator for DropDeIterator {
type Item = S;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
impl DoubleEndedIterator for DropDeIterator {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back()
}
}
struct S(&'static str);
impl std::ops::Drop for S {
fn drop(&mut self) {
@ -92,7 +105,7 @@ fn drop_order() {
}
let v = vec![S("one"), S("two"), S("three")];
let mut v = v.into_iter();
let mut v = DropDeIterator(v.into_iter());
println!("Last element is {}", v.next_back().unwrap().0);
//~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
println!("Done");

View file

@ -84,6 +84,19 @@ fn issue_14139() {
}
fn drop_order() {
struct DropDeIterator(std::vec::IntoIter<S>);
impl Iterator for DropDeIterator {
type Item = S;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
impl DoubleEndedIterator for DropDeIterator {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back()
}
}
struct S(&'static str);
impl std::ops::Drop for S {
fn drop(&mut self) {
@ -92,7 +105,7 @@ fn drop_order() {
}
let v = vec![S("one"), S("two"), S("three")];
let v = v.into_iter();
let v = DropDeIterator(v.into_iter());
println!("Last element is {}", v.last().unwrap().0);
//~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
println!("Done");

View file

@ -18,7 +18,7 @@ LL | let _ = DeIterator.last();
| help: try: `next_back()`
error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
--> tests/ui/double_ended_iterator_last.rs:96:36
--> tests/ui/double_ended_iterator_last.rs:109:36
|
LL | println!("Last element is {}", v.last().unwrap().0);
| ^^^^^^^^
@ -26,7 +26,7 @@ LL | println!("Last element is {}", v.last().unwrap().0);
= note: this change will alter drop order which may be undesirable
help: try
|
LL ~ let mut v = v.into_iter();
LL ~ let mut v = DropDeIterator(v.into_iter());
LL ~ println!("Last element is {}", v.next_back().unwrap().0);
|

View file

@ -11,6 +11,19 @@ fn main() {
}
fn drop_order() {
struct DropDeIterator(std::vec::IntoIter<S>);
impl Iterator for DropDeIterator {
type Item = S;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
impl DoubleEndedIterator for DropDeIterator {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back()
}
}
struct S(&'static str);
impl std::ops::Drop for S {
fn drop(&mut self) {
@ -19,7 +32,7 @@ fn drop_order() {
}
let v = vec![S("one"), S("two"), S("three")];
let v = (v.into_iter(), 42);
let v = (DropDeIterator(v.into_iter()), 42);
println!("Last element is {}", v.0.last().unwrap().0);
//~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
println!("Done");

View file

@ -1,5 +1,5 @@
error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
--> tests/ui/double_ended_iterator_last_unfixable.rs:23:36
--> tests/ui/double_ended_iterator_last_unfixable.rs:36:36
|
LL | println!("Last element is {}", v.0.last().unwrap().0);
| ^^^^------
@ -8,7 +8,7 @@ LL | println!("Last element is {}", v.0.last().unwrap().0);
|
= note: this change will alter drop order which may be undesirable
note: this must be made mutable to use `.next_back()`
--> tests/ui/double_ended_iterator_last_unfixable.rs:23:36
--> tests/ui/double_ended_iterator_last_unfixable.rs:36:36
|
LL | println!("Last element is {}", v.0.last().unwrap().0);
| ^^^

View file

@ -16,7 +16,7 @@ use crate::fn_mod::foo;
//~^ wildcard_imports
use crate::mod_mod::inner_mod;
//~^ wildcard_imports
use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod};
use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod};
//~^ wildcard_imports
#[macro_use]
use crate::struct_mod::{A, inner_struct_mod};
@ -26,7 +26,7 @@ use crate::struct_mod::{A, inner_struct_mod};
use wildcard_imports_helper::inner::inner_for_self_import;
use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
//~^ wildcard_imports
use wildcard_imports_helper::{ExternA, extern_foo};
use wildcard_imports_helper::{extern_foo, ExternA};
//~^ wildcard_imports
use std::io::prelude::*;
@ -138,7 +138,7 @@ mod in_fn_test {
fn test_extern() {
use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo};
//~^ wildcard_imports
use wildcard_imports_helper::{ExternA, extern_foo};
use wildcard_imports_helper::{extern_foo, ExternA};
//~^ wildcard_imports
inner_for_self_import::inner_extern_foo();
@ -160,7 +160,7 @@ mod in_fn_test {
}
fn test_extern_reexported() {
use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported};
use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum};
//~^ wildcard_imports
extern_exported();
@ -190,7 +190,7 @@ mod in_fn_test {
}
fn test_reexported() {
use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported};
use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum};
//~^ wildcard_imports
exported();

View file

@ -17,7 +17,7 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports.rs:19:5
|
LL | use crate::multi_fn_mod::*;
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}`
error: usage of wildcard import
--> tests/ui/wildcard_imports.rs:22:5
@ -35,7 +35,7 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports.rs:29:5
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}`
error: usage of wildcard import
--> tests/ui/wildcard_imports.rs:100:13
@ -59,7 +59,7 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports.rs:141:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}`
error: usage of wildcard import
--> tests/ui/wildcard_imports.rs:154:20
@ -77,13 +77,13 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports.rs:163:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}`
error: usage of wildcard import
--> tests/ui/wildcard_imports.rs:193:9
|
LL | use crate::in_fn_test::*;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}`
error: usage of wildcard import
--> tests/ui/wildcard_imports.rs:203:9

View file

@ -14,7 +14,7 @@ use crate::fn_mod::foo;
//~^ wildcard_imports
use crate::mod_mod::inner_mod;
//~^ wildcard_imports
use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod};
use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod};
//~^ wildcard_imports
use crate::struct_mod::{A, inner_struct_mod};
//~^ wildcard_imports
@ -23,7 +23,7 @@ use crate::struct_mod::{A, inner_struct_mod};
use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
//~^ wildcard_imports
use wildcard_imports_helper::prelude::v1::*;
use wildcard_imports_helper::{ExternA, extern_foo};
use wildcard_imports_helper::{extern_foo, ExternA};
//~^ wildcard_imports
use std::io::prelude::*;
@ -132,7 +132,7 @@ mod in_fn_test {
fn test_extern() {
use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo};
//~^ wildcard_imports
use wildcard_imports_helper::{ExternA, extern_foo};
use wildcard_imports_helper::{extern_foo, ExternA};
//~^ wildcard_imports
inner_for_self_import::inner_extern_foo();
@ -154,7 +154,7 @@ mod in_fn_test {
}
fn test_extern_reexported() {
use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported};
use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum};
//~^ wildcard_imports
extern_exported();
@ -184,7 +184,7 @@ mod in_fn_test {
}
fn test_reexported() {
use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported};
use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum};
//~^ wildcard_imports
exported();

View file

@ -17,7 +17,7 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:17:5
|
LL | use crate::multi_fn_mod::*;
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}`
error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:19:5
@ -35,7 +35,7 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:26:5
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}`
error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:95:13
@ -59,7 +59,7 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:135:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}`
error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:148:20
@ -77,13 +77,13 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:157:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}`
error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:187:9
|
LL | use crate::in_fn_test::*;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}`
error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:197:9

View file

@ -14,7 +14,7 @@ use crate::fn_mod::foo;
//~^ wildcard_imports
use crate::mod_mod::inner_mod;
//~^ wildcard_imports
use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod};
use crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod};
//~^ wildcard_imports
use crate::struct_mod::{A, inner_struct_mod};
//~^ wildcard_imports
@ -23,7 +23,7 @@ use crate::struct_mod::{A, inner_struct_mod};
use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
//~^ wildcard_imports
use wildcard_imports_helper::prelude::v1::*;
use wildcard_imports_helper::{ExternA, extern_foo};
use wildcard_imports_helper::{extern_foo, ExternA};
//~^ wildcard_imports
use std::io::prelude::*;
@ -132,7 +132,7 @@ mod in_fn_test {
fn test_extern() {
use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo};
//~^ wildcard_imports
use wildcard_imports_helper::{ExternA, extern_foo};
use wildcard_imports_helper::{extern_foo, ExternA};
//~^ wildcard_imports
inner_for_self_import::inner_extern_foo();
@ -154,7 +154,7 @@ mod in_fn_test {
}
fn test_extern_reexported() {
use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported};
use wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum};
//~^ wildcard_imports
extern_exported();
@ -184,7 +184,7 @@ mod in_fn_test {
}
fn test_reexported() {
use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported};
use crate::in_fn_test::{exported, ExportedStruct, ExportedEnum};
//~^ wildcard_imports
exported();

View file

@ -17,7 +17,7 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:17:5
|
LL | use crate::multi_fn_mod::*;
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_foo, multi_bar, multi_inner_mod}`
error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:19:5
@ -35,7 +35,7 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:26:5
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}`
error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:95:13
@ -59,7 +59,7 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:135:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_foo, ExternA}`
error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:148:20
@ -77,13 +77,13 @@ error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:157:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{extern_exported, ExternExportedStruct, ExternExportedEnum}`
error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:187:9
|
LL | use crate::in_fn_test::*;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{exported, ExportedStruct, ExportedEnum}`
error: usage of wildcard import
--> tests/ui/wildcard_imports_2021.rs:197:9

View file

@ -35,6 +35,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"ignore-32bit",
"ignore-64bit",
"ignore-aarch64",
"ignore-aarch64-pc-windows-msvc",
"ignore-aarch64-unknown-linux-gnu",
"ignore-aix",
"ignore-android",

View file

@ -15,6 +15,7 @@ pub enum ErrorKind {
Note,
Suggestion,
Warning,
Raw,
}
impl ErrorKind {
@ -39,6 +40,7 @@ impl ErrorKind {
"NOTE" | "note" | "MONO_ITEM" => ErrorKind::Note,
"SUGGESTION" => ErrorKind::Suggestion,
"WARN" | "WARNING" | "warn" | "warning" => ErrorKind::Warning,
"RAW" => ErrorKind::Raw,
_ => panic!(
"unexpected diagnostic kind `{s}`, expected \
`ERROR`, `WARN`, `NOTE`, `HELP` or `SUGGESTION`"
@ -55,6 +57,7 @@ impl fmt::Display for ErrorKind {
ErrorKind::Note => write!(f, "NOTE"),
ErrorKind::Suggestion => write!(f, "SUGGESTION"),
ErrorKind::Warning => write!(f, "WARN"),
ErrorKind::Raw => write!(f, "RAW"),
}
}
}

View file

@ -7,7 +7,6 @@ use regex::Regex;
use serde::Deserialize;
use crate::errors::{Error, ErrorKind};
use crate::runtest::ProcRes;
#[derive(Deserialize)]
struct Diagnostic {
@ -140,28 +139,19 @@ pub fn extract_rendered(output: &str) -> String {
.collect()
}
pub fn parse_output(file_name: &str, output: &str, proc_res: &ProcRes) -> Vec<Error> {
pub fn parse_output(file_name: &str, output: &str) -> Vec<Error> {
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: {}",
error, line, output
)),
|| (),
);
}
}
}
// Compiler can emit non-json lines in non-`--error-format=json` modes,
// and in some situations even in json mode.
match serde_json::from_str::<Diagnostic>(line) {
Ok(diagnostic) => push_actual_errors(&mut errors, &diagnostic, &[], file_name),
Err(_) => errors.push(Error {
line_num: None,
kind: ErrorKind::Raw,
msg: line.to_string(),
require_annotation: false,
}),
}
}
errors
@ -181,8 +171,6 @@ fn push_actual_errors(
.filter(|(_, span)| Path::new(&span.file_name) == Path::new(&file_name))
.collect();
let spans_in_this_file: Vec<_> = spans_info_in_this_file.iter().map(|(_, span)| span).collect();
let primary_spans: Vec<_> = spans_info_in_this_file
.iter()
.filter(|(is_primary, _)| *is_primary)
@ -280,7 +268,9 @@ fn push_actual_errors(
line_num: Some(span.line_start + index),
kind: ErrorKind::Suggestion,
msg: line.to_string(),
require_annotation: true,
// Empty suggestions (suggestions to remove something) are common
// and annotating them in source is not useful.
require_annotation: !line.is_empty(),
});
}
}
@ -294,13 +284,16 @@ fn push_actual_errors(
}
// Add notes for any labels that appear in the message.
for span in spans_in_this_file.iter().filter(|span| span.label.is_some()) {
errors.push(Error {
line_num: Some(span.line_start),
kind: ErrorKind::Note,
msg: span.label.clone().unwrap(),
require_annotation: true,
});
for (_, span) in spans_info_in_this_file {
if let Some(label) = &span.label {
errors.push(Error {
line_num: Some(span.line_start),
kind: ErrorKind::Note,
msg: label.clone(),
// Empty labels (only underlining spans) are common and do not need annotations.
require_annotation: !label.is_empty(),
});
}
}
// Flatten out the children.

View file

@ -23,7 +23,7 @@ use crate::common::{
output_base_dir, output_base_name, output_testname_unique,
};
use crate::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff};
use crate::errors::{Error, ErrorKind};
use crate::errors::{Error, ErrorKind, load_errors};
use crate::header::TestProps;
use crate::read2::{Truncated, read2_abbreviated};
use crate::util::{Utf8PathBufExt, add_dylib_path, logv, static_regex};
@ -577,23 +577,9 @@ impl<'test> TestCx<'test> {
}
}
fn check_all_error_patterns(
&self,
output_to_check: &str,
proc_res: &ProcRes,
pm: Option<PassMode>,
) {
if self.props.error_patterns.is_empty() && self.props.regex_error_patterns.is_empty() {
if pm.is_some() {
// FIXME(#65865)
return;
} else {
self.fatal(&format!("no error pattern specified in {}", self.testpaths.file));
}
}
/// Check `error-pattern` and `regex-error-pattern` directives.
fn check_all_error_patterns(&self, output_to_check: &str, proc_res: &ProcRes) {
let mut missing_patterns: Vec<String> = Vec::new();
self.check_error_patterns(output_to_check, &mut missing_patterns);
self.check_regex_error_patterns(output_to_check, proc_res, &mut missing_patterns);
@ -670,7 +656,9 @@ impl<'test> TestCx<'test> {
}
}
fn check_expected_errors(&self, expected_errors: Vec<Error>, proc_res: &ProcRes) {
/// Check `//~ KIND message` annotations.
fn check_expected_errors(&self, proc_res: &ProcRes) {
let expected_errors = load_errors(&self.testpaths.file, self.revision);
debug!(
"check_expected_errors: expected_errors={:?} proc_res.status={:?}",
expected_errors, proc_res.status
@ -711,11 +699,24 @@ impl<'test> TestCx<'test> {
.collect();
// Parse the JSON output from the compiler and extract out the messages.
let actual_errors = json::parse_output(&diagnostic_file_name, &proc_res.stderr, proc_res);
let actual_errors = json::parse_output(&diagnostic_file_name, &self.get_output(proc_res))
.into_iter()
.map(|e| Error { msg: self.normalize_output(&e.msg, &[]), ..e });
let mut unexpected = Vec::new();
let mut found = vec![false; expected_errors.len()];
for mut actual_error in actual_errors {
actual_error.msg = self.normalize_output(&actual_error.msg, &[]);
for actual_error in actual_errors {
for pattern in &self.props.error_patterns {
let pattern = pattern.trim();
if actual_error.msg.contains(pattern) {
let q = if actual_error.line_num.is_none() { "?" } else { "" };
self.fatal(&format!(
"error pattern '{pattern}' is found in structured \
diagnostics, use `//~{q} {} {pattern}` instead",
actual_error.kind,
));
}
}
let opt_index =
expected_errors.iter().enumerate().position(|(index, expected_error)| {

View file

@ -100,16 +100,8 @@ impl TestCx<'_> {
self.check_no_compiler_crash(&proc_res, self.props.should_ice);
let output_to_check = self.get_output(&proc_res);
let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
if !expected_errors.is_empty() {
if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty()
{
self.fatal("both error pattern and expected errors specified");
}
self.check_expected_errors(expected_errors, &proc_res);
} else {
self.check_all_error_patterns(&output_to_check, &proc_res, pm);
}
self.check_expected_errors(&proc_res);
self.check_all_error_patterns(&output_to_check, &proc_res);
if self.props.should_ice {
match proc_res.status.code() {
Some(101) => (),
@ -137,6 +129,6 @@ impl TestCx<'_> {
let output_to_check = self.get_output(&proc_res);
self.check_correct_failure_status(&proc_res);
self.check_all_error_patterns(&output_to_check, &proc_res, pm);
self.check_all_error_patterns(&output_to_check, &proc_res);
}
}

View file

@ -9,7 +9,7 @@ use super::{
AllowUnused, Emit, FailMode, LinkToAux, PassMode, TargetLocation, TestCx, TestOutput,
Truncated, UI_FIXED, WillExecute,
};
use crate::{errors, json};
use crate::json;
impl TestCx<'_> {
pub(super) fn run_ui_test(&self) {
@ -127,9 +127,7 @@ impl TestCx<'_> {
);
}
let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
if let WillExecute::Yes = should_run {
let output_to_check = if let WillExecute::Yes = should_run {
let proc_res = self.exec_compiled_test();
let run_output_errors = if self.props.check_run_results {
self.load_compare_outputs(&proc_res, TestOutput::Run, explicit)
@ -150,44 +148,19 @@ impl TestCx<'_> {
self.fatal_proc_rec("test run succeeded!", &proc_res);
}
let output_to_check = self.get_output(&proc_res);
if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty()
{
// "// error-pattern" comments
self.check_all_error_patterns(&output_to_check, &proc_res, pm);
}
self.check_forbid_output(&output_to_check, &proc_res)
}
self.get_output(&proc_res)
} else {
self.get_output(&proc_res)
};
debug!(
"run_ui_test: explicit={:?} config.compare_mode={:?} expected_errors={:?} \
"run_ui_test: explicit={:?} config.compare_mode={:?} \
proc_res.status={:?} props.error_patterns={:?}",
explicit,
self.config.compare_mode,
expected_errors,
proc_res.status,
self.props.error_patterns
explicit, self.config.compare_mode, proc_res.status, self.props.error_patterns
);
if !explicit && self.config.compare_mode.is_none() {
// "//~ERROR comments"
self.check_expected_errors(expected_errors, &proc_res);
} else if explicit && !expected_errors.is_empty() {
let msg = format!(
"line {}: cannot combine `--error-format` with {} annotations; use `error-pattern` instead",
expected_errors[0].line_num_str(),
expected_errors[0].kind,
);
self.fatal(&msg);
}
let output_to_check = self.get_output(&proc_res);
if should_run == WillExecute::No
&& (!self.props.error_patterns.is_empty()
|| !self.props.regex_error_patterns.is_empty())
{
// "// error-pattern" comments
self.check_all_error_patterns(&output_to_check, &proc_res, pm);
}
self.check_expected_errors(&proc_res);
self.check_all_error_patterns(&output_to_check, &proc_res);
self.check_forbid_output(&output_to_check, &proc_res);
if self.props.run_rustfix && self.config.compare_mode.is_none() {

View file

@ -7,6 +7,7 @@ edition = "2021"
[dependencies]
anyhow = "1.0.71"
itertools = "0.12"
leb128 = "0.2.5"
md5 = { package = "md-5" , version = "0.10.5" }
miniz_oxide = "0.7.1"

View file

@ -1,23 +1,33 @@
use std::collections::HashMap;
use std::fmt::{self, Debug, Write as _};
use std::sync::OnceLock;
use std::sync::LazyLock;
use anyhow::{Context, anyhow};
use anyhow::{Context, anyhow, bail, ensure};
use itertools::Itertools;
use regex::Regex;
use crate::parser::{Parser, unescape_llvm_string_contents};
use crate::covmap::FilenameTables;
use crate::llvm_utils::unescape_llvm_string_contents;
use crate::parser::Parser;
#[cfg(test)]
mod tests;
pub(crate) fn dump_covfun_mappings(
llvm_ir: &str,
filename_tables: &FilenameTables,
function_names: &HashMap<u64, String>,
) -> anyhow::Result<()> {
// Extract function coverage entries from the LLVM IR assembly, and associate
// each entry with its (demangled) name.
let mut covfun_entries = llvm_ir
.lines()
.filter_map(covfun_line_data)
.map(|line_data| (function_names.get(&line_data.name_hash).map(String::as_str), line_data))
.collect::<Vec<_>>();
.filter(|line| is_covfun_line(line))
.map(parse_covfun_line)
.map_ok(|line_data| {
(function_names.get(&line_data.name_hash).map(String::as_str), line_data)
})
.collect::<Result<Vec<_>, _>>()?;
covfun_entries.sort_by(|a, b| {
// Sort entries primarily by name, to help make the order consistent
// across platforms and relatively insensitive to changes.
@ -41,8 +51,12 @@ pub(crate) fn dump_covfun_mappings(
println!("Number of files: {num_files}");
for i in 0..num_files {
let global_file_id = parser.read_uleb128_u32()?;
println!("- file {i} => global file {global_file_id}");
let global_file_id = parser.read_uleb128_usize()?;
let &CovfunLineData { filenames_hash, .. } = line_data;
let Some(filename) = filename_tables.lookup(filenames_hash, global_file_id) else {
bail!("couldn't resolve global file: {filenames_hash}, {global_file_id}");
};
println!("- file {i} => {filename}");
}
let num_expressions = parser.read_uleb128_u32()?;
@ -107,36 +121,50 @@ pub(crate) fn dump_covfun_mappings(
Ok(())
}
#[derive(Debug, PartialEq, Eq)]
struct CovfunLineData {
name_hash: u64,
is_used: bool,
name_hash: u64,
filenames_hash: u64,
payload: Vec<u8>,
}
/// Checks a line of LLVM IR assembly to see if it contains an `__llvm_covfun`
/// entry, and if so extracts relevant data in a `CovfunLineData`.
fn covfun_line_data(line: &str) -> Option<CovfunLineData> {
let re = {
// We cheat a little bit and match variable names `@__covrec_[HASH]u`
// rather than the section name, because the section name is harder to
// extract and differs across Linux/Windows/macOS. We also extract the
// symbol name hash from the variable name rather than the data, since
// it's easier and both should match.
static RE: OnceLock<Regex> = OnceLock::new();
RE.get_or_init(|| {
Regex::new(
r#"^@__covrec_(?<name_hash>[0-9A-Z]+)(?<is_used>u)? = .*\[[0-9]+ x i8\] c"(?<payload>[^"]*)".*$"#,
)
.unwrap()
})
};
fn is_covfun_line(line: &str) -> bool {
line.starts_with("@__covrec_")
}
let captures = re.captures(line)?;
let name_hash = u64::from_str_radix(&captures["name_hash"], 16).unwrap();
/// Given a line of LLVM IR assembly that should contain an `__llvm_covfun`
/// entry, parses it to extract relevant data in a `CovfunLineData`.
fn parse_covfun_line(line: &str) -> anyhow::Result<CovfunLineData> {
ensure!(is_covfun_line(line));
// We cheat a little bit and match variable names `@__covrec_[HASH]u`
// rather than the section name, because the section name is harder to
// extract and differs across Linux/Windows/macOS.
const RE_STRING: &str = r#"(?x)^
@__covrec_[0-9A-Z]+(?<is_used>u)?
\ = \ # (trailing space)
.*
<\{
\ i64 \ (?<name_hash> -? [0-9]+),
\ i32 \ -? [0-9]+, # (length of payload; currently unused)
\ i64 \ -? [0-9]+, # (source hash; currently unused)
\ i64 \ (?<filenames_hash> -? [0-9]+),
\ \[ [0-9]+ \ x \ i8 \] \ c"(?<payload>[^"]*)"
\ # (trailing space)
}>
.*$
"#;
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(RE_STRING).unwrap());
let captures =
RE.captures(line).with_context(|| format!("couldn't parse covfun line: {line:?}"))?;
let is_used = captures.name("is_used").is_some();
let name_hash = i64::from_str_radix(&captures["name_hash"], 10).unwrap() as u64;
let filenames_hash = i64::from_str_radix(&captures["filenames_hash"], 10).unwrap() as u64;
let payload = unescape_llvm_string_contents(&captures["payload"]);
Some(CovfunLineData { name_hash, is_used, payload })
Ok(CovfunLineData { is_used, name_hash, filenames_hash, payload })
}
// Extra parser methods only needed when parsing `covfun` payloads.

View file

@ -0,0 +1,53 @@
use super::{CovfunLineData, parse_covfun_line};
/// Integers in LLVM IR are not inherently signed/unsigned, and the text format tends
/// to emit them in signed form, so this helper function converts `i64` to `u64`.
fn as_u64(x: i64) -> u64 {
x as u64
}
#[test]
fn parse_covfun_line_data() {
struct Case {
line: &'static str,
expected: CovfunLineData,
}
let cases = &[
// Copied from `trivial.ll`:
Case {
line: r#"@__covrec_49A9BAAE5F896E81u = linkonce_odr hidden constant <{ i64, i32, i64, i64, [9 x i8] }> <{ i64 5307978893922758273, i32 9, i64 445092354169400020, i64 6343436898695299756, [9 x i8] c"\01\01\00\01\01\03\01\00\0D" }>, section "__LLVM_COV,__llvm_covfun", align 8"#,
expected: CovfunLineData {
is_used: true,
name_hash: as_u64(5307978893922758273),
filenames_hash: as_u64(6343436898695299756),
payload: b"\x01\x01\x00\x01\x01\x03\x01\x00\x0D".to_vec(),
},
},
// Copied from `on-off-sandwich.ll`:
Case {
line: r#"@__covrec_D0CE53C5E64F319Au = linkonce_odr hidden constant <{ i64, i32, i64, i64, [14 x i8] }> <{ i64 -3400688559180533350, i32 14, i64 7307957714577672185, i64 892196767019953100, [14 x i8] c"\01\01\00\02\01\10\05\02\10\01\07\05\00\06" }>, section "__LLVM_COV,__llvm_covfun", align 8"#,
expected: CovfunLineData {
is_used: true,
name_hash: as_u64(-3400688559180533350),
filenames_hash: as_u64(892196767019953100),
payload: b"\x01\x01\x00\x02\x01\x10\x05\x02\x10\x01\x07\x05\x00\x06".to_vec(),
},
},
// Copied from `no-core.ll`:
Case {
line: r#"@__covrec_F8016FC82D46106u = linkonce_odr hidden constant <{ i64, i32, i64, i64, [9 x i8] }> <{ i64 1116917981370409222, i32 9, i64 -8857254680411629915, i64 -3625186110715410276, [9 x i8] c"\01\01\00\01\01\0C\01\00\0D" }>, section "__LLVM_COV,__llvm_covfun", align 8"#,
expected: CovfunLineData {
is_used: true,
name_hash: as_u64(1116917981370409222),
filenames_hash: as_u64(-3625186110715410276),
payload: b"\x01\x01\x00\x01\x01\x0C\x01\x00\x0D".to_vec(),
},
},
];
for &Case { line, ref expected } in cases {
println!("- {line}");
let line_data = parse_covfun_line(line).map_err(|e| e.to_string());
assert_eq!(line_data.as_ref(), Ok(expected));
}
}

View file

@ -0,0 +1,75 @@
use std::collections::HashMap;
use std::sync::LazyLock;
use anyhow::{Context, ensure};
use regex::Regex;
use crate::llvm_utils::{truncated_md5, unescape_llvm_string_contents};
use crate::parser::Parser;
#[derive(Debug, Default)]
pub(crate) struct FilenameTables {
map: HashMap<u64, Vec<String>>,
}
impl FilenameTables {
pub(crate) fn lookup(&self, filenames_hash: u64, global_file_id: usize) -> Option<&str> {
let table = self.map.get(&filenames_hash)?;
let filename = table.get(global_file_id)?;
Some(filename)
}
}
struct CovmapLineData {
payload: Vec<u8>,
}
pub(crate) fn make_filename_tables(llvm_ir: &str) -> anyhow::Result<FilenameTables> {
let mut map = HashMap::default();
for line in llvm_ir.lines().filter(|line| is_covmap_line(line)) {
let CovmapLineData { payload } = parse_covmap_line(line)?;
let mut parser = Parser::new(&payload);
let n_filenames = parser.read_uleb128_usize()?;
let uncompressed_bytes = parser.read_chunk_to_uncompressed_bytes()?;
parser.ensure_empty()?;
let mut filenames_table = vec![];
let mut parser = Parser::new(&uncompressed_bytes);
for _ in 0..n_filenames {
let len = parser.read_uleb128_usize()?;
let bytes = parser.read_n_bytes(len)?;
let filename = str::from_utf8(bytes)?;
filenames_table.push(filename.to_owned());
}
let filenames_hash = truncated_md5(&payload);
map.insert(filenames_hash, filenames_table);
}
Ok(FilenameTables { map })
}
fn is_covmap_line(line: &str) -> bool {
line.starts_with("@__llvm_coverage_mapping ")
}
fn parse_covmap_line(line: &str) -> anyhow::Result<CovmapLineData> {
ensure!(is_covmap_line(line));
const RE_STRING: &str = r#"(?x)^
@__llvm_coverage_mapping \ =
.*
\[ [0-9]+ \ x \ i8 \] \ c"(?<payload>[^"]*)"
.*$
"#;
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(RE_STRING).unwrap());
let captures =
RE.captures(line).with_context(|| format!("couldn't parse covmap line: {line:?}"))?;
let payload = unescape_llvm_string_contents(&captures["payload"]);
Ok(CovmapLineData { payload })
}

View file

@ -0,0 +1,85 @@
use std::borrow::Cow;
use std::sync::OnceLock;
use anyhow::{anyhow, ensure};
use regex::bytes;
use crate::parser::Parser;
#[cfg(test)]
mod tests;
/// Given the raw contents of a string literal in LLVM IR assembly, decodes any
/// backslash escapes and returns a vector containing the resulting byte string.
pub(crate) fn unescape_llvm_string_contents(contents: &str) -> Vec<u8> {
let escape_re = {
static RE: OnceLock<bytes::Regex> = OnceLock::new();
// LLVM IR supports two string escapes: `\\` and `\xx`.
RE.get_or_init(|| bytes::Regex::new(r"\\\\|\\([0-9A-Za-z]{2})").unwrap())
};
fn u8_from_hex_digits(digits: &[u8]) -> u8 {
// We know that the input contains exactly 2 hex digits, so these calls
// should never fail.
assert_eq!(digits.len(), 2);
let digits = std::str::from_utf8(digits).unwrap();
u8::from_str_radix(digits, 16).unwrap()
}
escape_re
.replace_all(contents.as_bytes(), |captures: &bytes::Captures<'_>| {
let byte = match captures.get(1) {
None => b'\\',
Some(hex_digits) => u8_from_hex_digits(hex_digits.as_bytes()),
};
[byte]
})
.into_owned()
}
/// LLVM's profiler/coverage metadata often uses an MD5 hash truncated to
/// 64 bits as a way to associate data stored in different tables/sections.
pub(crate) fn truncated_md5(bytes: &[u8]) -> u64 {
use md5::{Digest, Md5};
let mut hasher = Md5::new();
hasher.update(bytes);
let hash: [u8; 8] = hasher.finalize().as_slice()[..8].try_into().unwrap();
// The truncated hash is explicitly little-endian, regardless of host
// or target platform. (See `MD5Result::low` in LLVM's `MD5.h`.)
u64::from_le_bytes(hash)
}
impl<'a> Parser<'a> {
/// Reads a sequence of:
/// - Length of uncompressed data in bytes, as ULEB128
/// - Length of compressed data in bytes (or 0), as ULEB128
/// - The indicated number of compressed or uncompressed bytes
///
/// If the number of compressed bytes is 0, the subsequent bytes are
/// uncompressed. Otherwise, the subsequent bytes are compressed, and will
/// be decompressed.
///
/// Returns the uncompressed bytes that were read directly or decompressed.
pub(crate) fn read_chunk_to_uncompressed_bytes(&mut self) -> anyhow::Result<Cow<'a, [u8]>> {
let uncompressed_len = self.read_uleb128_usize()?;
let compressed_len = self.read_uleb128_usize()?;
if compressed_len == 0 {
// The bytes are uncompressed, so read them directly.
let uncompressed_bytes = self.read_n_bytes(uncompressed_len)?;
Ok(Cow::Borrowed(uncompressed_bytes))
} else {
// The bytes are compressed, so read and decompress them.
let compressed_bytes = self.read_n_bytes(compressed_len)?;
let uncompressed_bytes = miniz_oxide::inflate::decompress_to_vec_zlib_with_limit(
compressed_bytes,
uncompressed_len,
)
.map_err(|e| anyhow!("{e:?}"))?;
ensure!(uncompressed_bytes.len() == uncompressed_len);
Ok(Cow::Owned(uncompressed_bytes))
}
}
}

View file

@ -1,9 +1,5 @@
use super::unescape_llvm_string_contents;
// WARNING: These tests don't necessarily run in CI, and were mainly used to
// help track down problems when originally developing this tool.
// (The tool is still tested indirectly by snapshot tests that rely on it.)
// Tests for `unescape_llvm_string_contents`:
#[test]

View file

@ -1,4 +1,6 @@
mod covfun;
mod covmap;
mod llvm_utils;
mod parser;
mod prf_names;
@ -17,8 +19,9 @@ fn main() -> anyhow::Result<()> {
let llvm_ir_path = args.get(1).context("LLVM IR file not specified")?;
let llvm_ir = std::fs::read_to_string(llvm_ir_path).context("couldn't read LLVM IR file")?;
let filename_tables = covmap::make_filename_tables(&llvm_ir)?;
let function_names = crate::prf_names::make_function_names_table(&llvm_ir)?;
crate::covfun::dump_covfun_mappings(&llvm_ir, &function_names)?;
crate::covfun::dump_covfun_mappings(&llvm_ir, &filename_tables, &function_names)?;
Ok(())
}

View file

@ -1,38 +1,4 @@
#[cfg(test)]
mod tests;
use std::sync::OnceLock;
use anyhow::ensure;
use regex::bytes;
/// Given the raw contents of a string literal in LLVM IR assembly, decodes any
/// backslash escapes and returns a vector containing the resulting byte string.
pub(crate) fn unescape_llvm_string_contents(contents: &str) -> Vec<u8> {
let escape_re = {
static RE: OnceLock<bytes::Regex> = OnceLock::new();
// LLVM IR supports two string escapes: `\\` and `\xx`.
RE.get_or_init(|| bytes::Regex::new(r"\\\\|\\([0-9A-Za-z]{2})").unwrap())
};
fn u8_from_hex_digits(digits: &[u8]) -> u8 {
// We know that the input contains exactly 2 hex digits, so these calls
// should never fail.
assert_eq!(digits.len(), 2);
let digits = std::str::from_utf8(digits).unwrap();
u8::from_str_radix(digits, 16).unwrap()
}
escape_re
.replace_all(contents.as_bytes(), |captures: &bytes::Captures<'_>| {
let byte = match captures.get(1) {
None => b'\\',
Some(hex_digits) => u8_from_hex_digits(hex_digits.as_bytes()),
};
[byte]
})
.into_owned()
}
pub(crate) struct Parser<'a> {
rest: &'a [u8],

View file

@ -1,10 +1,10 @@
use std::collections::HashMap;
use std::sync::OnceLock;
use anyhow::{anyhow, ensure};
use regex::Regex;
use crate::parser::{Parser, unescape_llvm_string_contents};
use crate::llvm_utils::{truncated_md5, unescape_llvm_string_contents};
use crate::parser::Parser;
/// Scans through the contents of an LLVM IR assembly file to find `__llvm_prf_names`
/// entries, decodes them, and creates a table that maps name hash values to
@ -25,18 +25,6 @@ pub(crate) fn make_function_names_table(llvm_ir: &str) -> anyhow::Result<HashMap
Some(payload)
}
/// LLVM's profiler/coverage metadata often uses an MD5 hash truncated to
/// 64 bits as a way to associate data stored in different tables/sections.
fn truncated_md5(bytes: &[u8]) -> u64 {
use md5::{Digest, Md5};
let mut hasher = Md5::new();
hasher.update(bytes);
let hash: [u8; 8] = hasher.finalize().as_slice()[..8].try_into().unwrap();
// The truncated hash is explicitly little-endian, regardless of host
// or target platform. (See `MD5Result::low` in LLVM's `MD5.h`.)
u64::from_le_bytes(hash)
}
fn demangle_if_able(symbol_name_bytes: &[u8]) -> anyhow::Result<String> {
// In practice, raw symbol names should always be ASCII.
let symbol_name_str = std::str::from_utf8(symbol_name_bytes)?;
@ -54,26 +42,8 @@ pub(crate) fn make_function_names_table(llvm_ir: &str) -> anyhow::Result<HashMap
for payload in llvm_ir.lines().filter_map(prf_names_payload).map(unescape_llvm_string_contents)
{
let mut parser = Parser::new(&payload);
let uncompressed_len = parser.read_uleb128_usize()?;
let compressed_len = parser.read_uleb128_usize()?;
let uncompressed_bytes_vec;
let uncompressed_bytes: &[u8] = if compressed_len == 0 {
// The symbol name bytes are uncompressed, so read them directly.
parser.read_n_bytes(uncompressed_len)?
} else {
// The symbol name bytes are compressed, so read and decompress them.
let compressed_bytes = parser.read_n_bytes(compressed_len)?;
uncompressed_bytes_vec = miniz_oxide::inflate::decompress_to_vec_zlib_with_limit(
compressed_bytes,
uncompressed_len,
)
.map_err(|e| anyhow!("{e:?}"))?;
ensure!(uncompressed_bytes_vec.len() == uncompressed_len);
&uncompressed_bytes_vec
};
let uncompressed_bytes = parser.read_chunk_to_uncompressed_bytes()?;
parser.ensure_empty()?;
// Symbol names in the payload are separated by `0x01` bytes.
for raw_name in uncompressed_bytes.split(|&b| b == 0x01) {
@ -81,8 +51,6 @@ pub(crate) fn make_function_names_table(llvm_ir: &str) -> anyhow::Result<HashMap
let demangled = demangle_if_able(raw_name)?;
map.insert(hash, demangled);
}
parser.ensure_empty()?;
}
Ok(map)

View file

@ -5,7 +5,7 @@ license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
anyhow = { version = "1", features = ["backtrace"] }
anyhow = { version = "1" }
clap = { version = "4", features = ["derive"] }
serde = { version = "1.0.125", features = [ "derive" ] }
serde_json = "1.0.59"

View file

@ -312,6 +312,7 @@ impl<'a> LintExtractor<'a> {
if matches!(
lint.name.as_str(),
"unused_features" // broken lint
| "soft_unstable" // cannot have a stable example
) {
return Ok(());
}

View file

@ -1,5 +1,6 @@
// We're testing x86 target specific features
//@only-target: x86_64 i686
#![allow(unnecessary_transmutes)]
#[cfg(target_arch = "x86")]
use std::arch::x86::*;

View file

@ -1,7 +1,7 @@
[package]
name = "opt-dist"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
build_helper = { path = "../../build_helper" }
@ -10,12 +10,11 @@ log = "0.4"
anyhow = "1"
humantime = "2"
humansize = "2"
sysinfo = { version = "0.31.2", default-features = false, features = ["disk"] }
sysinfo = { version = "0.35.0", default-features = false, features = ["disk"] }
fs_extra = "1"
camino = "1"
tar = "0.4"
xz = { version = "0.1", package = "xz2" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
glob = "0.3"
tempfile = "3.5"

View file

@ -26,6 +26,7 @@ pub struct Environment {
use_bolt: bool,
shared_llvm: bool,
run_tests: bool,
fast_try_build: bool,
}
impl Environment {
@ -106,6 +107,10 @@ impl Environment {
pub fn run_tests(&self) -> bool {
self.run_tests
}
pub fn is_fast_try_build(&self) -> bool {
self.fast_try_build
}
}
/// What is the extension of binary executables on this platform?

View file

@ -113,13 +113,16 @@ impl Bootstrap {
"library/std",
])
.env("RUST_BACKTRACE", "full");
let cmd = add_shared_x_flags(env, cmd);
Self { cmd, metrics_path }
}
pub fn dist(env: &Environment, dist_args: &[String]) -> Self {
let metrics_path = env.build_root().join("build").join("metrics.json");
let cmd = cmd(&dist_args.iter().map(|arg| arg.as_str()).collect::<Vec<_>>())
.env("RUST_BACKTRACE", "full");
let args = dist_args.iter().map(|arg| arg.as_str()).collect::<Vec<_>>();
let cmd = cmd(&args).env("RUST_BACKTRACE", "full");
let cmd = add_shared_x_flags(env, cmd);
Self { cmd, metrics_path }
}
@ -184,3 +187,7 @@ impl Bootstrap {
Ok(())
}
}
fn add_shared_x_flags(env: &Environment, cmd: CmdBuilder) -> CmdBuilder {
if env.is_fast_try_build() { cmd.arg("--set").arg("rust.deny-warnings=false") } else { cmd }
}

View file

@ -95,7 +95,7 @@ enum EnvironmentCmd {
#[arg(long)]
benchmark_cargo_config: Vec<String>,
/// Perform tests after final build if it's not a try build
/// Perform tests after final build if it's not a fast try build
#[arg(long)]
run_tests: bool,
},
@ -111,11 +111,14 @@ enum EnvironmentCmd {
},
}
fn is_try_build() -> bool {
/// For a fast try build, we want to only build the bare minimum of components to get a
/// working toolchain, and not run any tests.
fn is_fast_try_build() -> bool {
std::env::var("DIST_TRY_BUILD").unwrap_or_else(|_| "0".to_string()) != "0"
}
fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)> {
let is_fast_try_build = is_fast_try_build();
let (env, args) = match args.env {
EnvironmentCmd::Local {
target_triple,
@ -144,6 +147,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
.skipped_tests(skipped_tests)
.benchmark_cargo_config(benchmark_cargo_config)
.run_tests(run_tests)
.fast_try_build(is_fast_try_build)
.build()?;
(env, shared.build_args)
@ -167,6 +171,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
.use_bolt(!is_aarch64)
.skipped_tests(vec![])
.run_tests(true)
.fast_try_build(is_fast_try_build)
.build()?;
(env, shared.build_args)
@ -187,6 +192,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
.use_bolt(false)
.skipped_tests(vec![])
.run_tests(true)
.fast_try_build(is_fast_try_build)
.build()?;
(env, shared.build_args)
@ -350,9 +356,8 @@ fn execute_pipeline(
// After dist has finished, run a subset of the test suite on the optimized artifacts to discover
// possible regressions.
// The tests are not executed for try builds, which can be in various broken states, so we don't
// want to gatekeep them with tests.
if !is_try_build() && env.run_tests() {
// The tests are not executed for fast try builds, which can be broken and might not pass them.
if !is_fast_try_build() && env.run_tests() {
timer.section("Run tests", |_| run_tests(env))?;
}
@ -361,7 +366,10 @@ fn execute_pipeline(
fn main() -> anyhow::Result<()> {
// Make sure that we get backtraces for easier debugging in CI
std::env::set_var("RUST_BACKTRACE", "1");
unsafe {
// SAFETY: we are the only thread running at this point
std::env::set_var("RUST_BACKTRACE", "1");
}
env_logger::builder()
.filter_level(LevelFilter::Info)
@ -393,9 +401,9 @@ fn main() -> anyhow::Result<()> {
let (env, mut build_args) = create_environment(args).context("Cannot create environment")?;
// Skip components that are not needed for try builds to speed them up
if is_try_build() {
log::info!("Skipping building of unimportant components for a try build");
// Skip components that are not needed for fast try builds to speed them up
if is_fast_try_build() {
log::info!("Skipping building of unimportant components for a fast try build");
for target in [
"rust-docs",
"rustc-docs",

View file

@ -1,8 +1,11 @@
use crate::command::Command;
use crate::env_var;
use crate::util::set_host_compiler_dylib_path;
/// Returns a command that can be used to invoke cargo. The cargo is provided by compiletest
/// through the `CARGO` env var.
pub fn cargo() -> Command {
Command::new(env_var("CARGO"))
let mut cmd = Command::new(env_var("CARGO"));
set_host_compiler_dylib_path(&mut cmd);
cmd
}

View file

@ -85,6 +85,7 @@ dependencies = [
"query-group-macro",
"rustc-hash 2.1.1",
"salsa",
"salsa-macros",
"semver",
"span",
"syntax",
@ -630,6 +631,7 @@ dependencies = [
"rustc-hash 2.1.1",
"rustc_apfloat",
"salsa",
"salsa-macros",
"smallvec",
"span",
"stdx",
@ -660,6 +662,7 @@ dependencies = [
"query-group-macro",
"rustc-hash 2.1.1",
"salsa",
"salsa-macros",
"smallvec",
"span",
"stdx",
@ -700,6 +703,7 @@ dependencies = [
"rustc-hash 2.1.1",
"rustc_apfloat",
"salsa",
"salsa-macros",
"scoped-tls",
"smallvec",
"span",
@ -936,6 +940,7 @@ dependencies = [
"rayon",
"rustc-hash 2.1.1",
"salsa",
"salsa-macros",
"span",
"stdx",
"syntax",
@ -1729,6 +1734,7 @@ dependencies = [
"proc-macro2",
"quote",
"salsa",
"salsa-macros",
"syn",
]
@ -2033,9 +2039,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "salsa"
version = "0.20.0"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1be22155f8d9732518b2db2bf379fe6f0b2375e76b08b7c8fe6c1b887d548c24"
checksum = "6f80d5cf3c3fcab2cef898012f242a670477a1baa609267376af9cb4409026c5"
dependencies = [
"boxcar",
"crossbeam-queue",
@ -2056,15 +2062,15 @@ dependencies = [
[[package]]
name = "salsa-macro-rules"
version = "0.20.0"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55a7ef0a84e336f7c5f0332d81727f5629fe042d2aa556c75307afebc9f78a5"
checksum = "05303d72606fbf2b9c9523cda2039bb8ecb00304027a3cd7e52b02a65c7d9185"
[[package]]
name = "salsa-macros"
version = "0.20.0"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d0e88a9c0c0d231a63f83dcd1a2c5e5d11044fac4b65bc9ad3b68ab48b0a0ab"
checksum = "eb2f0e2a30c65cb3cd63440c491dde68d9af7e1be2b77832ac7057141107db50"
dependencies = [
"heck",
"proc-macro2",

View file

@ -131,7 +131,9 @@ process-wrap = { version = "8.2.0", features = ["std"] }
pulldown-cmark-to-cmark = "10.0.4"
pulldown-cmark = { version = "0.9.6", default-features = false }
rayon = "1.10.0"
salsa = "0.20.0"
rowan = "=0.15.15"
salsa = { version = "0.21.1", default-features = false, features = ["rayon","salsa_unstable"] }
salsa-macros = "0.21.1"
semver = "1.0.26"
serde = { version = "1.0.219" }
serde_derive = { version = "1.0.219" }

View file

@ -15,6 +15,7 @@ rust-version.workspace = true
la-arena.workspace = true
dashmap.workspace = true
salsa.workspace = true
salsa-macros.workspace = true
query-group.workspace = true
rustc-hash.workspace = true
triomphe.workspace = true

View file

@ -392,7 +392,7 @@ impl BuiltDependency {
pub type CratesIdMap = FxHashMap<CrateBuilderId, Crate>;
#[salsa::input]
#[salsa_macros::input]
#[derive(Debug)]
pub struct Crate {
#[return_ref]

View file

@ -1,9 +1,13 @@
//! base_db defines basic database traits. The concrete DB is defined by ide.
pub use salsa;
pub use salsa_macros;
// FIXME: Rename this crate, base db is non descriptive
mod change;
mod input;
use std::hash::BuildHasherDefault;
use std::{cell::RefCell, hash::BuildHasherDefault, panic, sync::Once};
pub use crate::{
change::FileChange,
@ -17,7 +21,6 @@ pub use crate::{
use dashmap::{DashMap, mapref::entry::Entry};
pub use query_group::{self};
use rustc_hash::{FxHashSet, FxHasher};
pub use salsa::{self};
use salsa::{Durability, Setter};
pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
use span::Edition;
@ -28,7 +31,7 @@ pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}
#[macro_export]
macro_rules! impl_intern_key {
($id:ident, $loc:ident) => {
#[salsa::interned(no_lifetime)]
#[salsa_macros::interned(no_lifetime)]
pub struct $id {
pub loc: $loc,
}
@ -57,7 +60,12 @@ pub struct Files {
impl Files {
pub fn file_text(&self, file_id: vfs::FileId) -> FileText {
*self.files.get(&file_id).expect("Unable to fetch file; this is a bug")
match self.files.get(&file_id) {
Some(text) => *text,
None => {
panic!("Unable to fetch file text for `vfs::FileId`: {file_id:?}; this is a bug")
}
}
}
pub fn set_file_text(&self, db: &mut dyn SourceDatabase, file_id: vfs::FileId, text: &str) {
@ -93,10 +101,12 @@ impl Files {
/// Source root of the file.
pub fn source_root(&self, source_root_id: SourceRootId) -> SourceRootInput {
let source_root = self
.source_roots
.get(&source_root_id)
.expect("Unable to fetch source root id; this is a bug");
let source_root = match self.source_roots.get(&source_root_id) {
Some(source_root) => source_root,
None => panic!(
"Unable to fetch `SourceRootInput` with `SourceRootId` ({source_root_id:?}); this is a bug"
),
};
*source_root
}
@ -121,10 +131,12 @@ impl Files {
}
pub fn file_source_root(&self, id: vfs::FileId) -> FileSourceRootInput {
let file_source_root = self
.file_source_roots
.get(&id)
.expect("Unable to fetch FileSourceRootInput; this is a bug");
let file_source_root = match self.file_source_roots.get(&id) {
Some(file_source_root) => file_source_root,
None => panic!(
"Unable to get `FileSourceRootInput` with `vfs::FileId` ({id:?}); this is a bug",
),
};
*file_source_root
}
@ -152,7 +164,7 @@ impl Files {
}
}
#[salsa::interned(no_lifetime, debug, constructor=from_span)]
#[salsa_macros::interned(no_lifetime, debug, constructor=from_span)]
pub struct EditionedFileId {
pub editioned_file_id: span::EditionedFileId,
}
@ -187,18 +199,18 @@ impl EditionedFileId {
}
}
#[salsa::input(debug)]
#[salsa_macros::input(debug)]
pub struct FileText {
pub text: Arc<str>,
pub file_id: vfs::FileId,
}
#[salsa::input(debug)]
#[salsa_macros::input(debug)]
pub struct FileSourceRootInput {
pub source_root_id: SourceRootId,
}
#[salsa::input(debug)]
#[salsa_macros::input(debug)]
pub struct SourceRootInput {
pub source_root: Arc<SourceRoot>,
}
@ -265,7 +277,7 @@ pub fn transitive_deps(db: &dyn SourceDatabase, crate_id: Crate) -> FxHashSet<Cr
deps
}
#[salsa::db]
#[salsa_macros::db]
pub trait SourceDatabase: salsa::Database {
/// Text of the file.
fn file_text(&self, file_id: vfs::FileId) -> FileText;
@ -344,7 +356,7 @@ fn parse(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Parse<ast::SourceFil
}
fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<&[SyntaxError]> {
#[salsa::tracked(return_ref)]
#[salsa_macros::tracked(return_ref)]
fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<Box<[SyntaxError]>> {
let errors = db.parse(file_id).errors();
match &*errors {
@ -373,3 +385,49 @@ fn relevant_crates(db: &dyn RootQueryDb, file_id: FileId) -> Arc<[Crate]> {
let source_root = db.file_source_root(file_id);
db.source_root_crates(source_root.source_root_id(db))
}
#[must_use]
#[non_exhaustive]
pub struct DbPanicContext;
impl Drop for DbPanicContext {
fn drop(&mut self) {
Self::with_ctx(|ctx| assert!(ctx.pop().is_some()));
}
}
impl DbPanicContext {
pub fn enter(frame: String) -> DbPanicContext {
#[expect(clippy::print_stderr, reason = "already panicking anyway")]
fn set_hook() {
let default_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
default_hook(panic_info);
if let Some(backtrace) = salsa::Backtrace::capture() {
eprintln!("{backtrace:#}");
}
DbPanicContext::with_ctx(|ctx| {
if !ctx.is_empty() {
eprintln!("additional context:");
for (idx, frame) in ctx.iter().enumerate() {
eprintln!("{idx:>4}: {frame}\n");
}
}
});
}));
}
static SET_HOOK: Once = Once::new();
SET_HOOK.call_once(set_hook);
Self::with_ctx(|ctx| ctx.push(frame));
DbPanicContext
}
fn with_ctx(f: impl FnOnce(&mut Vec<String>)) {
thread_local! {
static CTX: RefCell<Vec<String>> = const { RefCell::new(Vec::new()) };
}
CTX.with(|ctx| f(&mut ctx.borrow_mut()));
}
}

View file

@ -28,6 +28,7 @@ triomphe.workspace = true
rustc_apfloat = "0.2.2"
text-size.workspace = true
salsa.workspace = true
salsa-macros.workspace = true
query-group.workspace = true
ra-ap-rustc_parse_format.workspace = true

View file

@ -1,6 +1,6 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
use std::{borrow::Cow, hash::Hash, ops};
use std::{borrow::Cow, convert::identity, hash::Hash, ops};
use base_db::Crate;
use cfg::{CfgExpr, CfgOptions};
@ -8,6 +8,7 @@ use either::Either;
use hir_expand::{
HirFileId, InFile,
attrs::{Attr, AttrId, RawAttrs, collect_attrs},
span_map::SpanMapRef,
};
use intern::{Symbol, sym};
use la_arena::{ArenaMap, Idx, RawIdx};
@ -45,8 +46,27 @@ impl Attrs {
(**self).iter().find(|attr| attr.id == id)
}
pub(crate) fn filter(db: &dyn DefDatabase, krate: Crate, raw_attrs: RawAttrs) -> Attrs {
Attrs(raw_attrs.filter(db, krate))
pub(crate) fn expand_cfg_attr(
db: &dyn DefDatabase,
krate: Crate,
raw_attrs: RawAttrs,
) -> Attrs {
Attrs(raw_attrs.expand_cfg_attr(db, krate))
}
pub(crate) fn is_cfg_enabled_for(
db: &dyn DefDatabase,
owner: &dyn ast::HasAttrs,
span_map: SpanMapRef<'_>,
cfg_options: &CfgOptions,
) -> Result<(), CfgExpr> {
RawAttrs::attrs_iter_expanded::<false>(db, owner, span_map, cfg_options)
.filter_map(|attr| attr.cfg())
.find_map(|cfg| match cfg_options.check(&cfg).is_none_or(identity) {
true => None,
false => Some(cfg),
})
.map_or(Ok(()), Err)
}
}
@ -522,38 +542,41 @@ impl AttrsWithOwner {
GenericParamId::ConstParamId(it) => {
let src = it.parent().child_source(db);
// FIXME: We should be never getting `None` here.
match src.value.get(it.local_id()) {
Some(val) => RawAttrs::from_attrs_owner(
return Attrs(match src.value.get(it.local_id()) {
Some(val) => RawAttrs::new_expanded(
db,
src.with_value(val),
val,
db.span_map(src.file_id).as_ref(),
def.krate(db).cfg_options(db),
),
None => RawAttrs::EMPTY,
}
});
}
GenericParamId::TypeParamId(it) => {
let src = it.parent().child_source(db);
// FIXME: We should be never getting `None` here.
match src.value.get(it.local_id()) {
Some(val) => RawAttrs::from_attrs_owner(
return Attrs(match src.value.get(it.local_id()) {
Some(val) => RawAttrs::new_expanded(
db,
src.with_value(val),
val,
db.span_map(src.file_id).as_ref(),
def.krate(db).cfg_options(db),
),
None => RawAttrs::EMPTY,
}
});
}
GenericParamId::LifetimeParamId(it) => {
let src = it.parent.child_source(db);
// FIXME: We should be never getting `None` here.
match src.value.get(it.local_id) {
Some(val) => RawAttrs::from_attrs_owner(
return Attrs(match src.value.get(it.local_id) {
Some(val) => RawAttrs::new_expanded(
db,
src.with_value(val),
val,
db.span_map(src.file_id).as_ref(),
def.krate(db).cfg_options(db),
),
None => RawAttrs::EMPTY,
}
});
}
},
AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
@ -561,7 +584,7 @@ impl AttrsWithOwner {
AttrDefId::UseId(it) => attrs_from_item_tree_loc(db, it),
};
let attrs = raw_attrs.filter(db, def.krate(db));
let attrs = raw_attrs.expand_cfg_attr(db, def.krate(db));
Attrs(attrs)
}

View file

@ -22,7 +22,7 @@ use crate::{
hir::generics::GenericParams,
import_map::ImportMap,
item_tree::{AttrOwner, ItemTree},
lang_item::{self, LangItem, LangItemTarget, LangItems},
lang_item::{self, LangItem},
nameres::{
DefMap, LocalDefMap,
assoc::{ImplItems, TraitItems},
@ -325,9 +325,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase {
// endregion:attrs
#[salsa::invoke(LangItems::lang_item_query)]
fn lang_item(&self, start_crate: Crate, item: LangItem) -> Option<LangItemTarget>;
#[salsa::invoke(ImportMap::import_map_query)]
fn import_map(&self, krate: Crate) -> Arc<ImportMap>;
@ -349,9 +346,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase {
// endregion:visibilities
#[salsa::invoke(LangItems::crate_lang_items_query)]
fn crate_lang_items(&self, krate: Crate) -> Option<Arc<LangItems>>;
#[salsa::invoke(crate::lang_item::notable_traits_in_deps)]
fn notable_traits_in_deps(&self, krate: Crate) -> Arc<[Arc<[TraitId]>]>;
#[salsa::invoke(crate::lang_item::crate_notable_traits)]

View file

@ -3,21 +3,24 @@
use std::mem;
use base_db::Crate;
use cfg::CfgOptions;
use drop_bomb::DropBomb;
use hir_expand::AstId;
use hir_expand::{
ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
attrs::RawAttrs, eager::EagerCallBackFn, mod_path::ModPath, span_map::SpanMap,
eager::EagerCallBackFn, mod_path::ModPath, span_map::SpanMap,
};
use span::{AstIdMap, Edition, SyntaxContext};
use syntax::ast::HasAttrs;
use syntax::{Parse, ast};
use syntax::{AstNode, Parse, ast};
use triomphe::Arc;
use tt::TextRange;
use crate::attr::Attrs;
use crate::expr_store::HygieneId;
use crate::macro_call_as_call_id;
use crate::nameres::DefMap;
use crate::{AsMacroCall, MacroId, UnresolvedMacro, db::DefDatabase};
use crate::{MacroId, UnresolvedMacro, db::DefDatabase};
#[derive(Debug)]
pub(super) struct Expander {
@ -64,22 +67,13 @@ impl Expander {
}
}
pub(super) fn attrs(
&self,
db: &dyn DefDatabase,
krate: Crate,
has_attrs: &dyn HasAttrs,
) -> Attrs {
Attrs::filter(db, krate, RawAttrs::new(db, has_attrs, self.span_map.as_ref()))
}
pub(super) fn is_cfg_enabled(
&self,
db: &dyn DefDatabase,
krate: Crate,
has_attrs: &dyn HasAttrs,
) -> bool {
self.attrs(db, krate, has_attrs).is_cfg_enabled(krate.cfg_options(db))
cfg_options: &CfgOptions,
) -> Result<(), cfg::CfgExpr> {
Attrs::is_cfg_enabled_for(db, has_attrs, self.span_map.as_ref(), cfg_options)
}
pub(super) fn call_syntax_ctx(&self) -> SyntaxContext {
@ -100,8 +94,31 @@ impl Expander {
let result = self.within_limit(db, |this| {
let macro_call = this.in_file(&macro_call);
match macro_call.as_call_id_with_errors(
let expands_to = hir_expand::ExpandTo::from_call_site(macro_call.value);
let ast_id = AstId::new(macro_call.file_id, this.ast_id_map().ast_id(macro_call.value));
let path = macro_call.value.path().and_then(|path| {
let range = path.syntax().text_range();
let mod_path = ModPath::from_src(db, path, &mut |range| {
this.span_map.span_for_range(range).ctx
})?;
let call_site = this.span_map.span_for_range(range);
Some((call_site, mod_path))
});
let Some((call_site, path)) = path else {
return ExpandResult::only_err(ExpandError::other(
this.span_map.span_for_range(macro_call.value.syntax().text_range()),
"malformed macro invocation",
));
};
match macro_call_as_call_id(
db,
ast_id,
&path,
call_site.ctx,
expands_to,
krate,
|path| resolver(path).map(|it| db.macro_def(it)),
eager_callback,

View file

@ -7,6 +7,7 @@ mod path;
use std::mem;
use cfg::CfgOptions;
use either::Either;
use hir_expand::{
HirFileId, InFile, Lookup, MacroDefId,
@ -81,8 +82,6 @@ pub(super) fn lower_body(
// even though they should be the same. Also, when the body comes from multiple expansions, their
// hygiene is different.
let krate = module.krate();
let mut self_param = None;
let mut source_map_self_param = None;
let mut params = vec![];
@ -100,9 +99,8 @@ pub(super) fn lower_body(
// and skip the body.
if skip_body {
if let Some(param_list) = parameters {
if let Some(self_param_syn) = param_list
.self_param()
.filter(|self_param| collector.expander.is_cfg_enabled(db, krate, self_param))
if let Some(self_param_syn) =
param_list.self_param().filter(|self_param| collector.check_cfg(self_param))
{
let is_mutable =
self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
@ -119,10 +117,7 @@ pub(super) fn lower_body(
source_map_self_param =
Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
}
let count = param_list
.params()
.filter(|it| collector.expander.is_cfg_enabled(db, krate, it))
.count();
let count = param_list.params().filter(|it| collector.check_cfg(it)).count();
params = (0..count).map(|_| collector.missing_pat()).collect();
};
let body_expr = collector.missing_expr();
@ -138,9 +133,7 @@ pub(super) fn lower_body(
}
if let Some(param_list) = parameters {
if let Some(self_param_syn) =
param_list.self_param().filter(|it| collector.expander.is_cfg_enabled(db, krate, it))
{
if let Some(self_param_syn) = param_list.self_param().filter(|it| collector.check_cfg(it)) {
let is_mutable =
self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none();
let hygiene = self_param_syn
@ -157,7 +150,7 @@ pub(super) fn lower_body(
}
for param in param_list.params() {
if collector.expander.is_cfg_enabled(db, krate, &param) {
if collector.check_cfg(&param) {
let param_pat = collector.collect_pat_top(param.pat());
params.push(param_pat);
}
@ -346,7 +339,7 @@ pub(crate) fn lower_function(
collector.collect_impl_trait(&mut expr_collector, |collector, mut impl_trait_lower_fn| {
if let Some(param_list) = fn_.value.param_list() {
if let Some(param) = param_list.self_param() {
let enabled = collector.expander.is_cfg_enabled(db, module.krate(), &param);
let enabled = collector.check_cfg(&param);
if enabled {
has_self_param = true;
params.push(match param.ty() {
@ -381,7 +374,7 @@ pub(crate) fn lower_function(
}
let p = param_list
.params()
.filter(|param| collector.expander.is_cfg_enabled(db, module.krate(), param))
.filter(|param| collector.check_cfg(param))
.filter(|param| {
let is_variadic = param.dotdotdot_token().is_some();
has_variadic |= is_variadic;
@ -441,6 +434,7 @@ pub(crate) fn lower_function(
pub struct ExprCollector<'db> {
db: &'db dyn DefDatabase,
cfg_options: &'db CfgOptions,
expander: Expander,
def_map: Arc<DefMap>,
local_def_map: Arc<LocalDefMap>,
@ -553,6 +547,7 @@ impl ExprCollector<'_> {
let expander = Expander::new(db, current_file_id, &def_map);
ExprCollector {
db,
cfg_options: module.krate().cfg_options(db),
module,
def_map,
local_def_map,
@ -1026,7 +1021,9 @@ impl ExprCollector<'_> {
/// Returns `None` if and only if the expression is `#[cfg]`d out.
fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
let syntax_ptr = AstPtr::new(&expr);
self.check_cfg(&expr)?;
if !self.check_cfg(&expr) {
return None;
}
// FIXME: Move some of these arms out into separate methods for clarity
Some(match expr {
@ -1114,6 +1111,7 @@ impl ExprCollector<'_> {
ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e),
ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e),
ast::Expr::CallExpr(e) => {
// FIXME: Remove this once we drop support for <1.86, https://github.com/rust-lang/rust/commit/ac9cb908ac4301dfc25e7a2edee574320022ae2c
let is_rustc_box = {
let attrs = e.attrs();
attrs.filter_map(|it| it.as_simple_atom()).any(|it| it == "rustc_box")
@ -1156,13 +1154,17 @@ impl ExprCollector<'_> {
match_arm_list
.arms()
.filter_map(|arm| {
self.check_cfg(&arm).map(|()| MatchArm {
pat: self.collect_pat_top(arm.pat()),
expr: self.collect_expr_opt(arm.expr()),
guard: arm
.guard()
.map(|guard| self.collect_expr_opt(guard.condition())),
})
if self.check_cfg(&arm) {
Some(MatchArm {
pat: self.collect_pat_top(arm.pat()),
expr: self.collect_expr_opt(arm.expr()),
guard: arm
.guard()
.map(|guard| self.collect_expr_opt(guard.condition())),
})
} else {
None
}
})
.collect()
} else {
@ -1230,7 +1232,9 @@ impl ExprCollector<'_> {
let fields = nfl
.fields()
.filter_map(|field| {
self.check_cfg(&field)?;
if !self.check_cfg(&field) {
return None;
}
let name = field.field_name()?.as_name();
@ -1483,7 +1487,9 @@ impl ExprCollector<'_> {
}
fn maybe_collect_expr_as_pat(&mut self, expr: &ast::Expr) -> Option<PatId> {
self.check_cfg(expr)?;
if !self.check_cfg(expr) {
return None;
}
let syntax_ptr = AstPtr::new(expr);
let result = match expr {
@ -1558,7 +1564,9 @@ impl ExprCollector<'_> {
let args = record_field_list
.fields()
.filter_map(|f| {
self.check_cfg(&f)?;
if !self.check_cfg(&f) {
return None;
}
let field_expr = f.expr()?;
let pat = self.collect_expr_as_pat(field_expr);
let name = f.field_name()?.as_name();
@ -2044,7 +2052,7 @@ impl ExprCollector<'_> {
fn collect_stmt(&mut self, statements: &mut Vec<Statement>, s: ast::Stmt) {
match s {
ast::Stmt::LetStmt(stmt) => {
if self.check_cfg(&stmt).is_none() {
if !self.check_cfg(&stmt) {
return;
}
let pat = self.collect_pat_top(stmt.pat());
@ -2059,7 +2067,7 @@ impl ExprCollector<'_> {
ast::Stmt::ExprStmt(stmt) => {
let expr = stmt.expr();
match &expr {
Some(expr) if self.check_cfg(expr).is_none() => return,
Some(expr) if !self.check_cfg(expr) => return,
_ => (),
}
let has_semi = stmt.semicolon_token().is_some();
@ -2074,7 +2082,7 @@ impl ExprCollector<'_> {
}
}
ast::Stmt::Item(ast::Item::MacroDef(macro_)) => {
if self.check_cfg(&macro_).is_none() {
if !self.check_cfg(&macro_) {
return;
}
let Some(name) = macro_.name() else {
@ -2086,7 +2094,7 @@ impl ExprCollector<'_> {
self.collect_macro_def(statements, macro_id);
}
ast::Stmt::Item(ast::Item::MacroRules(macro_)) => {
if self.check_cfg(&macro_).is_none() {
if !self.check_cfg(&macro_) {
return;
}
let Some(name) = macro_.name() else {
@ -2360,7 +2368,9 @@ impl ExprCollector<'_> {
let args = record_pat_field_list
.fields()
.filter_map(|f| {
self.check_cfg(&f)?;
if !self.check_cfg(&f) {
return None;
}
let ast_pat = f.pat()?;
let pat = self.collect_pat(ast_pat, binding_list);
let name = f.field_name()?.as_name();
@ -2536,25 +2546,18 @@ impl ExprCollector<'_> {
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
/// not.
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> {
let attrs = self.expander.attrs(self.db, self.module.krate(), owner);
match attrs.cfg() {
Some(cfg) => {
let cfg_options = self.module.krate().cfg_options(self.db);
if cfg_options.check(&cfg) != Some(false) {
return Some(());
}
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> bool {
let enabled = self.expander.is_cfg_enabled(self.db, owner, self.cfg_options);
match enabled {
Ok(()) => true,
Err(cfg) => {
self.source_map.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode {
node: self.expander.in_file(SyntaxNodePtr::new(owner.syntax())),
cfg,
opts: cfg_options.clone(),
opts: self.cfg_options.clone(),
});
None
false
}
None => Some(()),
}
}

View file

@ -224,7 +224,7 @@ impl ExprCollector<'_> {
curarg = parser.curarg;
let to_span = |inner_span: rustc_parse_format::InnerSpan| {
let to_span = |inner_span: std::ops::Range<usize>| {
is_direct_literal.then(|| {
TextRange::new(
inner_span.start.try_into().unwrap(),

View file

@ -110,7 +110,7 @@ impl GenericParamsCollector {
fn lower_param_list(&mut self, ec: &mut ExprCollector<'_>, params: ast::GenericParamList) {
for generic_param in params.generic_params() {
let enabled = ec.expander.is_cfg_enabled(ec.db, ec.module.krate(), &generic_param);
let enabled = ec.check_cfg(&generic_param);
if !enabled {
continue;
}
@ -270,7 +270,7 @@ impl GenericParamsCollector {
let self_ = Name::new_symbol_root(sym::Self_);
let idx = self.type_or_consts.alloc(
TypeParamData {
name: Some(self_.clone()),
name: Some(self_),
default: None,
provenance: TypeParamProvenance::TraitSelf,
}

View file

@ -214,7 +214,7 @@ pub(crate) fn parse(
};
}
let to_span = |inner_span: parse::InnerSpan| {
let to_span = |inner_span: std::ops::Range<usize>| {
is_source_literal.then(|| {
TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap())
})
@ -297,7 +297,7 @@ pub(crate) fn parse(
unfinished_literal.clear();
}
let span = parser.arg_places.get(placeholder_index).and_then(|&s| to_span(s));
let span = parser.arg_places.get(placeholder_index).and_then(|s| to_span(s.clone()));
placeholder_index += 1;
let position_span = to_span(position_span);

View file

@ -167,7 +167,7 @@ pub struct ItemScope {
// the resolutions of the imports of this scope
use_imports_types: FxHashMap<ImportOrExternCrate, ImportOrDef>,
use_imports_values: FxHashMap<ImportOrGlob, ImportOrDef>,
use_imports_macros: FxHashMap<ImportOrGlob, ImportOrDef>,
use_imports_macros: FxHashMap<ImportOrExternCrate, ImportOrDef>,
use_decls: Vec<UseId>,
extern_crate_decls: Vec<ExternCrateId>,
@ -242,7 +242,7 @@ impl ItemScope {
self.types.iter().map(|(n, &i)| (n, i))
}
pub fn macros(&self) -> impl Iterator<Item = (&Name, Item<MacroId, ImportOrGlob>)> + '_ {
pub fn macros(&self) -> impl Iterator<Item = (&Name, Item<MacroId, ImportOrExternCrate>)> + '_ {
self.macros.iter().map(|(n, &i)| (n, i))
}
@ -250,9 +250,9 @@ impl ItemScope {
self.use_imports_types
.keys()
.copied()
.chain(self.use_imports_macros.keys().copied())
.filter_map(ImportOrExternCrate::import_or_glob)
.chain(self.use_imports_values.keys().copied())
.chain(self.use_imports_macros.keys().copied())
.filter_map(ImportOrGlob::into_import)
.sorted()
.dedup()
@ -263,7 +263,7 @@ impl ItemScope {
let mut def_map;
let mut scope = self;
while let Some(&m) = scope.use_imports_macros.get(&ImportOrGlob::Import(import)) {
while let Some(&m) = scope.use_imports_macros.get(&ImportOrExternCrate::Import(import)) {
match m {
ImportOrDef::Import(i) => {
let module_id = i.use_.lookup(db).container;
@ -682,7 +682,6 @@ impl ItemScope {
}
_ => _ = glob_imports.macros.remove(&lookup),
}
let import = import.and_then(ImportOrExternCrate::import_or_glob);
let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import {
self.use_imports_macros.insert(
@ -698,7 +697,6 @@ impl ItemScope {
{
if glob_imports.macros.remove(&lookup) {
cov_mark::hit!(import_shadowed);
let import = import.and_then(ImportOrExternCrate::import_or_glob);
let prev = std::mem::replace(&mut fld.import, import);
if let Some(import) = import {
self.use_imports_macros.insert(
@ -783,8 +781,9 @@ impl ItemScope {
if let Some(Item { import, .. }) = def.macros {
buf.push_str(" m");
match import {
Some(ImportOrGlob::Import(_)) => buf.push('i'),
Some(ImportOrGlob::Glob(_)) => buf.push('g'),
Some(ImportOrExternCrate::Import(_)) => buf.push('i'),
Some(ImportOrExternCrate::Glob(_)) => buf.push('g'),
Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'),
None => (),
}
}
@ -893,9 +892,7 @@ impl PerNs {
ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import),
ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import),
ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import),
ModuleDefId::MacroId(mac) => {
PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::import_or_glob))
}
ModuleDefId::MacroId(mac) => PerNs::macros(mac, v, import),
}
}
}

View file

@ -179,7 +179,7 @@ impl ItemTree {
/// Returns the inner attributes of the source file.
pub fn top_level_attrs(&self, db: &dyn DefDatabase, krate: Crate) -> Attrs {
Attrs::filter(
Attrs::expand_cfg_attr(
db,
krate,
self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&RawAttrs::EMPTY).clone(),
@ -191,7 +191,7 @@ impl ItemTree {
}
pub(crate) fn attrs(&self, db: &dyn DefDatabase, krate: Crate, of: AttrOwner) -> Attrs {
Attrs::filter(db, krate, self.raw_attrs(of).clone())
Attrs::expand_cfg_attr(db, krate, self.raw_attrs(of).clone())
}
/// Returns a count of a few, expensive items.

View file

@ -83,6 +83,91 @@ impl LangItemTarget {
}
}
/// Salsa query. This will look for lang items in a specific crate.
#[salsa_macros::tracked(return_ref)]
pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangItems>> {
let _p = tracing::info_span!("crate_lang_items_query").entered();
let mut lang_items = LangItems::default();
let crate_def_map = db.crate_def_map(krate);
for (_, module_data) in crate_def_map.modules() {
for impl_def in module_data.scope.impls() {
lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef);
for &(_, assoc) in db.impl_items(impl_def).items.iter() {
match assoc {
AssocItemId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function)
}
AssocItemId::TypeAliasId(t) => {
lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias)
}
AssocItemId::ConstId(_) => (),
}
}
}
for def in module_data.scope.declarations() {
match def {
ModuleDefId::TraitId(trait_) => {
lang_items.collect_lang_item(db, trait_, LangItemTarget::Trait);
db.trait_items(trait_).items.iter().for_each(|&(_, assoc_id)| match assoc_id {
AssocItemId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function);
}
AssocItemId::TypeAliasId(alias) => {
lang_items.collect_lang_item(db, alias, LangItemTarget::TypeAlias)
}
AssocItemId::ConstId(_) => {}
});
}
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
lang_items.collect_lang_item(db, e, LangItemTarget::EnumId);
db.enum_variants(e).variants.iter().for_each(|&(id, _)| {
lang_items.collect_lang_item(db, id, LangItemTarget::EnumVariant);
});
}
ModuleDefId::AdtId(AdtId::StructId(s)) => {
lang_items.collect_lang_item(db, s, LangItemTarget::Struct);
}
ModuleDefId::AdtId(AdtId::UnionId(u)) => {
lang_items.collect_lang_item(db, u, LangItemTarget::Union);
}
ModuleDefId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function);
}
ModuleDefId::StaticId(s) => {
lang_items.collect_lang_item(db, s, LangItemTarget::Static);
}
ModuleDefId::TypeAliasId(t) => {
lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias);
}
_ => {}
}
}
}
if lang_items.items.is_empty() { None } else { Some(Box::new(lang_items)) }
}
/// Salsa query. Look for a lang item, starting from the specified crate and recursively
/// traversing its dependencies.
#[salsa_macros::tracked]
pub fn lang_item(
db: &dyn DefDatabase,
start_crate: Crate,
item: LangItem,
) -> Option<LangItemTarget> {
let _p = tracing::info_span!("lang_item_query").entered();
if let Some(target) =
crate_lang_items(db, start_crate).as_ref().and_then(|it| it.items.get(&item).copied())
{
return Some(target);
}
start_crate.data(db).dependencies.iter().find_map(|dep| lang_item(db, dep.crate_id, item))
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct LangItems {
items: FxHashMap<LangItem, LangItemTarget>,
@ -93,96 +178,6 @@ impl LangItems {
self.items.get(&item).copied()
}
/// Salsa query. This will look for lang items in a specific crate.
pub(crate) fn crate_lang_items_query(
db: &dyn DefDatabase,
krate: Crate,
) -> Option<Arc<LangItems>> {
let _p = tracing::info_span!("crate_lang_items_query").entered();
let mut lang_items = LangItems::default();
let crate_def_map = db.crate_def_map(krate);
for (_, module_data) in crate_def_map.modules() {
for impl_def in module_data.scope.impls() {
lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef);
for &(_, assoc) in db.impl_items(impl_def).items.iter() {
match assoc {
AssocItemId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function)
}
AssocItemId::TypeAliasId(t) => {
lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias)
}
AssocItemId::ConstId(_) => (),
}
}
}
for def in module_data.scope.declarations() {
match def {
ModuleDefId::TraitId(trait_) => {
lang_items.collect_lang_item(db, trait_, LangItemTarget::Trait);
db.trait_items(trait_).items.iter().for_each(
|&(_, assoc_id)| match assoc_id {
AssocItemId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function);
}
AssocItemId::TypeAliasId(alias) => lang_items.collect_lang_item(
db,
alias,
LangItemTarget::TypeAlias,
),
AssocItemId::ConstId(_) => {}
},
);
}
ModuleDefId::AdtId(AdtId::EnumId(e)) => {
lang_items.collect_lang_item(db, e, LangItemTarget::EnumId);
db.enum_variants(e).variants.iter().for_each(|&(id, _)| {
lang_items.collect_lang_item(db, id, LangItemTarget::EnumVariant);
});
}
ModuleDefId::AdtId(AdtId::StructId(s)) => {
lang_items.collect_lang_item(db, s, LangItemTarget::Struct);
}
ModuleDefId::AdtId(AdtId::UnionId(u)) => {
lang_items.collect_lang_item(db, u, LangItemTarget::Union);
}
ModuleDefId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function);
}
ModuleDefId::StaticId(s) => {
lang_items.collect_lang_item(db, s, LangItemTarget::Static);
}
ModuleDefId::TypeAliasId(t) => {
lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias);
}
_ => {}
}
}
}
if lang_items.items.is_empty() { None } else { Some(Arc::new(lang_items)) }
}
/// Salsa query. Look for a lang item, starting from the specified crate and recursively
/// traversing its dependencies.
pub(crate) fn lang_item_query(
db: &dyn DefDatabase,
start_crate: Crate,
item: LangItem,
) -> Option<LangItemTarget> {
let _p = tracing::info_span!("lang_item_query").entered();
if let Some(target) =
db.crate_lang_items(start_crate).and_then(|it| it.items.get(&item).copied())
{
return Some(target);
}
start_crate.data(db).dependencies.iter().find_map(|dep| db.lang_item(dep.crate_id, item))
}
fn collect_lang_item<T>(
&mut self,
db: &dyn DefDatabase,
@ -269,18 +264,38 @@ macro_rules! language_item_table {
}
impl LangItem {
pub fn resolve_function(self, db: &dyn DefDatabase, start_crate: Crate) -> Option<FunctionId> {
lang_item(db, start_crate, self).and_then(|t| t.as_function())
}
pub fn resolve_trait(self, db: &dyn DefDatabase, start_crate: Crate) -> Option<TraitId> {
lang_item(db, start_crate, self).and_then(|t| t.as_trait())
}
pub fn resolve_enum(self, db: &dyn DefDatabase, start_crate: Crate) -> Option<EnumId> {
lang_item(db, start_crate, self).and_then(|t| t.as_enum())
}
pub fn resolve_type_alias(
self,
db: &dyn DefDatabase,
start_crate: Crate,
) -> Option<TypeAliasId> {
lang_item(db, start_crate, self).and_then(|t| t.as_type_alias())
}
/// Opposite of [`LangItem::name`]
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
Self::from_symbol(name.symbol())
}
pub fn path(&self, db: &dyn DefDatabase, start_crate: Crate) -> Option<Path> {
let t = db.lang_item(start_crate, *self)?;
let t = lang_item(db, start_crate, *self)?;
Some(Path::LangItem(t, None))
}
pub fn ty_rel_path(&self, db: &dyn DefDatabase, start_crate: Crate, seg: Name) -> Option<Path> {
let t = db.lang_item(start_crate, *self)?;
let t = lang_item(db, start_crate, *self)?;
Some(Path::LangItem(t, Some(seg)))
}
}

View file

@ -64,8 +64,8 @@ use std::hash::{Hash, Hasher};
use base_db::{Crate, impl_intern_key};
use hir_expand::{
AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
MacroDefId, MacroDefKind,
AstId, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
MacroDefKind,
builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander},
db::ExpandDatabase,
eager::expand_eager_macro_input,
@ -79,7 +79,7 @@ use la_arena::Idx;
use nameres::DefMap;
use span::{AstIdNode, Edition, FileAstId, SyntaxContext};
use stdx::impl_from;
use syntax::{AstNode, ast};
use syntax::ast;
pub use hir_expand::{Intern, Lookup, tt};
@ -554,7 +554,7 @@ pub enum ItemContainerId {
impl_from!(ModuleId for ItemContainerId);
/// A Data Type
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum AdtId {
StructId(StructId),
UnionId(UnionId),
@ -563,7 +563,7 @@ pub enum AdtId {
impl_from!(StructId, UnionId, EnumId for AdtId);
/// A macro
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum MacroId {
Macro2Id(Macro2Id),
MacroRulesId(MacroRulesId),
@ -619,7 +619,7 @@ impl_from!(
/// A constant, which might appears as a const item, an anonymous const block in expressions
/// or patterns, or as a constant in types with const generics.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum GeneralConstId {
ConstId(ConstId),
StaticId(StaticId),
@ -656,7 +656,7 @@ impl GeneralConstId {
}
/// The defs which have a body (have root expressions for type inference).
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum DefWithBodyId {
FunctionId(FunctionId),
StaticId(StaticId),
@ -701,7 +701,7 @@ pub enum AssocItemId {
// casting them, and somehow making the constructors private, which would be annoying.
impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId);
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum GenericDefId {
AdtId(AdtId),
// consts can have type parameters from their parents (i.e. associated consts of traits)
@ -790,7 +790,7 @@ impl From<AssocItemId> for GenericDefId {
}
}
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum CallableDefId {
FunctionId(FunctionId),
StructId(StructId),
@ -906,7 +906,7 @@ impl From<VariantId> for AttrDefId {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum VariantId {
EnumVariantId(EnumVariantId),
StructId(StructId),
@ -1166,66 +1166,6 @@ impl ModuleDefId {
})
}
}
// FIXME: Replace this with a plain function, it only has one impl
/// A helper trait for converting to MacroCallId
trait AsMacroCall {
fn as_call_id_with_errors(
&self,
db: &dyn ExpandDatabase,
krate: Crate,
resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
eager_callback: &mut dyn FnMut(
InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
MacroCallId,
),
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
}
impl AsMacroCall for InFile<&ast::MacroCall> {
fn as_call_id_with_errors(
&self,
db: &dyn ExpandDatabase,
krate: Crate,
resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
eager_callback: &mut dyn FnMut(
InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
MacroCallId,
),
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
let span_map = db.span_map(self.file_id);
let path = self.value.path().and_then(|path| {
let range = path.syntax().text_range();
let mod_path = ModPath::from_src(db, path, &mut |range| {
span_map.as_ref().span_for_range(range).ctx
})?;
let call_site = span_map.span_for_range(range);
Some((call_site, mod_path))
});
let Some((call_site, path)) = path else {
return Ok(ExpandResult::only_err(ExpandError::other(
span_map.span_for_range(self.value.syntax().text_range()),
"malformed macro invocation",
)));
};
macro_call_as_call_id_with_eager(
db,
ast_id,
&path,
call_site.ctx,
expands_to,
krate,
resolver,
resolver,
eager_callback,
)
}
}
/// Helper wrapper for `AstId` with `ModPath`
#[derive(Clone, Debug, Eq, PartialEq)]
struct AstIdWithPath<T: AstIdNode> {
@ -1239,41 +1179,14 @@ impl<T: AstIdNode> AstIdWithPath<T> {
}
}
fn macro_call_as_call_id(
db: &dyn ExpandDatabase,
call: &AstIdWithPath<ast::MacroCall>,
call_site: SyntaxContext,
expand_to: ExpandTo,
krate: Crate,
resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
eager_callback: &mut dyn FnMut(
InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
MacroCallId,
),
) -> Result<Option<MacroCallId>, UnresolvedMacro> {
macro_call_as_call_id_with_eager(
db,
call.ast_id,
&call.path,
call_site,
expand_to,
krate,
resolver,
resolver,
eager_callback,
)
.map(|res| res.value)
}
fn macro_call_as_call_id_with_eager(
pub fn macro_call_as_call_id(
db: &dyn ExpandDatabase,
ast_id: AstId<ast::MacroCall>,
path: &ModPath,
call_site: SyntaxContext,
expand_to: ExpandTo,
krate: Crate,
resolver: impl FnOnce(&ModPath) -> Option<MacroDefId>,
eager_resolver: impl Fn(&ModPath) -> Option<MacroDefId>,
resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy,
eager_callback: &mut dyn FnMut(
InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>,
MacroCallId,
@ -1289,7 +1202,7 @@ fn macro_call_as_call_id_with_eager(
ast_id,
def,
call_site,
&|path| eager_resolver(path).filter(MacroDefId::is_fn_like),
&|path| resolver(path).filter(MacroDefId::is_fn_like),
eager_callback,
),
_ if def.is_fn_like() => ExpandResult {

Some files were not shown because too many files have changed in this diff Show more