提交 e026b59c 编写于 作者: B bors

Auto merge of #48694 - kennytm:rollup, r=kennytm

Rollup of 8 pull requests

- Successful merges: #48283, #48466, #48569, #48629, #48637, #48680, #48513, #48664
- Failed merges:
......@@ -50,3 +50,6 @@
[submodule "src/llvm-emscripten"]
path = src/llvm-emscripten
url = https://github.com/rust-lang/llvm
[submodule "src/stdsimd"]
path = src/stdsimd
url = https://github.com/rust-lang-nursery/stdsimd
......@@ -321,6 +321,9 @@
# bootstrap)
#codegen-backends = ["llvm"]
# This is the name of the directory in which codegen backends will get installed
#codegen-backends-dir = "codegen-backends"
# Flag indicating whether `libstd` calls an imported function to handle basic IO
# when targeting WebAssembly. Enable this to debug tests for the `wasm32-unknown-unknown`
# target, as without this option the test output will not be captured.
......
......@@ -314,7 +314,6 @@ class RustBuild(object):
self.build_dir = os.path.join(os.getcwd(), "build")
self.clean = False
self.config_toml = ''
self.printed = False
self.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
self.use_locked_deps = ''
self.use_vendored_sources = ''
......@@ -336,7 +335,6 @@ class RustBuild(object):
if self.rustc().startswith(self.bin_root()) and \
(not os.path.exists(self.rustc()) or
self.program_out_of_date(self.rustc_stamp())):
self.print_what_bootstrap_means()
if os.path.exists(self.bin_root()):
shutil.rmtree(self.bin_root())
filename = "rust-std-{}-{}.tar.gz".format(
......@@ -351,10 +349,17 @@ class RustBuild(object):
with open(self.rustc_stamp(), 'w') as rust_stamp:
rust_stamp.write(self.date)
# This is required so that we don't mix incompatible MinGW
# libraries/binaries that are included in rust-std with
# the system MinGW ones.
if "pc-windows-gnu" in self.build:
filename = "rust-mingw-{}-{}.tar.gz".format(
rustc_channel, self.build)
self._download_stage0_helper(filename, "rust-mingw")
if self.cargo().startswith(self.bin_root()) and \
(not os.path.exists(self.cargo()) or
self.program_out_of_date(self.cargo_stamp())):
self.print_what_bootstrap_means()
filename = "cargo-{}-{}.tar.gz".format(cargo_channel, self.build)
self._download_stage0_helper(filename, "cargo")
self.fix_executable("{}/bin/cargo".format(self.bin_root()))
......@@ -555,23 +560,6 @@ class RustBuild(object):
return '.exe'
return ''
def print_what_bootstrap_means(self):
"""Prints more information about the build system"""
if hasattr(self, 'printed'):
return
self.printed = True
if os.path.exists(self.bootstrap_binary()):
return
if '--help' not in sys.argv or len(sys.argv) == 1:
return
print('info: the build system for Rust is written in Rust, so this')
print(' script is now going to download a stage0 rust compiler')
print(' and then compile the build system itself')
print('')
print('info: in the meantime you can read more about rustbuild at')
print(' src/bootstrap/README.md before the download finishes')
def bootstrap_binary(self):
"""Return the path of the boostrap binary
......@@ -585,7 +573,6 @@ class RustBuild(object):
def build_bootstrap(self):
"""Build bootstrap"""
self.print_what_bootstrap_means()
build_dir = os.path.join(self.build_dir, "bootstrap")
if self.clean and os.path.exists(build_dir):
shutil.rmtree(build_dir)
......@@ -670,8 +657,16 @@ class RustBuild(object):
self._download_url = 'https://dev-static.rust-lang.org'
def bootstrap():
def bootstrap(help_triggered):
"""Configure, fetch, build and run the initial bootstrap"""
# If the user is asking for help, let them know that the whole download-and-build
# process has to happen before anything is printed out.
if help_triggered:
print("info: Downloading and building bootstrap before processing --help")
print(" command. See src/bootstrap/README.md for help with common")
print(" commands.")
parser = argparse.ArgumentParser(description='Build rust')
parser.add_argument('--config')
parser.add_argument('--build')
......@@ -708,7 +703,7 @@ def bootstrap():
print(' and so in order to preserve your $HOME this will now')
print(' use vendored sources by default. Note that if this')
print(' does not work you should run a normal build first')
print(' before running a command like `sudo make install`')
print(' before running a command like `sudo ./x.py install`')
if build.use_vendored_sources:
if not os.path.exists('.cargo'):
......@@ -734,7 +729,10 @@ def bootstrap():
if 'dev' in data:
build.set_dev_environment()
build.update_submodules()
# No help text depends on submodules. This check saves ~1 minute of git commands, even if
# all the submodules are present and downloaded!
if not help_triggered:
build.update_submodules()
# Fetch/build the bootstrap
build.build = args.build or build.build_triple()
......@@ -760,7 +758,7 @@ def main():
help_triggered = (
'-h' in sys.argv) or ('--help' in sys.argv) or (len(sys.argv) == 1)
try:
bootstrap()
bootstrap(help_triggered)
if not help_triggered:
print("Build completed successfully in {}".format(
format_build_time(time() - start_time)))
......
......@@ -464,7 +464,7 @@ fn run(self, builder: &Builder) -> Interned<PathBuf> {
pub fn sysroot_codegen_backends(&self, compiler: Compiler) -> PathBuf {
self.sysroot_libdir(compiler, compiler.host)
.with_file_name("codegen-backends")
.with_file_name(self.build.config.rust_codegen_backends_dir.clone())
}
/// Returns the compiler's libdir where it stores the dynamic libraries that
......
......@@ -514,7 +514,8 @@ fn rustc_cargo_env(build: &Build, cargo: &mut Command) {
cargo.env("CFG_RELEASE", build.rust_release())
.env("CFG_RELEASE_CHANNEL", &build.config.channel)
.env("CFG_VERSION", build.rust_version())
.env("CFG_PREFIX", build.config.prefix.clone().unwrap_or_default());
.env("CFG_PREFIX", build.config.prefix.clone().unwrap_or_default())
.env("CFG_CODEGEN_BACKENDS_DIR", &build.config.rust_codegen_backends_dir);
let libdir_relative = build.config.libdir_relative().unwrap_or(Path::new("lib"));
cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative);
......
......@@ -96,6 +96,7 @@ pub struct Config {
pub rust_debuginfo_tests: bool,
pub rust_dist_src: bool,
pub rust_codegen_backends: Vec<Interned<String>>,
pub rust_codegen_backends_dir: String,
pub build: Interned<String>,
pub hosts: Vec<Interned<String>>,
......@@ -289,6 +290,7 @@ struct Rust {
test_miri: Option<bool>,
save_toolstates: Option<String>,
codegen_backends: Option<Vec<String>>,
codegen_backends_dir: Option<String>,
wasm_syscall: Option<bool>,
}
......@@ -330,6 +332,7 @@ pub fn parse(args: &[String]) -> Config {
config.rust_dist_src = true;
config.test_miri = false;
config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")];
config.rust_codegen_backends_dir = "codegen-backends".to_owned();
config.rustc_error_format = flags.rustc_error_format;
config.on_fail = flags.on_fail;
......@@ -488,6 +491,8 @@ pub fn parse(args: &[String]) -> Config {
.collect();
}
set(&mut config.rust_codegen_backends_dir, rust.codegen_backends_dir.clone());
match rust.codegen_units {
Some(0) => config.rust_codegen_units = Some(num_cpus::get() as u32),
Some(n) => config.rust_codegen_units = Some(n),
......
......@@ -590,7 +590,8 @@ fn run(self, builder: &Builder) -> PathBuf {
let mut src = builder.sysroot_libdir(compiler, target).to_path_buf();
src.pop(); // Remove the trailing /lib folder from the sysroot_libdir
cp_filtered(&src, &dst, &|path| {
path.file_name().and_then(|s| s.to_str()) != Some("codegen-backends")
path.file_name().and_then(|s| s.to_str()) !=
Some(build.config.rust_codegen_backends_dir.as_str())
});
let mut cmd = rust_installer(builder);
......
......@@ -312,6 +312,7 @@ fn invoke_rustdoc(builder: &Builder, compiler: Compiler, target: Interned<String
cmd.arg("--html-after-content").arg(&footer)
.arg("--html-before-content").arg(&version_info)
.arg("--html-in-header").arg(&favicon)
.arg("--markdown-no-toc")
.arg("--markdown-playground-url")
.arg("https://play.rust-lang.org/")
.arg("-o").arg(&out)
......
......@@ -68,16 +68,21 @@
#![feature(allow_internal_unstable)]
#![feature(asm)]
#![feature(associated_type_defaults)]
#![feature(attr_literals)]
#![feature(cfg_target_feature)]
#![feature(cfg_target_has_atomic)]
#![feature(concat_idents)]
#![feature(const_fn)]
#![feature(custom_attribute)]
#![feature(doc_spotlight)]
#![feature(fundamental)]
#![feature(i128_type)]
#![feature(inclusive_range_syntax)]
#![feature(intrinsics)]
#![feature(iterator_flatten)]
#![feature(iterator_repeat_with)]
#![feature(lang_items)]
#![feature(link_llvm_intrinsics)]
#![feature(never_type)]
#![feature(no_core)]
#![feature(on_unimplemented)]
......@@ -85,15 +90,17 @@
#![feature(prelude_import)]
#![feature(repr_simd, platform_intrinsics)]
#![feature(rustc_attrs)]
#![feature(rustc_const_unstable)]
#![feature(simd_ffi)]
#![feature(specialization)]
#![feature(staged_api)]
#![feature(stmt_expr_attributes)]
#![feature(target_feature)]
#![feature(unboxed_closures)]
#![feature(untagged_unions)]
#![feature(unwind_attributes)]
#![feature(doc_spotlight)]
#![feature(rustc_const_unstable)]
#![feature(iterator_repeat_with)]
#![feature(iterator_flatten)]
#![cfg_attr(stage0, allow(unused_attributes))]
#[prelude_import]
#[allow(unused)]
......@@ -179,3 +186,21 @@
mod iter_private;
mod tuple;
mod unit;
// Pull in the the `coresimd` crate directly into libcore. This is where all the
// architecture-specific (and vendor-specific) intrinsics are defined. AKA
// things like SIMD and such. Note that the actual source for all this lies in a
// different repository, rust-lang-nursery/stdsimd. That's why the setup here is
// a bit wonky.
#[path = "../stdsimd/coresimd/mod.rs"]
#[allow(missing_docs, missing_debug_implementations, dead_code)]
#[unstable(feature = "stdsimd", issue = "48556")]
#[cfg(not(stage0))] // allow changes to how stdsimd works in stage0
mod coresimd;
#[unstable(feature = "stdsimd", issue = "48556")]
#[cfg(not(stage0))]
pub use coresimd::simd;
#[unstable(feature = "stdsimd", issue = "48556")]
#[cfg(not(stage0))]
pub use coresimd::arch;
......@@ -12,13 +12,7 @@
use target::{Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::linux_musl_base::opts();
// Most of these settings are copied from the armv7_unknown_linux_gnueabihf
// target.
base.features = "+v7,+vfp3,+neon".to_string();
base.cpu = "cortex-a8".to_string();
base.max_atomic_width = Some(64);
let base = super::linux_musl_base::opts();
Ok(Target {
// It's important we use "gnueabihf" and not "musleabihf" here. LLVM
// uses it to determine the calling convention and float ABI, and LLVM
......@@ -33,9 +27,15 @@ pub fn target() -> TargetResult {
target_env: "musl".to_string(),
target_vendor: "unknown".to_string(),
linker_flavor: LinkerFlavor::Gcc,
// Most of these settings are copied from the armv7_unknown_linux_gnueabihf
// target.
options: TargetOptions {
features: "+v7,+vfp3,+d16,+thumb2,-neon".to_string(),
cpu: "generic".to_string(),
max_atomic_width: Some(64),
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
},
}
})
}
......@@ -303,7 +303,9 @@ fn get_trans_sysroot(backend_name: &str) -> fn() -> Box<TransCrate> {
let sysroot = sysroot_candidates.iter()
.map(|sysroot| {
let libdir = filesearch::relative_target_lib_path(&sysroot, &target);
sysroot.join(libdir).with_file_name("codegen-backends")
sysroot.join(libdir)
.with_file_name(option_env!("CFG_CODEGEN_BACKENDS_DIR")
.unwrap_or("codegen-backends"))
})
.filter(|f| {
info!("codegen backend candidate: {}", f.display());
......
# The walking tour of rustdoc
Rustdoc is implemented entirely within the crate `librustdoc`. After partially compiling a crate to
get its AST (technically the HIR map) from rustc, librustdoc performs two major steps past that to
render a set of documentation:
* "Clean" the AST into a form that's more suited to creating documentation (and slightly more
resistant to churn in the compiler).
* Use this cleaned AST to render a crate's documentation, one page at a time.
Naturally, there's more than just this, and those descriptions simplify out lots of details, but
that's the high-level overview.
(Side note: this is a library crate! The `rustdoc` binary is crated using the project in
`src/tools/rustdoc`. Note that literally all that does is call the `main()` that's in this crate's
`lib.rs`, though.)
## Cheat sheet
* Use `x.py build --stage 1 src/libstd src/tools/rustdoc` to make a useable rustdoc you can run on
other projects.
* Add `src/libtest` to be able to use `rustdoc --test`.
* If you've used `rustup toolchain link local /path/to/build/$TARGET/stage1` previously, then
after the previous build command, `cargo +local doc` will Just Work.
* Use `x.py doc --stage 1 src/libstd` to use this rustdoc to generate the standard library docs.
* The completed docs will be available in `build/$TARGET/doc/std`, though the bundle is meant to
be used as though you would copy out the `doc` folder to a web server, since that's where the
CSS/JS and landing page are.
* Most of the HTML printing code is in `html/format.rs` and `html/render.rs`. It's in a bunch of
`fmt::Display` implementations and supplementary functions.
* The types that got `Display` impls above are defined in `clean/mod.rs`, right next to the custom
`Clean` trait used to process them out of the rustc HIR.
* The bits specific to using rustdoc as a test harness are in `test.rs`.
* The Markdown renderer is loaded up in `html/markdown.rs`, including functions for extracting
doctests from a given block of Markdown.
* The tests on rustdoc *output* are located in `src/test/rustdoc`, where they're handled by the test
runner of rustbuild and the supplementary script `src/etc/htmldocck.py`.
* Tests on search index generation are located in `src/test/rustdoc-js`, as a series of JavaScript
files that encode queries on the standard library search index and expected results.
## From crate to clean
In `core.rs` are two central items: the `DocContext` struct, and the `run_core` function. The latter
is where rustdoc calls out to rustc to compile a crate to the point where rustdoc can take over. The
former is a state container used when crawling through a crate to gather its documentation.
The main process of crate crawling is done in `clean/mod.rs` through several implementations of the
`Clean` trait defined within. This is a conversion trait, which defines one method:
```rust
pub trait Clean<T> {
fn clean(&self, cx: &DocContext) -> T;
}
```
`clean/mod.rs` also defines the types for the "cleaned" AST used later on to render documentation
pages. Each usually accompanies an implementation of `Clean` that takes some AST or HIR type from
rustc and converts it into the appropriate "cleaned" type. "Big" items like modules or associated
items may have some extra processing in its `Clean` implementation, but for the most part these
impls are straightforward conversions. The "entry point" to this module is the `impl Clean<Crate>
for visit_ast::RustdocVisitor`, which is called by `run_core` above.
You see, I actually lied a little earlier: There's another AST transformation that happens before
the events in `clean/mod.rs`. In `visit_ast.rs` is the type `RustdocVisitor`, which *actually*
crawls a `hir::Crate` to get the first intermediate representation, defined in `doctree.rs`. This
pass is mainly to get a few intermediate wrappers around the HIR types and to process visibility
and inlining. This is where `#[doc(inline)]`, `#[doc(no_inline)]`, and `#[doc(hidden)]` are
processed, as well as the logic for whether a `pub use` should get the full page or a "Reexport"
line in the module page.
The other major thing that happens in `clean/mod.rs` is the collection of doc comments and
`#[doc=""]` attributes into a separate field of the Attributes struct, present on anything that gets
hand-written documentation. This makes it easier to collect this documentation later in the process.
The primary output of this process is a clean::Crate with a tree of Items which describe the
publicly-documentable items in the target crate.
### Hot potato
Before moving on to the next major step, a few important "passes" occur over the documentation.
These do things like combine the separate "attributes" into a single string and strip leading
whitespace to make the document easier on the markdown parser, or drop items that are not public or
deliberately hidden with `#[doc(hidden)]`. These are all implemented in the `passes/` directory, one
file per pass. By default, all of these passes are run on a crate, but the ones regarding dropping
private/hidden items can be bypassed by passing `--document-private-items` to rustdoc.
(Strictly speaking, you can fine-tune the passes run and even add your own, but [we're trying to
deprecate that][44136]. If you need finer-grain control over these passes, please let us know!)
[44136]: https://github.com/rust-lang/rust/issues/44136
## From clean to crate
This is where the "second phase" in rustdoc begins. This phase primarily lives in the `html/`
folder, and it all starts with `run()` in `html/render.rs`. This code is responsible for setting up
the `Context`, `SharedContext`, and `Cache` which are used during rendering, copying out the static
files which live in every rendered set of documentation (things like the fonts, CSS, and JavaScript
that live in `html/static/`), creating the search index, and printing out the source code rendering,
before beginning the process of rendering all the documentation for the crate.
Several functions implemented directly on `Context` take the `clean::Crate` and set up some state
between rendering items or recursing on a module's child items. From here the "page rendering"
begins, via an enormous `write!()` call in `html/layout.rs`. The parts that actually generate HTML
from the items and documentation occurs within a series of `std::fmt::Display` implementations and
functions that pass around a `&mut std::fmt::Formatter`. The top-level implementation that writes
out the page body is the `impl<'a> fmt::Display for Item<'a>` in `html/render.rs`, which switches
out to one of several `item_*` functions based on the kind of `Item` being rendered.
Depending on what kind of rendering code you're looking for, you'll probably find it either in
`html/render.rs` for major items like "what sections should I print for a struct page" or
`html/format.rs` for smaller component pieces like "how should I print a where clause as part of
some other item".
Whenever rustdoc comes across an item that should print hand-written documentation alongside, it
calls out to `html/markdown.rs` which interfaces with the Markdown parser. This is exposed as a
series of types that wrap a string of Markdown, and implement `fmt::Display` to emit HTML text. It
takes special care to enable certain features like footnotes and tables and add syntax highlighting
to Rust code blocks (via `html/highlight.rs`) before running the Markdown parser. There's also a
function in here (`find_testable_code`) that specifically scans for Rust code blocks so the
test-runner code can find all the doctests in the crate.
### From soup to nuts
(alternate title: ["An unbroken thread that stretches from those first `Cell`s to us"][video])
[video]: https://www.youtube.com/watch?v=hOLAGYmUQV0
It's important to note that the AST cleaning can ask the compiler for information (crucially,
`DocContext` contains a `TyCtxt`), but page rendering cannot. The `clean::Crate` created within
`run_core` is passed outside the compiler context before being handed to `html::render::run`. This
means that a lot of the "supplementary data" that isn't immediately available inside an item's
definition, like which trait is the `Deref` trait used by the language, needs to be collected during
cleaning, stored in the `DocContext`, and passed along to the `SharedContext` during HTML rendering.
This manifests as a bunch of shared state, context variables, and `RefCell`s.
Also of note is that some items that come from "asking the compiler" don't go directly into the
`DocContext` - for example, when loading items from a foreign crate, rustdoc will ask about trait
implementations and generate new `Item`s for the impls based on that information. This goes directly
into the returned `Crate` rather than roundabout through the `DocContext`. This way, these
implementations can be collected alongside the others, right before rendering the HTML.
## Other tricks up its sleeve
All this describes the process for generating HTML documentation from a Rust crate, but there are
couple other major modes that rustdoc runs in. It can also be run on a standalone Markdown file, or
it can run doctests on Rust code or standalone Markdown files. For the former, it shortcuts straight
to `html/markdown.rs`, optionally including a mode which inserts a Table of Contents to the output
HTML.
For the latter, rustdoc runs a similar partial-compilation to get relevant documentation in
`test.rs`, but instead of going through the full clean and render process, it runs a much simpler
crate walk to grab *just* the hand-written documentation. Combined with the aforementioned
"`find_testable_code`" in `html/markdown.rs`, it builds up a collection of tests to run before
handing them off to the libtest test runner. One notable location in `test.rs` is the function
`make_test`, which is where hand-written doctests get transformed into something that can be
executed.
## Dotting i's and crossing t's
So that's rustdoc's code in a nutshell, but there's more things in the repo that deal with it. Since
we have the full `compiletest` suite at hand, there's a set of tests in `src/test/rustdoc` that make
sure the final HTML is what we expect in various situations. These tests also use a supplementary
script, `src/etc/htmldocck.py`, that allows it to look through the final HTML using XPath notation
to get a precise look at the output. The full description of all the commands available to rustdoc
tests is in `htmldocck.py`.
In addition, there are separate tests for the search index and rustdoc's ability to query it. The
files in `src/test/rustdoc-js` each contain a different search query and the expected results,
broken out by search tab. These files are processed by a script in `src/tools/rustdoc-js` and the
Node.js runtime. These tests don't have as thorough of a writeup, but a broad example that features
results in all tabs can be found in `basic.js`. The basic idea is that you match a given `QUERY`
with a set of `EXPECTED` results, complete with the full item path of each item.
......@@ -299,6 +299,7 @@
#![feature(rand)]
#![feature(raw)]
#![feature(rustc_attrs)]
#![feature(stdsimd)]
#![feature(sip_hash_13)]
#![feature(slice_bytes)]
#![feature(slice_concat_ext)]
......@@ -501,6 +502,35 @@
// compiler
pub mod rt;
// Pull in the the `stdsimd` crate directly into libstd. This is the same as
// libcore's arch/simd modules where the source of truth here is in a different
// repository, but we pull things in here manually to get it into libstd.
//
// Note that the #[cfg] here is intended to do two things. First it allows us to
// change the rustc implementation of intrinsics in stage0 by not compiling simd
// intrinsics in stage0. Next it doesn't compile anything in test mode as
// stdsimd has tons of its own tests which we don't want to run.
#[path = "../stdsimd/stdsimd/mod.rs"]
#[allow(missing_debug_implementations, missing_docs, dead_code)]
#[unstable(feature = "stdsimd", issue = "48556")]
#[cfg(all(not(stage0), not(test)))]
mod stdsimd;
// A "fake" module needed by the `stdsimd` module to compile, not actually
// exported though.
#[cfg(not(stage0))]
mod coresimd {
pub use core::arch;
pub use core::simd;
}
#[unstable(feature = "stdsimd", issue = "48556")]
#[cfg(all(not(stage0), not(test)))]
pub use stdsimd::simd;
#[unstable(feature = "stdsimd", issue = "48556")]
#[cfg(all(not(stage0), not(test)))]
pub use stdsimd::arch;
// Include a number of private modules that exist solely to provide
// the rustdoc documentation for primitive types. Using `include!`
// because rustdoc only looks for these modules at the crate level.
......
......@@ -59,7 +59,7 @@
/// [`to_lowercase`]: ../../std/primitive.char.html#method.to_lowercase
/// [`char`]: ../../std/primitive.char.html
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ToLowercase(CaseMappingIter);
#[stable(feature = "rust1", since = "1.0.0")]
......@@ -81,7 +81,7 @@ impl FusedIterator for ToLowercase {}
/// [`to_uppercase`]: ../../std/primitive.char.html#method.to_uppercase
/// [`char`]: ../../std/primitive.char.html
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ToUppercase(CaseMappingIter);
#[stable(feature = "rust1", since = "1.0.0")]
......@@ -95,7 +95,7 @@ fn next(&mut self) -> Option<char> {
#[unstable(feature = "fused", issue = "35602")]
impl FusedIterator for ToUppercase {}
#[derive(Debug)]
#[derive(Debug, Clone)]
enum CaseMappingIter {
Three(char, char, char),
Two(char, char),
......
Subproject commit 678cbd325c84070c9dbe4303969fbd2734c0b4ee
......@@ -71,6 +71,7 @@ fn filter_dirs(path: &Path) -> bool {
"src/librustc/mir/interpret",
"src/librustc_mir/interpret",
"src/target",
"src/stdsimd",
];
skip.iter().any(|p| path.ends_with(p))
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册