Merge branch 'master' into issue-30961
This commit is contained in:
commit
79358aa523
80 changed files with 2169 additions and 2561 deletions
19
configure
vendored
19
configure
vendored
|
|
@ -600,7 +600,7 @@ opt debug-assertions 0 "build with debugging assertions"
|
|||
opt fast-make 0 "use .gitmodules as timestamp for submodule deps"
|
||||
opt ccache 0 "invoke gcc/clang via ccache to reuse object files between builds"
|
||||
opt local-rust 0 "use an installed rustc rather than downloading a snapshot"
|
||||
opt local-rebuild 0 "use an installed rustc matching the current version, for rebuilds"
|
||||
opt local-rebuild 0 "assume local-rust matches the current version, for rebuilds; implies local-rust, and is implied if local-rust already matches the current version"
|
||||
opt llvm-static-stdcpp 0 "statically link to libstdc++ for LLVM"
|
||||
opt rpath 1 "build rpaths into rustc itself"
|
||||
opt stage0-landing-pads 1 "enable landing pads during bootstrap with stage0"
|
||||
|
|
@ -612,6 +612,7 @@ opt rustbuild 0 "use the rust and cargo based build system"
|
|||
opt orbit 0 "get MIR where it belongs - everywhere; most importantly, in orbit"
|
||||
opt codegen-tests 1 "run the src/test/codegen tests"
|
||||
opt option-checking 1 "complain about unrecognized options in this configure script"
|
||||
opt ninja 0 "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)"
|
||||
|
||||
# Optimization and debugging options. These may be overridden by the release channel, etc.
|
||||
opt_nosave optimize 1 "build optimized rust code"
|
||||
|
|
@ -785,6 +786,17 @@ probe CFG_BISON bison
|
|||
probe CFG_GDB gdb
|
||||
probe CFG_LLDB lldb
|
||||
|
||||
if [ -n "$CFG_ENABLE_NINJA" ]
|
||||
then
|
||||
probe CFG_NINJA ninja
|
||||
if [ -z "$CFG_NINJA" ]
|
||||
then
|
||||
# On Debian and Fedora, the `ninja` binary is an IRC bot, so the build tool was
|
||||
# renamed. Handle this case.
|
||||
probe CFG_NINJA ninja-build
|
||||
fi
|
||||
fi
|
||||
|
||||
# For building LLVM
|
||||
probe_need CFG_CMAKE cmake
|
||||
|
||||
|
|
@ -1534,7 +1546,10 @@ do
|
|||
fi
|
||||
|
||||
# We need the generator later on for compiler-rt even if LLVM's not built
|
||||
if [ ${is_msvc} -ne 0 ]
|
||||
if [ -n "$CFG_NINJA" ]
|
||||
then
|
||||
generator="Ninja"
|
||||
elif [ ${is_msvc} -ne 0 ]
|
||||
then
|
||||
case "$CFG_MSVC_ROOT" in
|
||||
*14.0*)
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ CFG_LIB_NAME_mips-unknown-linux-gnu=lib$(1).so
|
|||
CFG_STATIC_LIB_NAME_mips-unknown-linux-gnu=lib$(1).a
|
||||
CFG_LIB_GLOB_mips-unknown-linux-gnu=lib$(1)-*.so
|
||||
CFG_LIB_DSYM_GLOB_mips-unknown-linux-gnu=lib$(1)-*.dylib.dSYM
|
||||
CFG_JEMALLOC_CFLAGS_mips-unknown-linux-gnu := -mips32r2 -msoft-float -mabi=32 $(CFLAGS)
|
||||
CFG_GCCISH_CFLAGS_mips-unknown-linux-gnu := -Wall -g -fPIC -mips32r2 -msoft-float -mabi=32 $(CFLAGS)
|
||||
CFG_JEMALLOC_CFLAGS_mips-unknown-linux-gnu := -mips32r2 -mabi=32 $(CFLAGS)
|
||||
CFG_GCCISH_CFLAGS_mips-unknown-linux-gnu := -Wall -g -fPIC -mips32r2 -mabi=32 $(CFLAGS)
|
||||
CFG_GCCISH_CXXFLAGS_mips-unknown-linux-gnu := -fno-rtti $(CXXFLAGS)
|
||||
CFG_GCCISH_LINK_FLAGS_mips-unknown-linux-gnu := -shared -fPIC -g -mips32r2 -msoft-float -mabi=32
|
||||
CFG_GCCISH_LINK_FLAGS_mips-unknown-linux-gnu := -shared -fPIC -g -mips32r2 -mabi=32
|
||||
CFG_GCCISH_DEF_FLAG_mips-unknown-linux-gnu := -Wl,--export-dynamic,--dynamic-list=
|
||||
CFG_LLC_FLAGS_mips-unknown-linux-gnu :=
|
||||
CFG_INSTALL_NAME_mips-unknown-linux-gnu =
|
||||
|
|
|
|||
14
mk/llvm.mk
14
mk/llvm.mk
|
|
@ -43,7 +43,9 @@ $$(LLVM_CONFIG_$(1)): $$(LLVM_DONE_$(1))
|
|||
|
||||
$$(LLVM_DONE_$(1)): $$(LLVM_DEPS_TARGET_$(1)) $$(LLVM_STAMP_$(1))
|
||||
@$$(call E, cmake: llvm)
|
||||
ifeq ($$(findstring msvc,$(1)),msvc)
|
||||
ifneq ($$(CFG_NINJA),)
|
||||
$$(Q)$$(CFG_NINJA) -C $$(CFG_LLVM_BUILD_DIR_$(1))
|
||||
else ifeq ($$(findstring msvc,$(1)),msvc)
|
||||
$$(Q)$$(CFG_CMAKE) --build $$(CFG_LLVM_BUILD_DIR_$(1)) \
|
||||
--config $$(LLVM_BUILD_CONFIG_MODE)
|
||||
else
|
||||
|
|
@ -51,8 +53,16 @@ else
|
|||
endif
|
||||
$$(Q)touch $$@
|
||||
|
||||
ifeq ($$(findstring msvc,$(1)),msvc)
|
||||
ifneq ($$(CFG_NINJA),)
|
||||
clean-llvm$(1):
|
||||
@$$(call E, clean: llvm)
|
||||
$$(Q)$$(CFG_NINJA) -C $$(CFG_LLVM_BUILD_DIR_$(1)) -t clean
|
||||
else ifeq ($$(findstring msvc,$(1)),msvc)
|
||||
clean-llvm$(1):
|
||||
@$$(call E, clean: llvm)
|
||||
$$(Q)$$(CFG_CMAKE) --build $$(CFG_LLVM_BUILD_DIR_$(1)) \
|
||||
--config $$(LLVM_BUILD_CONFIG_MODE) \
|
||||
--target clean
|
||||
else
|
||||
clean-llvm$(1):
|
||||
@$$(call E, clean: llvm)
|
||||
|
|
|
|||
55
mk/main.mk
55
mk/main.mk
|
|
@ -20,29 +20,6 @@ CFG_RELEASE_NUM=1.12.0
|
|||
# versions (section 9)
|
||||
CFG_PRERELEASE_VERSION=.1
|
||||
|
||||
# Append a version-dependent hash to each library, so we can install different
|
||||
# versions in the same place
|
||||
CFG_FILENAME_EXTRA=$(shell printf '%s' $(CFG_RELEASE)$(CFG_EXTRA_FILENAME) | $(CFG_HASH_COMMAND))
|
||||
|
||||
# A magic value that allows the compiler to use unstable features during the
|
||||
# bootstrap even when doing so would normally be an error because of feature
|
||||
# staging or because the build turns on warnings-as-errors and unstable features
|
||||
# default to warnings. The build has to match this key in an env var.
|
||||
#
|
||||
# This value is keyed off the release to ensure that all compilers for one
|
||||
# particular release have the same bootstrap key. Note that this is
|
||||
# intentionally not "secure" by any definition, this is largely just a deterrent
|
||||
# from users enabling unstable features on the stable compiler.
|
||||
CFG_BOOTSTRAP_KEY=$(CFG_FILENAME_EXTRA)
|
||||
|
||||
# The stage0 compiler needs to use the previous key recorded in src/stage0.txt,
|
||||
# except for local-rebuild when it just uses the same current key.
|
||||
ifdef CFG_ENABLE_LOCAL_REBUILD
|
||||
CFG_BOOTSTRAP_KEY_STAGE0=$(CFG_BOOTSTRAP_KEY)
|
||||
else
|
||||
CFG_BOOTSTRAP_KEY_STAGE0=$(shell grep 'rustc_key' $(S)src/stage0.txt | sed 's/rustc_key: '//)
|
||||
endif
|
||||
|
||||
ifeq ($(CFG_RELEASE_CHANNEL),stable)
|
||||
# This is the normal semver version string, e.g. "0.12.0", "0.12.0-nightly"
|
||||
CFG_RELEASE=$(CFG_RELEASE_NUM)
|
||||
|
|
@ -72,6 +49,38 @@ CFG_RELEASE=$(CFG_RELEASE_NUM)-dev
|
|||
CFG_PACKAGE_VERS=$(CFG_RELEASE_NUM)-dev
|
||||
endif
|
||||
|
||||
# Append a version-dependent hash to each library, so we can install different
|
||||
# versions in the same place
|
||||
CFG_FILENAME_EXTRA=$(shell printf '%s' $(CFG_RELEASE)$(CFG_EXTRA_FILENAME) | $(CFG_HASH_COMMAND))
|
||||
|
||||
# A magic value that allows the compiler to use unstable features during the
|
||||
# bootstrap even when doing so would normally be an error because of feature
|
||||
# staging or because the build turns on warnings-as-errors and unstable features
|
||||
# default to warnings. The build has to match this key in an env var.
|
||||
#
|
||||
# This value is keyed off the release to ensure that all compilers for one
|
||||
# particular release have the same bootstrap key. Note that this is
|
||||
# intentionally not "secure" by any definition, this is largely just a deterrent
|
||||
# from users enabling unstable features on the stable compiler.
|
||||
CFG_BOOTSTRAP_KEY=$(CFG_FILENAME_EXTRA)
|
||||
|
||||
# If local-rust is the same as the current version, then force a local-rebuild
|
||||
ifdef CFG_ENABLE_LOCAL_RUST
|
||||
ifeq ($(CFG_RELEASE),\
|
||||
$(shell $(S)src/etc/local_stage0.sh --print-rustc-release $(CFG_LOCAL_RUST_ROOT)))
|
||||
CFG_INFO := $(info cfg: auto-detected local-rebuild $(CFG_RELEASE))
|
||||
CFG_ENABLE_LOCAL_REBUILD = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
# The stage0 compiler needs to use the previous key recorded in src/stage0.txt,
|
||||
# except for local-rebuild when it just uses the same current key.
|
||||
ifdef CFG_ENABLE_LOCAL_REBUILD
|
||||
CFG_BOOTSTRAP_KEY_STAGE0=$(CFG_BOOTSTRAP_KEY)
|
||||
else
|
||||
CFG_BOOTSTRAP_KEY_STAGE0=$(shell sed -ne 's/^rustc_key: //p' $(S)src/stage0.txt)
|
||||
endif
|
||||
|
||||
# The name of the package to use for creating tarballs, installers etc.
|
||||
CFG_PACKAGE_NAME=rustc-$(CFG_PACKAGE_VERS)
|
||||
|
||||
|
|
|
|||
7
mk/rt.mk
7
mk/rt.mk
|
|
@ -350,10 +350,17 @@ $$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) $$(LLVM_CONFIG_$$(CFG_BUILD
|
|||
$$(COMPRT_DEFINES_$(1)) \
|
||||
$$(COMPRT_BUILD_CC_$(1)) \
|
||||
-G"$$(CFG_CMAKE_GENERATOR)"
|
||||
ifneq ($$(CFG_NINJA),)
|
||||
$$(CFG_CMAKE) --build "$$(COMPRT_BUILD_DIR_$(1))" \
|
||||
--target $$(COMPRT_BUILD_TARGET_$(1)) \
|
||||
--config $$(LLVM_BUILD_CONFIG_MODE) \
|
||||
-- $$(COMPRT_BUILD_ARGS_$(1))
|
||||
else
|
||||
$$(Q)$$(CFG_CMAKE) --build "$$(COMPRT_BUILD_DIR_$(1))" \
|
||||
--target $$(COMPRT_BUILD_TARGET_$(1)) \
|
||||
--config $$(LLVM_BUILD_CONFIG_MODE) \
|
||||
-- $$(COMPRT_BUILD_ARGS_$(1)) $$(MFLAGS)
|
||||
endif
|
||||
$$(Q)cp "$$(COMPRT_OUTPUT_$(1))" $$@
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ endif
|
|||
|
||||
$(SNAPSHOT_RUSTC_POST_CLEANUP): \
|
||||
$(S)src/stage0.txt \
|
||||
$(S)src/etc/local_stage0.sh \
|
||||
$(S)src/etc/get-stage0.py $(MKFILE_DEPS) \
|
||||
| $(HBIN0_H_$(CFG_BUILD))/
|
||||
@$(call E, fetch: $@)
|
||||
|
|
|
|||
|
|
@ -317,6 +317,7 @@ impl Config {
|
|||
("OPTIMIZE_TESTS", self.rust_optimize_tests),
|
||||
("DEBUGINFO_TESTS", self.rust_debuginfo_tests),
|
||||
("LOCAL_REBUILD", self.local_rebuild),
|
||||
("NINJA", self.ninja),
|
||||
}
|
||||
|
||||
match key {
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ pub struct Build {
|
|||
ver_date: Option<String>,
|
||||
version: String,
|
||||
package_vers: String,
|
||||
local_rebuild: bool,
|
||||
bootstrap_key: String,
|
||||
bootstrap_key_stage0: String,
|
||||
|
||||
|
|
@ -174,6 +175,7 @@ impl Build {
|
|||
Some(ref s) => PathBuf::from(s),
|
||||
None => stage0_root.join(exe("cargo", &config.build)),
|
||||
};
|
||||
let local_rebuild = config.local_rebuild;
|
||||
|
||||
Build {
|
||||
flags: flags,
|
||||
|
|
@ -189,6 +191,7 @@ impl Build {
|
|||
short_ver_hash: None,
|
||||
ver_date: None,
|
||||
version: String::new(),
|
||||
local_rebuild: local_rebuild,
|
||||
bootstrap_key: String::new(),
|
||||
bootstrap_key_stage0: String::new(),
|
||||
package_vers: String::new(),
|
||||
|
|
@ -219,6 +222,16 @@ impl Build {
|
|||
sanity::check(self);
|
||||
self.verbose("collecting channel variables");
|
||||
channel::collect(self);
|
||||
// If local-rust is the same as the current version, then force a local-rebuild
|
||||
let local_version_verbose = output(
|
||||
Command::new(&self.rustc).arg("--version").arg("--verbose"));
|
||||
let local_release = local_version_verbose
|
||||
.lines().filter(|x| x.starts_with("release:"))
|
||||
.next().unwrap().trim_left_matches("release:").trim();
|
||||
if local_release == self.release {
|
||||
self.verbose(&format!("auto-detected local-rebuild {}", self.release));
|
||||
self.local_rebuild = true;
|
||||
}
|
||||
self.verbose("updating submodules");
|
||||
self.update_submodules();
|
||||
|
||||
|
|
@ -525,7 +538,7 @@ impl Build {
|
|||
.arg("--target").arg(target);
|
||||
|
||||
let stage;
|
||||
if compiler.stage == 0 && self.config.local_rebuild {
|
||||
if compiler.stage == 0 && self.local_rebuild {
|
||||
// Assume the local-rebuild rustc already has stage1 features.
|
||||
stage = 1;
|
||||
} else {
|
||||
|
|
@ -766,7 +779,7 @@ impl Build {
|
|||
// In stage0 we're using a previously released stable compiler, so we
|
||||
// use the stage0 bootstrap key. Otherwise we use our own build's
|
||||
// bootstrap key.
|
||||
let bootstrap_key = if compiler.is_snapshot(self) && !self.config.local_rebuild {
|
||||
let bootstrap_key = if compiler.is_snapshot(self) && !self.local_rebuild {
|
||||
&self.bootstrap_key_stage0
|
||||
} else {
|
||||
&self.bootstrap_key
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ this using our `Cargo.toml` file:
|
|||
|
||||
```toml
|
||||
[dependencies]
|
||||
libc = { version = "0.2.11", default-features = false }
|
||||
libc = { version = "0.2.14", default-features = false }
|
||||
```
|
||||
|
||||
Note that the default features have been disabled. This is a critical step -
|
||||
|
|
@ -36,8 +36,7 @@ or overriding the default shim for the C `main` function with your own.
|
|||
The function marked `#[start]` is passed the command line parameters
|
||||
in the same format as C:
|
||||
|
||||
```rust
|
||||
# #![feature(libc)]
|
||||
```rust,ignore
|
||||
#![feature(lang_items)]
|
||||
#![feature(start)]
|
||||
#![no_std]
|
||||
|
|
@ -51,15 +50,21 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
|||
0
|
||||
}
|
||||
|
||||
// These functions and traits are used by the compiler, but not
|
||||
// These functions are used by the compiler, but not
|
||||
// for a bare-bones hello world. These are normally
|
||||
// provided by libstd.
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
#[lang = "panic_fmt"] extern fn panic_fmt() -> ! { loop {} }
|
||||
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
|
||||
# #[no_mangle] pub extern fn rust_eh_register_frames () {}
|
||||
# #[no_mangle] pub extern fn rust_eh_unregister_frames () {}
|
||||
# // fn main() {} tricked you, rustdoc!
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
pub extern fn eh_personality() {
|
||||
}
|
||||
|
||||
#[lang = "panic_fmt"]
|
||||
#[no_mangle]
|
||||
pub extern fn rust_begin_panic(_msg: core::fmt::Arguments,
|
||||
_file: &'static str,
|
||||
_line: u32) -> ! {
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
To override the compiler-inserted `main` shim, one has to disable it
|
||||
|
|
@ -67,37 +72,55 @@ with `#![no_main]` and then create the appropriate symbol with the
|
|||
correct ABI and the correct name, which requires overriding the
|
||||
compiler's name mangling too:
|
||||
|
||||
```rust
|
||||
# #![feature(libc)]
|
||||
```rust,ignore
|
||||
#![feature(lang_items)]
|
||||
#![feature(start)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
// Pull in the system libc library for what crt0.o likely requires
|
||||
extern crate libc;
|
||||
|
||||
// Entry point for this program
|
||||
#[no_mangle] // ensure that this symbol is called `main` in the output
|
||||
pub extern fn main(argc: i32, argv: *const *const u8) -> i32 {
|
||||
pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
#[lang = "panic_fmt"] extern fn panic_fmt() -> ! { loop {} }
|
||||
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
|
||||
# #[no_mangle] pub extern fn rust_eh_register_frames () {}
|
||||
# #[no_mangle] pub extern fn rust_eh_unregister_frames () {}
|
||||
# // fn main() {} tricked you, rustdoc!
|
||||
// These functions and traits are used by the compiler, but not
|
||||
// for a bare-bones hello world. These are normally
|
||||
// provided by libstd.
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
pub extern fn eh_personality() {
|
||||
}
|
||||
|
||||
#[lang = "panic_fmt"]
|
||||
#[no_mangle]
|
||||
pub extern fn rust_begin_panic(_msg: core::fmt::Arguments,
|
||||
_file: &'static str,
|
||||
_line: u32) -> ! {
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
The compiler currently makes a few assumptions about symbols which are available
|
||||
in the executable to call. Normally these functions are provided by the standard
|
||||
library, but without it you must define your own.
|
||||
## More about the langauge items
|
||||
|
||||
The compiler currently makes a few assumptions about symbols which are
|
||||
available in the executable to call. Normally these functions are provided by
|
||||
the standard library, but without it you must define your own. These symbols
|
||||
are called "language items", and they each have an internal name, and then a
|
||||
signature that an implementation must conform to.
|
||||
|
||||
The first of these two functions, `eh_personality`, is used by the failure
|
||||
mechanisms of the compiler. This is often mapped to GCC's personality function
|
||||
(see the [libstd implementation][unwind] for more information), but crates
|
||||
which do not trigger a panic can be assured that this function is never
|
||||
called. The second function, `panic_fmt`, is also used by the failure
|
||||
mechanisms of the compiler.
|
||||
|
||||
called. Both the language item and the symbol name are `eh_personality`.
|
||||
|
||||
[unwind]: https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs
|
||||
|
||||
The second function, `panic_fmt`, is also used by the failure mechanisms of the
|
||||
compiler. When a panic happens, this controls the message that's displayed on
|
||||
the screen. While the language item's name is `panic_fmt`, the symbol name is
|
||||
`rust_begin_panic`.
|
||||
|
|
|
|||
|
|
@ -51,12 +51,12 @@ struct Vec<T> {
|
|||
```
|
||||
|
||||
Unlike the previous example it *appears* that everything is exactly as we
|
||||
want. Every generic argument to Vec shows up in the at least one field.
|
||||
want. Every generic argument to Vec shows up in at least one field.
|
||||
Good to go!
|
||||
|
||||
Nope.
|
||||
|
||||
The drop checker will generously determine that Vec<T> does not own any values
|
||||
The drop checker will generously determine that `Vec<T>` does not own any values
|
||||
of type T. This will in turn make it conclude that it doesn't need to worry
|
||||
about Vec dropping any T's in its destructor for determining drop check
|
||||
soundness. This will in turn allow people to create unsoundness using
|
||||
|
|
@ -81,7 +81,7 @@ Raw pointers that own an allocation is such a pervasive pattern that the
|
|||
standard library made a utility for itself called `Unique<T>` which:
|
||||
|
||||
* wraps a `*const T` for variance
|
||||
* includes a `PhantomData<T>`,
|
||||
* includes a `PhantomData<T>`
|
||||
* auto-derives Send/Sync as if T was contained
|
||||
* marks the pointer as NonZero for the null-pointer optimization
|
||||
|
||||
|
|
|
|||
|
|
@ -853,6 +853,20 @@ extern crate std; // equivalent to: extern crate std as std;
|
|||
extern crate std as ruststd; // linking to 'std' under another name
|
||||
```
|
||||
|
||||
When naming Rust crates, hyphens are disallowed. However, Cargo packages may
|
||||
make use of them. In such case, when `Cargo.toml` doesn't specify a crate name,
|
||||
Cargo will transparently replace `-` with `_` (Refer to [RFC 940] for more
|
||||
details).
|
||||
|
||||
Here is an example:
|
||||
|
||||
```{.ignore}
|
||||
// Importing the Cargo package hello-world
|
||||
extern crate hello_world; // hyphen replaced with an underscore
|
||||
```
|
||||
|
||||
[RFC 940]: https://github.com/rust-lang/rfcs/blob/master/text/0940-hyphens-considered-harmful.md
|
||||
|
||||
#### Use declarations
|
||||
|
||||
A _use declaration_ creates one or more local name bindings synonymous with
|
||||
|
|
@ -3744,9 +3758,9 @@ Since `'static` "lives longer" than `'a`, `&'static str` is a subtype of
|
|||
|
||||
## Type coercions
|
||||
|
||||
Coercions are defined in [RFC401]. A coercion is implicit and has no syntax.
|
||||
Coercions are defined in [RFC 401]. A coercion is implicit and has no syntax.
|
||||
|
||||
[RFC401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
|
||||
[RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
|
||||
|
||||
### Coercion sites
|
||||
|
||||
|
|
@ -3886,7 +3900,7 @@ Coercion is allowed between the following types:
|
|||
|
||||
In the future, coerce_inner will be recursively extended to tuples and
|
||||
structs. In addition, coercions from sub-traits to super-traits will be
|
||||
added. See [RFC401] for more details.
|
||||
added. See [RFC 401] for more details.
|
||||
|
||||
# Special traits
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,13 @@ if [ -z $TARG_DIR ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
case "$TARG_DIR" in
|
||||
--print-rustc-release)
|
||||
# not actually copying to TARG_DIR, just print the local rustc version and exit
|
||||
${PREFIX}/bin/rustc${BIN_SUF} --version --verbose | sed -ne 's/^release: //p'
|
||||
;;
|
||||
*)
|
||||
|
||||
cp ${PREFIX}/bin/rustc${BIN_SUF} ${TARG_DIR}/stage0/bin/
|
||||
cp ${PREFIX}/${LIB_DIR}/${RUSTLIBDIR}/${TARG_DIR}/${LIB_DIR}/* ${TARG_DIR}/stage0/${LIB_DIR}/
|
||||
cp ${PREFIX}/${LIB_DIR}/${LIB_PREFIX}extra*${LIB_SUF} ${TARG_DIR}/stage0/${LIB_DIR}/
|
||||
|
|
@ -66,3 +73,5 @@ cp ${PREFIX}/${LIB_DIR}/${LIB_PREFIX}term*${LIB_SUF} ${TARG_DIR}/stage0/${LIB_DI
|
|||
|
||||
# do not fail if one of the above fails, as all we need is a working rustc!
|
||||
exit 0
|
||||
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -520,7 +520,7 @@ impl<T> Vec<T> {
|
|||
#[inline]
|
||||
#[stable(feature = "vec_as_slice", since = "1.7.0")]
|
||||
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
&mut self[..]
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the length of a vector.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use ptr;
|
|||
/// An implementation of SipHash 1-3.
|
||||
///
|
||||
/// See: https://131002.net/siphash/
|
||||
#[unstable(feature = "sip_hash_13", issue = "29754")]
|
||||
#[unstable(feature = "sip_hash_13", issue = "34767")]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct SipHasher13 {
|
||||
hasher: Hasher<Sip13Rounds>,
|
||||
|
|
@ -27,7 +27,7 @@ pub struct SipHasher13 {
|
|||
/// An implementation of SipHash 2-4.
|
||||
///
|
||||
/// See: https://131002.net/siphash/
|
||||
#[unstable(feature = "sip_hash_13", issue = "29754")]
|
||||
#[unstable(feature = "sip_hash_13", issue = "34767")]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct SipHasher24 {
|
||||
hasher: Hasher<Sip24Rounds>,
|
||||
|
|
@ -154,14 +154,14 @@ impl SipHasher {
|
|||
impl SipHasher13 {
|
||||
/// Creates a new `SipHasher13` with the two initial keys set to 0.
|
||||
#[inline]
|
||||
#[unstable(feature = "sip_hash_13", issue = "29754")]
|
||||
#[unstable(feature = "sip_hash_13", issue = "34767")]
|
||||
pub fn new() -> SipHasher13 {
|
||||
SipHasher13::new_with_keys(0, 0)
|
||||
}
|
||||
|
||||
/// Creates a `SipHasher13` that is keyed off the provided keys.
|
||||
#[inline]
|
||||
#[unstable(feature = "sip_hash_13", issue = "29754")]
|
||||
#[unstable(feature = "sip_hash_13", issue = "34767")]
|
||||
pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 {
|
||||
SipHasher13 {
|
||||
hasher: Hasher::new_with_keys(key0, key1)
|
||||
|
|
@ -172,14 +172,14 @@ impl SipHasher13 {
|
|||
impl SipHasher24 {
|
||||
/// Creates a new `SipHasher24` with the two initial keys set to 0.
|
||||
#[inline]
|
||||
#[unstable(feature = "sip_hash_13", issue = "29754")]
|
||||
#[unstable(feature = "sip_hash_13", issue = "34767")]
|
||||
pub fn new() -> SipHasher24 {
|
||||
SipHasher24::new_with_keys(0, 0)
|
||||
}
|
||||
|
||||
/// Creates a `SipHasher24` that is keyed off the provided keys.
|
||||
#[inline]
|
||||
#[unstable(feature = "sip_hash_13", issue = "29754")]
|
||||
#[unstable(feature = "sip_hash_13", issue = "34767")]
|
||||
pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher24 {
|
||||
SipHasher24 {
|
||||
hasher: Hasher::new_with_keys(key0, key1)
|
||||
|
|
@ -232,7 +232,7 @@ impl super::Hasher for SipHasher {
|
|||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "sip_hash_13", issue = "29754")]
|
||||
#[unstable(feature = "sip_hash_13", issue = "34767")]
|
||||
impl super::Hasher for SipHasher13 {
|
||||
#[inline]
|
||||
fn write(&mut self, msg: &[u8]) {
|
||||
|
|
@ -245,7 +245,7 @@ impl super::Hasher for SipHasher13 {
|
|||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "sip_hash_13", issue = "29754")]
|
||||
#[unstable(feature = "sip_hash_13", issue = "34767")]
|
||||
impl super::Hasher for SipHasher24 {
|
||||
#[inline]
|
||||
fn write(&mut self, msg: &[u8]) {
|
||||
|
|
|
|||
|
|
@ -491,8 +491,6 @@ impl<'a, I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for &'a mut I {
|
|||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait ExactSizeIterator: Iterator {
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
/// Returns the exact number of times the iterator will iterate.
|
||||
///
|
||||
/// This method has a default implementation, so you usually should not
|
||||
|
|
@ -516,6 +514,8 @@ pub trait ExactSizeIterator: Iterator {
|
|||
///
|
||||
/// assert_eq!(5, five.len());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn len(&self) -> usize {
|
||||
let (lower, upper) = self.size_hint();
|
||||
// Note: This assertion is overly defensive, but it checks the invariant
|
||||
|
|
@ -525,6 +525,32 @@ pub trait ExactSizeIterator: Iterator {
|
|||
assert_eq!(upper, Some(lower));
|
||||
lower
|
||||
}
|
||||
|
||||
/// Returns whether the iterator is empty.
|
||||
///
|
||||
/// This method has a default implementation using `self.len()`, so you
|
||||
/// don't need to implement it yourself.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(exact_size_is_empty)]
|
||||
///
|
||||
/// let mut one_element = 0..1;
|
||||
/// assert!(!one_element.is_empty());
|
||||
///
|
||||
/// assert_eq!(one_element.next(), Some(0));
|
||||
/// assert!(one_element.is_empty());
|
||||
///
|
||||
/// assert_eq!(one_element.next(), None);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "exact_size_is_empty", issue = "0")]
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
//!
|
||||
//! # How to use the core library
|
||||
//!
|
||||
//! Please note that all of these details are currently not considered stable.
|
||||
//!
|
||||
// FIXME: Fill me in with more detail when the interface settles
|
||||
//! This library is built on the assumption of a few existing symbols:
|
||||
//!
|
||||
|
|
@ -34,11 +36,12 @@
|
|||
//! These functions are often provided by the system libc, but can also be
|
||||
//! provided by the [rlibc crate](https://crates.io/crates/rlibc).
|
||||
//!
|
||||
//! * `rust_begin_unwind` - This function takes three arguments, a
|
||||
//! `fmt::Arguments`, a `&str`, and a `u32`. These three arguments dictate
|
||||
//! the panic message, the file at which panic was invoked, and the line.
|
||||
//! It is up to consumers of this core library to define this panic
|
||||
//! function; it is only required to never return.
|
||||
//! * `rust_begin_panic` - This function takes three arguments, a
|
||||
//! `fmt::Arguments`, a `&'static str`, and a `u32`. These three arguments
|
||||
//! dictate the panic message, the file at which panic was invoked, and the
|
||||
//! line. It is up to consumers of this core library to define this panic
|
||||
//! function; it is only required to never return. This requires a `lang`
|
||||
//! attribute named `panic_fmt`.
|
||||
|
||||
// Since libcore defines many fundamental lang items, all tests live in a
|
||||
// separate crate, libcoretest, to avoid bizarre issues.
|
||||
|
|
|
|||
|
|
@ -2318,7 +2318,7 @@ impl usize {
|
|||
/// let num = 12.4_f32;
|
||||
/// let inf = f32::INFINITY;
|
||||
/// let zero = 0f32;
|
||||
/// let sub: f32 = 0.000000000000000000000000000000000000011754942;
|
||||
/// let sub: f32 = 1.1754942e-38;
|
||||
/// let nan = f32::NAN;
|
||||
///
|
||||
/// assert_eq!(num.classify(), FpCategory::Normal);
|
||||
|
|
|
|||
|
|
@ -571,12 +571,21 @@ macro_rules! fnptr_impls_safety_abi {
|
|||
}
|
||||
|
||||
macro_rules! fnptr_impls_args {
|
||||
($($Arg: ident),*) => {
|
||||
($($Arg: ident),+) => {
|
||||
fnptr_impls_safety_abi! { extern "Rust" fn($($Arg),*) -> Ret, $($Arg),* }
|
||||
fnptr_impls_safety_abi! { extern "C" fn($($Arg),*) -> Ret, $($Arg),* }
|
||||
fnptr_impls_safety_abi! { extern "C" fn($($Arg),* , ...) -> Ret, $($Arg),* }
|
||||
fnptr_impls_safety_abi! { unsafe extern "Rust" fn($($Arg),*) -> Ret, $($Arg),* }
|
||||
fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),*) -> Ret, $($Arg),* }
|
||||
}
|
||||
fnptr_impls_safety_abi! { unsafe extern "C" fn($($Arg),* , ...) -> Ret, $($Arg),* }
|
||||
};
|
||||
() => {
|
||||
// No variadic functions with 0 parameters
|
||||
fnptr_impls_safety_abi! { extern "Rust" fn() -> Ret, }
|
||||
fnptr_impls_safety_abi! { extern "C" fn() -> Ret, }
|
||||
fnptr_impls_safety_abi! { unsafe extern "Rust" fn() -> Ret, }
|
||||
fnptr_impls_safety_abi! { unsafe extern "C" fn() -> Ret, }
|
||||
};
|
||||
}
|
||||
|
||||
fnptr_impls_args! { }
|
||||
|
|
|
|||
|
|
@ -171,3 +171,17 @@ fn test_unsized_unique() {
|
|||
let zs: &mut [i32] = &mut [1, 2, 3];
|
||||
assert!(ys == zs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variadic_fnptr() {
|
||||
use core::hash::{Hash, SipHasher};
|
||||
extern "C" {
|
||||
fn printf(_: *const u8, ...);
|
||||
}
|
||||
let p: unsafe extern "C" fn(*const u8, ...) = printf;
|
||||
let q = p.clone();
|
||||
assert_eq!(p, q);
|
||||
assert!(!(p < q));
|
||||
let mut s = SipHasher::new();
|
||||
assert_eq!(p.hash(&mut s), q.hash(&mut s));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ use syntax::ast;
|
|||
use syntax::parse::token;
|
||||
use syntax::ptr::P;
|
||||
use syntax_pos::{self, Pos, Span};
|
||||
use errors::{DiagnosticBuilder, check_old_skool};
|
||||
use errors::{DiagnosticBuilder, check_old_school};
|
||||
|
||||
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn note_and_explain_region(self,
|
||||
|
|
@ -485,7 +485,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||
"{}",
|
||||
trace.origin);
|
||||
|
||||
if !is_simple_error || check_old_skool() {
|
||||
if !is_simple_error || check_old_school() {
|
||||
err.note_expected_found(&"type", &expected, &found);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -739,8 +739,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
|||
"for every macro invocation, print its name and arguments"),
|
||||
enable_nonzeroing_move_hints: bool = (false, parse_bool,
|
||||
"force nonzeroing move optimization on"),
|
||||
keep_mtwt_tables: bool = (false, parse_bool,
|
||||
"don't clear the resolution tables after analysis"),
|
||||
keep_hygiene_data: bool = (false, parse_bool,
|
||||
"don't clear the hygiene data after analysis"),
|
||||
keep_ast: bool = (false, parse_bool,
|
||||
"keep the AST after lowering it to HIR"),
|
||||
show_span: Option<String> = (None, parse_opt_string,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ use mir::transform as mir_pass;
|
|||
|
||||
use syntax::ast::{NodeId, Name};
|
||||
use errors::{self, DiagnosticBuilder};
|
||||
use errors::emitter::{Emitter, BasicEmitter, EmitterWriter};
|
||||
use errors::emitter::{Emitter, EmitterWriter};
|
||||
use errors::snippet::FormatMode;
|
||||
use syntax::json::JsonEmitter;
|
||||
use syntax::feature_gate;
|
||||
use syntax::parse;
|
||||
|
|
@ -439,7 +440,7 @@ pub fn build_session_with_codemap(sopts: config::Options,
|
|||
config::ErrorOutputType::HumanReadable(color_config) => {
|
||||
Box::new(EmitterWriter::stderr(color_config,
|
||||
Some(registry),
|
||||
codemap.clone(),
|
||||
Some(codemap.clone()),
|
||||
errors::snippet::FormatMode::EnvironmentSelected))
|
||||
}
|
||||
config::ErrorOutputType::Json => {
|
||||
|
|
@ -575,24 +576,32 @@ unsafe fn configure_llvm(sess: &Session) {
|
|||
}
|
||||
|
||||
pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
|
||||
let mut emitter: Box<Emitter> = match output {
|
||||
let emitter: Box<Emitter> = match output {
|
||||
config::ErrorOutputType::HumanReadable(color_config) => {
|
||||
Box::new(BasicEmitter::stderr(color_config))
|
||||
Box::new(EmitterWriter::stderr(color_config,
|
||||
None,
|
||||
None,
|
||||
FormatMode::EnvironmentSelected))
|
||||
}
|
||||
config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
|
||||
};
|
||||
emitter.emit(&MultiSpan::new(), msg, None, errors::Level::Fatal);
|
||||
let handler = errors::Handler::with_emitter(true, false, emitter);
|
||||
handler.emit(&MultiSpan::new(), msg, errors::Level::Fatal);
|
||||
panic!(errors::FatalError);
|
||||
}
|
||||
|
||||
pub fn early_warn(output: config::ErrorOutputType, msg: &str) {
|
||||
let mut emitter: Box<Emitter> = match output {
|
||||
let emitter: Box<Emitter> = match output {
|
||||
config::ErrorOutputType::HumanReadable(color_config) => {
|
||||
Box::new(BasicEmitter::stderr(color_config))
|
||||
Box::new(EmitterWriter::stderr(color_config,
|
||||
None,
|
||||
None,
|
||||
FormatMode::EnvironmentSelected))
|
||||
}
|
||||
config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
|
||||
};
|
||||
emitter.emit(&MultiSpan::new(), msg, None, errors::Level::Warning);
|
||||
let handler = errors::Handler::with_emitter(true, false, emitter);
|
||||
handler.emit(&MultiSpan::new(), msg, errors::Level::Warning);
|
||||
}
|
||||
|
||||
// Err(0) means compilation was stopped, but no errors were found.
|
||||
|
|
|
|||
|
|
@ -236,8 +236,8 @@ pub fn compile_input(sess: &Session,
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn keep_mtwt_tables(sess: &Session) -> bool {
|
||||
sess.opts.debugging_opts.keep_mtwt_tables
|
||||
fn keep_hygiene_data(sess: &Session) -> bool {
|
||||
sess.opts.debugging_opts.keep_hygiene_data
|
||||
}
|
||||
|
||||
fn keep_ast(sess: &Session) -> bool {
|
||||
|
|
@ -479,9 +479,8 @@ pub fn phase_1_parse_input<'a>(sess: &'a Session,
|
|||
input: &Input)
|
||||
-> PResult<'a, ast::Crate> {
|
||||
// These may be left in an incoherent state after a previous compile.
|
||||
// `clear_tables` and `clear_ident_interner` can be used to free
|
||||
// memory, but they do not restore the initial state.
|
||||
syntax::ext::mtwt::reset_tables();
|
||||
syntax::ext::hygiene::reset_hygiene_data();
|
||||
// `clear_ident_interner` can be used to free memory, but it does not restore the initial state.
|
||||
token::reset_ident_interner();
|
||||
let continue_after_error = sess.opts.continue_parse_after_error;
|
||||
sess.diagnostic().set_continue_after_error(continue_after_error);
|
||||
|
|
@ -761,9 +760,9 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
|
|||
hir_map::Forest::new(lower_crate(sess, &krate, &mut resolver), &sess.dep_graph)
|
||||
});
|
||||
|
||||
// Discard MTWT tables that aren't required past lowering to HIR.
|
||||
if !keep_mtwt_tables(sess) {
|
||||
syntax::ext::mtwt::clear_tables();
|
||||
// Discard hygiene data, which isn't required past lowering to HIR.
|
||||
if !keep_hygiene_data(sess) {
|
||||
syntax::ext::hygiene::reset_hygiene_data();
|
||||
}
|
||||
|
||||
Ok(ExpansionResult {
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ use syntax::feature_gate::{GatedCfg, UnstableFeatures};
|
|||
use syntax::parse::{self, PResult};
|
||||
use syntax_pos::MultiSpan;
|
||||
use errors::emitter::Emitter;
|
||||
use errors::snippet::FormatMode;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test;
|
||||
|
|
@ -139,10 +140,15 @@ pub fn run(args: Vec<String>) -> isize {
|
|||
match session {
|
||||
Some(sess) => sess.fatal(&abort_msg(err_count)),
|
||||
None => {
|
||||
let mut emitter =
|
||||
errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto);
|
||||
emitter.emit(&MultiSpan::new(), &abort_msg(err_count), None,
|
||||
errors::Level::Fatal);
|
||||
let emitter =
|
||||
errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto,
|
||||
None,
|
||||
None,
|
||||
FormatMode::EnvironmentSelected);
|
||||
let handler = errors::Handler::with_emitter(true, false, Box::new(emitter));
|
||||
handler.emit(&MultiSpan::new(),
|
||||
&abort_msg(err_count),
|
||||
errors::Level::Fatal);
|
||||
exit_on_err();
|
||||
}
|
||||
}
|
||||
|
|
@ -374,23 +380,26 @@ fn handle_explain(code: &str,
|
|||
|
||||
fn check_cfg(sopts: &config::Options,
|
||||
output: ErrorOutputType) {
|
||||
let mut emitter: Box<Emitter> = match output {
|
||||
let emitter: Box<Emitter> = match output {
|
||||
config::ErrorOutputType::HumanReadable(color_config) => {
|
||||
Box::new(errors::emitter::BasicEmitter::stderr(color_config))
|
||||
Box::new(errors::emitter::EmitterWriter::stderr(color_config,
|
||||
None,
|
||||
None,
|
||||
FormatMode::EnvironmentSelected))
|
||||
}
|
||||
config::ErrorOutputType::Json => Box::new(json::JsonEmitter::basic()),
|
||||
};
|
||||
let handler = errors::Handler::with_emitter(true, false, emitter);
|
||||
|
||||
let mut saw_invalid_predicate = false;
|
||||
for item in sopts.cfg.iter() {
|
||||
match item.node {
|
||||
ast::MetaItemKind::List(ref pred, _) => {
|
||||
saw_invalid_predicate = true;
|
||||
emitter.emit(&MultiSpan::new(),
|
||||
handler.emit(&MultiSpan::new(),
|
||||
&format!("invalid predicate in --cfg command line argument: `{}`",
|
||||
pred),
|
||||
None,
|
||||
errors::Level::Fatal);
|
||||
errors::Level::Fatal);
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
|
|
@ -1070,26 +1079,34 @@ pub fn monitor<F: FnOnce() + Send + 'static>(f: F) {
|
|||
if let Err(value) = thread.unwrap().join() {
|
||||
// Thread panicked without emitting a fatal diagnostic
|
||||
if !value.is::<errors::FatalError>() {
|
||||
let mut emitter = errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto);
|
||||
let emitter =
|
||||
Box::new(errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto,
|
||||
None,
|
||||
None,
|
||||
FormatMode::EnvironmentSelected));
|
||||
let handler = errors::Handler::with_emitter(true, false, emitter);
|
||||
|
||||
// a .span_bug or .bug call has already printed what
|
||||
// it wants to print.
|
||||
if !value.is::<errors::ExplicitBug>() {
|
||||
emitter.emit(&MultiSpan::new(), "unexpected panic", None, errors::Level::Bug);
|
||||
handler.emit(&MultiSpan::new(),
|
||||
"unexpected panic",
|
||||
errors::Level::Bug);
|
||||
}
|
||||
|
||||
let xs = ["the compiler unexpectedly panicked. this is a bug.".to_string(),
|
||||
format!("we would appreciate a bug report: {}", BUG_REPORT_URL)];
|
||||
for note in &xs {
|
||||
emitter.emit(&MultiSpan::new(), ¬e[..], None, errors::Level::Note)
|
||||
handler.emit(&MultiSpan::new(),
|
||||
¬e[..],
|
||||
errors::Level::Note);
|
||||
}
|
||||
if match env::var_os("RUST_BACKTRACE") {
|
||||
Some(val) => &val != "0",
|
||||
None => false,
|
||||
} {
|
||||
emitter.emit(&MultiSpan::new(),
|
||||
handler.emit(&MultiSpan::new(),
|
||||
"run with `RUST_BACKTRACE=1` for a backtrace",
|
||||
None,
|
||||
errors::Level::Note);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -456,7 +456,7 @@ impl<'ast> pprust::PpAnn for HygieneAnnotation<'ast> {
|
|||
pp::space(&mut s.s)?;
|
||||
// FIXME #16420: this doesn't display the connections
|
||||
// between syntax contexts
|
||||
s.synth_comment(format!("{}#{}", nm, ctxt.0))
|
||||
s.synth_comment(format!("{}{:?}", nm, ctxt))
|
||||
}
|
||||
pprust::NodeName(&ast::Name(nm)) => {
|
||||
pp::space(&mut s.s)?;
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ use syntax::ast;
|
|||
use syntax::abi::Abi;
|
||||
use syntax::codemap::CodeMap;
|
||||
use errors;
|
||||
use errors::emitter::{CoreEmitter, Emitter};
|
||||
use errors::{Level, RenderSpan};
|
||||
use errors::emitter::Emitter;
|
||||
use errors::{Level, DiagnosticBuilder};
|
||||
use syntax::parse::token;
|
||||
use syntax::feature_gate::UnstableFeatures;
|
||||
use syntax_pos::DUMMY_SP;
|
||||
|
|
@ -76,15 +76,12 @@ fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
|
|||
}
|
||||
}
|
||||
|
||||
impl CoreEmitter for ExpectErrorEmitter {
|
||||
fn emit_message(&mut self,
|
||||
_sp: &RenderSpan,
|
||||
msg: &str,
|
||||
_: Option<&str>,
|
||||
lvl: Level,
|
||||
_is_header: bool,
|
||||
_show_snippet: bool) {
|
||||
remove_message(self, msg, lvl);
|
||||
impl Emitter for ExpectErrorEmitter {
|
||||
fn emit(&mut self, db: &DiagnosticBuilder) {
|
||||
remove_message(self, &db.message, db.level);
|
||||
for child in &db.children {
|
||||
remove_message(self, &child.message, child.level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -49,6 +49,7 @@ use std::thread::panicking;
|
|||
pub mod emitter;
|
||||
pub mod snippet;
|
||||
pub mod registry;
|
||||
pub mod styled_buffer;
|
||||
|
||||
use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION };
|
||||
use syntax_pos::{MacroBacktrace};
|
||||
|
|
@ -81,16 +82,6 @@ pub trait CodeMapper {
|
|||
fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace>;
|
||||
}
|
||||
|
||||
impl RenderSpan {
|
||||
fn span(&self) -> &MultiSpan {
|
||||
match *self {
|
||||
FullSpan(ref msp) |
|
||||
Suggestion(CodeSuggestion { ref msp, .. }) =>
|
||||
msp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeSuggestion {
|
||||
/// Returns the assembled code suggestion.
|
||||
pub fn splice_lines(&self, cm: &CodeMapper) -> String {
|
||||
|
|
@ -238,7 +229,7 @@ impl<'a> DiagnosticBuilder<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
self.handler.emit.borrow_mut().emit_struct(&self);
|
||||
self.handler.emitter.borrow_mut().emit(&self);
|
||||
self.cancel();
|
||||
self.handler.panic_if_treat_err_as_bug();
|
||||
|
||||
|
|
@ -359,11 +350,20 @@ impl<'a> DiagnosticBuilder<'a> {
|
|||
fn new(handler: &'a Handler,
|
||||
level: Level,
|
||||
message: &str) -> DiagnosticBuilder<'a> {
|
||||
DiagnosticBuilder::new_with_code(handler, level, None, message)
|
||||
}
|
||||
|
||||
/// Convenience function for internal use, clients should use one of the
|
||||
/// struct_* methods on Handler.
|
||||
fn new_with_code(handler: &'a Handler,
|
||||
level: Level,
|
||||
code: Option<String>,
|
||||
message: &str) -> DiagnosticBuilder<'a> {
|
||||
DiagnosticBuilder {
|
||||
handler: handler,
|
||||
level: level,
|
||||
message: message.to_owned(),
|
||||
code: None,
|
||||
code: code,
|
||||
span: MultiSpan::new(),
|
||||
children: vec![],
|
||||
}
|
||||
|
|
@ -397,10 +397,10 @@ impl<'a> fmt::Debug for DiagnosticBuilder<'a> {
|
|||
impl<'a> Drop for DiagnosticBuilder<'a> {
|
||||
fn drop(&mut self) {
|
||||
if !panicking() && !self.cancelled() {
|
||||
self.handler.emit.borrow_mut().emit(&MultiSpan::new(),
|
||||
"Error constructed but not emitted",
|
||||
None,
|
||||
Bug);
|
||||
let mut db = DiagnosticBuilder::new(self.handler,
|
||||
Bug,
|
||||
"Error constructed but not emitted");
|
||||
db.emit();
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
|
@ -411,7 +411,7 @@ impl<'a> Drop for DiagnosticBuilder<'a> {
|
|||
/// others log errors for later reporting.
|
||||
pub struct Handler {
|
||||
err_count: Cell<usize>,
|
||||
emit: RefCell<Box<Emitter>>,
|
||||
emitter: RefCell<Box<Emitter>>,
|
||||
pub can_emit_warnings: bool,
|
||||
treat_err_as_bug: bool,
|
||||
continue_after_error: Cell<bool>,
|
||||
|
|
@ -423,7 +423,7 @@ impl Handler {
|
|||
registry: Option<registry::Registry>,
|
||||
can_emit_warnings: bool,
|
||||
treat_err_as_bug: bool,
|
||||
cm: Rc<CodeMapper>)
|
||||
cm: Option<Rc<CodeMapper>>)
|
||||
-> Handler {
|
||||
let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm,
|
||||
snippet::FormatMode::EnvironmentSelected));
|
||||
|
|
@ -435,7 +435,7 @@ impl Handler {
|
|||
e: Box<Emitter>) -> Handler {
|
||||
Handler {
|
||||
err_count: Cell::new(0),
|
||||
emit: RefCell::new(e),
|
||||
emitter: RefCell::new(e),
|
||||
can_emit_warnings: can_emit_warnings,
|
||||
treat_err_as_bug: treat_err_as_bug,
|
||||
continue_after_error: Cell::new(true),
|
||||
|
|
@ -588,7 +588,7 @@ impl Handler {
|
|||
self.bump_err_count();
|
||||
}
|
||||
pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
|
||||
self.emit.borrow_mut().emit(&sp.into(), msg, None, Note);
|
||||
self.emit(&sp.into(), msg, Note);
|
||||
}
|
||||
pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
|
||||
self.span_bug(sp, &format!("unimplemented {}", msg));
|
||||
|
|
@ -597,7 +597,10 @@ impl Handler {
|
|||
if self.treat_err_as_bug {
|
||||
self.bug(msg);
|
||||
}
|
||||
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Fatal);
|
||||
let mut db = DiagnosticBuilder::new(self,
|
||||
Fatal,
|
||||
msg);
|
||||
db.emit();
|
||||
self.bump_err_count();
|
||||
FatalError
|
||||
}
|
||||
|
|
@ -605,17 +608,29 @@ impl Handler {
|
|||
if self.treat_err_as_bug {
|
||||
self.bug(msg);
|
||||
}
|
||||
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Error);
|
||||
let mut db = DiagnosticBuilder::new(self,
|
||||
Error,
|
||||
msg);
|
||||
db.emit();
|
||||
self.bump_err_count();
|
||||
}
|
||||
pub fn warn(&self, msg: &str) {
|
||||
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Warning);
|
||||
let mut db = DiagnosticBuilder::new(self,
|
||||
Warning,
|
||||
msg);
|
||||
db.emit();
|
||||
}
|
||||
pub fn note_without_error(&self, msg: &str) {
|
||||
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Note);
|
||||
let mut db = DiagnosticBuilder::new(self,
|
||||
Note,
|
||||
msg);
|
||||
db.emit();
|
||||
}
|
||||
pub fn bug(&self, msg: &str) -> ! {
|
||||
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Bug);
|
||||
let mut db = DiagnosticBuilder::new(self,
|
||||
Bug,
|
||||
msg);
|
||||
db.emit();
|
||||
panic!(ExplicitBug);
|
||||
}
|
||||
pub fn unimpl(&self, msg: &str) -> ! {
|
||||
|
|
@ -661,7 +676,9 @@ impl Handler {
|
|||
msg: &str,
|
||||
lvl: Level) {
|
||||
if lvl == Warning && !self.can_emit_warnings { return }
|
||||
self.emit.borrow_mut().emit(&msp, msg, None, lvl);
|
||||
let mut db = DiagnosticBuilder::new(self, lvl, msg);
|
||||
db.set_span(msp.clone());
|
||||
db.emit();
|
||||
if !self.continue_after_error.get() { self.abort_if_errors(); }
|
||||
}
|
||||
pub fn emit_with_code(&self,
|
||||
|
|
@ -670,7 +687,12 @@ impl Handler {
|
|||
code: &str,
|
||||
lvl: Level) {
|
||||
if lvl == Warning && !self.can_emit_warnings { return }
|
||||
self.emit.borrow_mut().emit(&msp, msg, Some(code), lvl);
|
||||
let mut db = DiagnosticBuilder::new_with_code(self,
|
||||
lvl,
|
||||
Some(code.to_owned()),
|
||||
msg);
|
||||
db.set_span(msp.clone());
|
||||
db.emit();
|
||||
if !self.continue_after_error.get() { self.abort_if_errors(); }
|
||||
}
|
||||
}
|
||||
|
|
@ -734,13 +756,13 @@ pub fn expect<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T where
|
|||
///
|
||||
/// FIXME(#33240)
|
||||
#[cfg(not(test))]
|
||||
pub fn check_old_skool() -> bool {
|
||||
pub fn check_old_school() -> bool {
|
||||
use std::env;
|
||||
env::var("RUST_NEW_ERROR_FORMAT").is_err()
|
||||
}
|
||||
|
||||
/// For unit tests, use the new format.
|
||||
#[cfg(test)]
|
||||
pub fn check_old_skool() -> bool {
|
||||
pub fn check_old_school() -> bool {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,10 @@
|
|||
|
||||
// Code for annotating snippets.
|
||||
|
||||
use syntax_pos::{Span, FileMap, CharPos, LineInfo};
|
||||
use check_old_skool;
|
||||
use syntax_pos::{Span, FileMap};
|
||||
use CodeMapper;
|
||||
use std::cmp;
|
||||
use std::rc::Rc;
|
||||
use std::mem;
|
||||
use {Level};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum FormatMode {
|
||||
|
|
@ -49,37 +47,31 @@ pub struct FileInfo {
|
|||
format_mode: FormatMode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Line {
|
||||
line_index: usize,
|
||||
annotations: Vec<Annotation>,
|
||||
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub struct Line {
|
||||
pub line_index: usize,
|
||||
pub annotations: Vec<Annotation>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
|
||||
struct Annotation {
|
||||
pub struct Annotation {
|
||||
/// Start column, 0-based indexing -- counting *characters*, not
|
||||
/// utf-8 bytes. Note that it is important that this field goes
|
||||
/// first, so that when we sort, we sort orderings by start
|
||||
/// column.
|
||||
start_col: usize,
|
||||
pub start_col: usize,
|
||||
|
||||
/// End column within the line (exclusive)
|
||||
end_col: usize,
|
||||
pub end_col: usize,
|
||||
|
||||
/// Is this annotation derived from primary span
|
||||
is_primary: bool,
|
||||
pub is_primary: bool,
|
||||
|
||||
/// Is this a large span minimized down to a smaller span
|
||||
is_minimized: bool,
|
||||
pub is_minimized: bool,
|
||||
|
||||
/// Optional label to display adjacent to the annotation.
|
||||
label: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RenderedLine {
|
||||
pub text: Vec<StyledString>,
|
||||
pub kind: RenderedLineKind,
|
||||
pub label: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -88,14 +80,9 @@ pub struct StyledString {
|
|||
pub style: Style,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StyledBuffer {
|
||||
text: Vec<Vec<char>>,
|
||||
styles: Vec<Vec<Style>>
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Style {
|
||||
HeaderMsg,
|
||||
FileNameStyle,
|
||||
LineAndColumn,
|
||||
LineNumber,
|
||||
|
|
@ -104,813 +91,9 @@ pub enum Style {
|
|||
UnderlineSecondary,
|
||||
LabelPrimary,
|
||||
LabelSecondary,
|
||||
OldSkoolNoteText,
|
||||
OldSkoolNote,
|
||||
OldSchoolNoteText,
|
||||
OldSchoolNote,
|
||||
NoStyle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RenderedLineKind {
|
||||
PrimaryFileName,
|
||||
OtherFileName,
|
||||
SourceText {
|
||||
file: Rc<FileMap>,
|
||||
line_index: usize,
|
||||
},
|
||||
Annotations,
|
||||
Elision,
|
||||
}
|
||||
|
||||
impl SnippetData {
|
||||
pub fn new(codemap: Rc<CodeMapper>,
|
||||
primary_span: Option<Span>,
|
||||
format_mode: FormatMode) // (*)
|
||||
-> Self {
|
||||
// (*) The primary span indicates the file that must appear
|
||||
// first, and which will have a line number etc in its
|
||||
// name. Outside of tests, this is always `Some`, but for many
|
||||
// tests it's not relevant to test this portion of the logic,
|
||||
// and it's tedious to pick a primary span (read: tedious to
|
||||
// port older tests that predate the existence of a primary
|
||||
// span).
|
||||
|
||||
debug!("SnippetData::new(primary_span={:?})", primary_span);
|
||||
|
||||
let mut data = SnippetData {
|
||||
codemap: codemap.clone(),
|
||||
files: vec![],
|
||||
format_mode: format_mode.clone()
|
||||
};
|
||||
if let Some(primary_span) = primary_span {
|
||||
let lo = codemap.lookup_char_pos(primary_span.lo);
|
||||
data.files.push(
|
||||
FileInfo {
|
||||
file: lo.file,
|
||||
primary_span: Some(primary_span),
|
||||
lines: vec![],
|
||||
format_mode: format_mode.clone(),
|
||||
});
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
pub fn push(&mut self, span: Span, is_primary: bool, label: Option<String>) {
|
||||
debug!("SnippetData::push(span={:?}, is_primary={}, label={:?})",
|
||||
span, is_primary, label);
|
||||
|
||||
let file_lines = match self.codemap.span_to_lines(span) {
|
||||
Ok(file_lines) => file_lines,
|
||||
Err(_) => {
|
||||
// ignore unprintable spans completely.
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.file(&file_lines.file)
|
||||
.push_lines(&file_lines.lines, is_primary, label);
|
||||
}
|
||||
|
||||
fn file(&mut self, file_map: &Rc<FileMap>) -> &mut FileInfo {
|
||||
let index = self.files.iter().position(|f| f.file.name == file_map.name);
|
||||
if let Some(index) = index {
|
||||
return &mut self.files[index];
|
||||
}
|
||||
|
||||
self.files.push(
|
||||
FileInfo {
|
||||
file: file_map.clone(),
|
||||
lines: vec![],
|
||||
primary_span: None,
|
||||
format_mode: self.format_mode.clone()
|
||||
});
|
||||
self.files.last_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn render_lines(&self) -> Vec<RenderedLine> {
|
||||
debug!("SnippetData::render_lines()");
|
||||
|
||||
let mut rendered_lines: Vec<_> =
|
||||
self.files.iter()
|
||||
.flat_map(|f| f.render_file_lines(&self.codemap))
|
||||
.collect();
|
||||
prepend_prefixes(&mut rendered_lines, &self.format_mode);
|
||||
trim_lines(&mut rendered_lines);
|
||||
rendered_lines
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StringSource {
|
||||
fn make_string(self) -> String;
|
||||
}
|
||||
|
||||
impl StringSource for String {
|
||||
fn make_string(self) -> String {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl StringSource for Vec<char> {
|
||||
fn make_string(self) -> String {
|
||||
self.into_iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<(S, Style, RenderedLineKind)> for RenderedLine
|
||||
where S: StringSource
|
||||
{
|
||||
fn from((text, style, kind): (S, Style, RenderedLineKind)) -> Self {
|
||||
RenderedLine {
|
||||
text: vec![StyledString {
|
||||
text: text.make_string(),
|
||||
style: style,
|
||||
}],
|
||||
kind: kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S1,S2> From<(S1, Style, S2, Style, RenderedLineKind)> for RenderedLine
|
||||
where S1: StringSource, S2: StringSource
|
||||
{
|
||||
fn from(tuple: (S1, Style, S2, Style, RenderedLineKind)) -> Self {
|
||||
let (text1, style1, text2, style2, kind) = tuple;
|
||||
RenderedLine {
|
||||
text: vec![
|
||||
StyledString {
|
||||
text: text1.make_string(),
|
||||
style: style1,
|
||||
},
|
||||
StyledString {
|
||||
text: text2.make_string(),
|
||||
style: style2,
|
||||
}
|
||||
],
|
||||
kind: kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderedLine {
|
||||
fn trim_last(&mut self) {
|
||||
if let Some(last_text) = self.text.last_mut() {
|
||||
let len = last_text.text.trim_right().len();
|
||||
last_text.text.truncate(len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderedLineKind {
|
||||
fn prefix(&self) -> StyledString {
|
||||
match *self {
|
||||
RenderedLineKind::SourceText { file: _, line_index } =>
|
||||
StyledString {
|
||||
text: format!("{}", line_index + 1),
|
||||
style: Style::LineNumber,
|
||||
},
|
||||
RenderedLineKind::Elision =>
|
||||
StyledString {
|
||||
text: String::from("..."),
|
||||
style: Style::LineNumber,
|
||||
},
|
||||
RenderedLineKind::PrimaryFileName |
|
||||
RenderedLineKind::OtherFileName |
|
||||
RenderedLineKind::Annotations =>
|
||||
StyledString {
|
||||
text: String::from(""),
|
||||
style: Style::LineNumber,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StyledBuffer {
|
||||
fn new() -> StyledBuffer {
|
||||
StyledBuffer { text: vec![], styles: vec![] }
|
||||
}
|
||||
|
||||
fn render(&self, source_kind: RenderedLineKind) -> Vec<RenderedLine> {
|
||||
let mut output: Vec<RenderedLine> = vec![];
|
||||
let mut styled_vec: Vec<StyledString> = vec![];
|
||||
|
||||
for (row, row_style) in self.text.iter().zip(&self.styles) {
|
||||
let mut current_style = Style::NoStyle;
|
||||
let mut current_text = String::new();
|
||||
|
||||
for (&c, &s) in row.iter().zip(row_style) {
|
||||
if s != current_style {
|
||||
if !current_text.is_empty() {
|
||||
styled_vec.push(StyledString { text: current_text, style: current_style });
|
||||
}
|
||||
current_style = s;
|
||||
current_text = String::new();
|
||||
}
|
||||
current_text.push(c);
|
||||
}
|
||||
if !current_text.is_empty() {
|
||||
styled_vec.push(StyledString { text: current_text, style: current_style });
|
||||
}
|
||||
|
||||
if output.is_empty() {
|
||||
//We know our first output line is source and the rest are highlights and labels
|
||||
output.push(RenderedLine { text: styled_vec, kind: source_kind.clone() });
|
||||
} else {
|
||||
output.push(RenderedLine { text: styled_vec, kind: RenderedLineKind::Annotations });
|
||||
}
|
||||
styled_vec = vec![];
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
|
||||
while line >= self.text.len() {
|
||||
self.text.push(vec![]);
|
||||
self.styles.push(vec![]);
|
||||
}
|
||||
|
||||
if col < self.text[line].len() {
|
||||
self.text[line][col] = chr;
|
||||
self.styles[line][col] = style;
|
||||
} else {
|
||||
let mut i = self.text[line].len();
|
||||
while i < col {
|
||||
let s = match self.text[0].get(i) {
|
||||
Some(&'\t') => '\t',
|
||||
_ => ' '
|
||||
};
|
||||
self.text[line].push(s);
|
||||
self.styles[line].push(Style::NoStyle);
|
||||
i += 1;
|
||||
}
|
||||
self.text[line].push(chr);
|
||||
self.styles[line].push(style);
|
||||
}
|
||||
}
|
||||
|
||||
fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
|
||||
let mut n = col;
|
||||
for c in string.chars() {
|
||||
self.putc(line, n, c, style);
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn set_style(&mut self, line: usize, col: usize, style: Style) {
|
||||
if self.styles.len() > line && self.styles[line].len() > col {
|
||||
self.styles[line][col] = style;
|
||||
}
|
||||
}
|
||||
|
||||
fn append(&mut self, line: usize, string: &str, style: Style) {
|
||||
if line >= self.text.len() {
|
||||
self.puts(line, 0, string, style);
|
||||
} else {
|
||||
let col = self.text[line].len();
|
||||
self.puts(line, col, string, style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileInfo {
|
||||
fn push_lines(&mut self,
|
||||
lines: &[LineInfo],
|
||||
is_primary: bool,
|
||||
label: Option<String>) {
|
||||
assert!(lines.len() > 0);
|
||||
|
||||
// If a span covers multiple lines, we reduce it to a single
|
||||
// point at the start of the span. This means that instead
|
||||
// of producing output like this:
|
||||
//
|
||||
// ```
|
||||
// --> foo.rs:2:1
|
||||
// 2 |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>)
|
||||
// |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// 3 |> -> Set<LR0Item<'grammar>>
|
||||
// |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// (and so on)
|
||||
// ```
|
||||
//
|
||||
// we produce:
|
||||
//
|
||||
// ```
|
||||
// --> foo.rs:2:1
|
||||
// 2 |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>)
|
||||
// ^
|
||||
// ```
|
||||
//
|
||||
// Basically, although this loses information, multi-line spans just
|
||||
// never look good.
|
||||
|
||||
let (line, start_col, mut end_col, is_minimized) = if lines.len() == 1 {
|
||||
(lines[0].line_index, lines[0].start_col, lines[0].end_col, false)
|
||||
} else {
|
||||
(lines[0].line_index, lines[0].start_col, CharPos(lines[0].start_col.0 + 1), true)
|
||||
};
|
||||
|
||||
// Watch out for "empty spans". If we get a span like 6..6, we
|
||||
// want to just display a `^` at 6, so convert that to
|
||||
// 6..7. This is degenerate input, but it's best to degrade
|
||||
// gracefully -- and the parser likes to suply a span like
|
||||
// that for EOF, in particular.
|
||||
if start_col == end_col {
|
||||
end_col.0 += 1;
|
||||
}
|
||||
|
||||
let index = self.ensure_source_line(line);
|
||||
self.lines[index].push_annotation(start_col,
|
||||
end_col,
|
||||
is_primary,
|
||||
is_minimized,
|
||||
label);
|
||||
}
|
||||
|
||||
/// Ensure that we have a `Line` struct corresponding to
|
||||
/// `line_index` in the file. If we already have some other lines,
|
||||
/// then this will add the intervening lines to ensure that we
|
||||
/// have a complete snippet. (Note that when we finally display,
|
||||
/// some of those lines may be elided.)
|
||||
fn ensure_source_line(&mut self, line_index: usize) -> usize {
|
||||
if self.lines.is_empty() {
|
||||
self.lines.push(Line::new(line_index));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the range of lines we have thus far.
|
||||
let first_line_index = self.lines.first().unwrap().line_index;
|
||||
let last_line_index = self.lines.last().unwrap().line_index;
|
||||
assert!(first_line_index <= last_line_index);
|
||||
|
||||
// If the new line is lower than all the lines we have thus
|
||||
// far, then insert the new line and any intervening lines at
|
||||
// the front. In a silly attempt at micro-optimization, we
|
||||
// don't just call `insert` repeatedly, but instead make a new
|
||||
// (empty) vector, pushing the new lines onto it, and then
|
||||
// appending the old vector.
|
||||
if line_index < first_line_index {
|
||||
let lines = mem::replace(&mut self.lines, vec![]);
|
||||
self.lines.extend(
|
||||
(line_index .. first_line_index)
|
||||
.map(|line| Line::new(line))
|
||||
.chain(lines));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the new line comes after the ones we have so far, insert
|
||||
// lines for it.
|
||||
if line_index > last_line_index {
|
||||
self.lines.extend(
|
||||
(last_line_index+1 .. line_index+1)
|
||||
.map(|line| Line::new(line)));
|
||||
return self.lines.len() - 1;
|
||||
}
|
||||
|
||||
// Otherwise it should already exist.
|
||||
return line_index - first_line_index;
|
||||
}
|
||||
|
||||
fn render_file_lines(&self, codemap: &Rc<CodeMapper>) -> Vec<RenderedLine> {
|
||||
let old_school = match self.format_mode {
|
||||
FormatMode::OriginalErrorFormat => true,
|
||||
FormatMode::NewErrorFormat => false,
|
||||
FormatMode::EnvironmentSelected => check_old_skool()
|
||||
};
|
||||
|
||||
// As a first step, we elide any instance of more than one
|
||||
// continuous unannotated line.
|
||||
|
||||
let mut lines_iter = self.lines.iter();
|
||||
let mut output = vec![];
|
||||
|
||||
// First insert the name of the file.
|
||||
if !old_school {
|
||||
match self.primary_span {
|
||||
Some(span) => {
|
||||
let lo = codemap.lookup_char_pos(span.lo);
|
||||
output.push(RenderedLine {
|
||||
text: vec![StyledString {
|
||||
text: lo.file.name.clone(),
|
||||
style: Style::FileNameStyle,
|
||||
}, StyledString {
|
||||
text: format!(":{}:{}", lo.line, lo.col.0 + 1),
|
||||
style: Style::LineAndColumn,
|
||||
}],
|
||||
kind: RenderedLineKind::PrimaryFileName,
|
||||
});
|
||||
output.push(RenderedLine {
|
||||
text: vec![StyledString {
|
||||
text: "".to_string(),
|
||||
style: Style::FileNameStyle,
|
||||
}],
|
||||
kind: RenderedLineKind::Annotations,
|
||||
});
|
||||
}
|
||||
None => {
|
||||
output.push(RenderedLine {
|
||||
text: vec![StyledString {
|
||||
text: self.file.name.clone(),
|
||||
style: Style::FileNameStyle,
|
||||
}],
|
||||
kind: RenderedLineKind::OtherFileName,
|
||||
});
|
||||
output.push(RenderedLine {
|
||||
text: vec![StyledString {
|
||||
text: "".to_string(),
|
||||
style: Style::FileNameStyle,
|
||||
}],
|
||||
kind: RenderedLineKind::Annotations,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut next_line = lines_iter.next();
|
||||
while next_line.is_some() {
|
||||
// Consume lines with annotations.
|
||||
while let Some(line) = next_line {
|
||||
if line.annotations.is_empty() { break; }
|
||||
|
||||
let mut rendered_lines = self.render_line(line);
|
||||
assert!(!rendered_lines.is_empty());
|
||||
if old_school {
|
||||
match self.primary_span {
|
||||
Some(span) => {
|
||||
let lo = codemap.lookup_char_pos(span.lo);
|
||||
let hi = codemap.lookup_char_pos(span.hi);
|
||||
//Before each secondary line in old skool-mode, print the label
|
||||
//as an old-style note
|
||||
if !line.annotations[0].is_primary {
|
||||
if let Some(ann) = line.annotations[0].label.clone() {
|
||||
output.push(RenderedLine {
|
||||
text: vec![StyledString {
|
||||
text: lo.file.name.clone(),
|
||||
style: Style::FileNameStyle,
|
||||
}, StyledString {
|
||||
text: format!(":{}:{}: {}:{} ", lo.line, lo.col.0 + 1,
|
||||
hi.line, hi.col.0+1),
|
||||
style: Style::LineAndColumn,
|
||||
}, StyledString {
|
||||
text: format!("note: "),
|
||||
style: Style::OldSkoolNote,
|
||||
}, StyledString {
|
||||
text: format!("{}", ann),
|
||||
style: Style::OldSkoolNoteText,
|
||||
}],
|
||||
kind: RenderedLineKind::Annotations,
|
||||
});
|
||||
}
|
||||
}
|
||||
rendered_lines[0].text.insert(0, StyledString {
|
||||
text: format!(":{} ", lo.line),
|
||||
style: Style::LineAndColumn,
|
||||
});
|
||||
rendered_lines[0].text.insert(0, StyledString {
|
||||
text: lo.file.name.clone(),
|
||||
style: Style::FileNameStyle,
|
||||
});
|
||||
let gap_amount =
|
||||
rendered_lines[0].text[0].text.len() +
|
||||
rendered_lines[0].text[1].text.len();
|
||||
assert!(rendered_lines.len() >= 2,
|
||||
"no annotations resulted from: {:?}",
|
||||
line);
|
||||
for i in 1..rendered_lines.len() {
|
||||
rendered_lines[i].text.insert(0, StyledString {
|
||||
text: vec![" "; gap_amount].join(""),
|
||||
style: Style::NoStyle
|
||||
});
|
||||
}
|
||||
}
|
||||
_ =>()
|
||||
}
|
||||
}
|
||||
output.append(&mut rendered_lines);
|
||||
next_line = lines_iter.next();
|
||||
}
|
||||
|
||||
// Emit lines without annotations, but only if they are
|
||||
// followed by a line with an annotation.
|
||||
let unannotated_line = next_line;
|
||||
let mut unannotated_lines = 0;
|
||||
while let Some(line) = next_line {
|
||||
if !line.annotations.is_empty() { break; }
|
||||
unannotated_lines += 1;
|
||||
next_line = lines_iter.next();
|
||||
}
|
||||
if unannotated_lines > 1 {
|
||||
output.push(RenderedLine::from((String::new(),
|
||||
Style::NoStyle,
|
||||
RenderedLineKind::Elision)));
|
||||
} else if let Some(line) = unannotated_line {
|
||||
output.append(&mut self.render_line(line));
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
|
||||
let old_school = match self.format_mode {
|
||||
FormatMode::OriginalErrorFormat => true,
|
||||
FormatMode::NewErrorFormat => false,
|
||||
FormatMode::EnvironmentSelected => check_old_skool()
|
||||
};
|
||||
|
||||
let source_string = self.file.get_line(line.line_index)
|
||||
.unwrap_or("");
|
||||
let source_kind = RenderedLineKind::SourceText {
|
||||
file: self.file.clone(),
|
||||
line_index: line.line_index,
|
||||
};
|
||||
|
||||
let mut styled_buffer = StyledBuffer::new();
|
||||
|
||||
// First create the source line we will highlight.
|
||||
styled_buffer.append(0, &source_string, Style::Quotation);
|
||||
|
||||
if line.annotations.is_empty() {
|
||||
return styled_buffer.render(source_kind);
|
||||
}
|
||||
|
||||
// We want to display like this:
|
||||
//
|
||||
// vec.push(vec.pop().unwrap());
|
||||
// --- ^^^ _ previous borrow ends here
|
||||
// | |
|
||||
// | error occurs here
|
||||
// previous borrow of `vec` occurs here
|
||||
//
|
||||
// But there are some weird edge cases to be aware of:
|
||||
//
|
||||
// vec.push(vec.pop().unwrap());
|
||||
// -------- - previous borrow ends here
|
||||
// ||
|
||||
// |this makes no sense
|
||||
// previous borrow of `vec` occurs here
|
||||
//
|
||||
// For this reason, we group the lines into "highlight lines"
|
||||
// and "annotations lines", where the highlight lines have the `~`.
|
||||
|
||||
//let mut highlight_line = Self::whitespace(&source_string);
|
||||
|
||||
// Sort the annotations by (start, end col)
|
||||
let mut annotations = line.annotations.clone();
|
||||
annotations.sort();
|
||||
|
||||
// Next, create the highlight line.
|
||||
for annotation in &annotations {
|
||||
if old_school {
|
||||
for p in annotation.start_col .. annotation.end_col {
|
||||
if p == annotation.start_col {
|
||||
styled_buffer.putc(1, p, '^',
|
||||
if annotation.is_primary {
|
||||
Style::UnderlinePrimary
|
||||
} else {
|
||||
Style::OldSkoolNote
|
||||
});
|
||||
}
|
||||
else {
|
||||
styled_buffer.putc(1, p, '~',
|
||||
if annotation.is_primary {
|
||||
Style::UnderlinePrimary
|
||||
} else {
|
||||
Style::OldSkoolNote
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for p in annotation.start_col .. annotation.end_col {
|
||||
if annotation.is_primary {
|
||||
styled_buffer.putc(1, p, '^', Style::UnderlinePrimary);
|
||||
if !annotation.is_minimized {
|
||||
styled_buffer.set_style(0, p, Style::UnderlinePrimary);
|
||||
}
|
||||
} else {
|
||||
styled_buffer.putc(1, p, '-', Style::UnderlineSecondary);
|
||||
if !annotation.is_minimized {
|
||||
styled_buffer.set_style(0, p, Style::UnderlineSecondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we are going to write labels in. To start, we'll exclude
|
||||
// the annotations with no labels.
|
||||
let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) =
|
||||
annotations.into_iter()
|
||||
.partition(|a| a.label.is_some());
|
||||
|
||||
// If there are no annotations that need text, we're done.
|
||||
if labeled_annotations.is_empty() {
|
||||
return styled_buffer.render(source_kind);
|
||||
}
|
||||
if old_school {
|
||||
return styled_buffer.render(source_kind);
|
||||
}
|
||||
|
||||
// Now add the text labels. We try, when possible, to stick the rightmost
|
||||
// annotation at the end of the highlight line:
|
||||
//
|
||||
// vec.push(vec.pop().unwrap());
|
||||
// --- --- - previous borrow ends here
|
||||
//
|
||||
// But sometimes that's not possible because one of the other
|
||||
// annotations overlaps it. For example, from the test
|
||||
// `span_overlap_label`, we have the following annotations
|
||||
// (written on distinct lines for clarity):
|
||||
//
|
||||
// fn foo(x: u32) {
|
||||
// --------------
|
||||
// -
|
||||
//
|
||||
// In this case, we can't stick the rightmost-most label on
|
||||
// the highlight line, or we would get:
|
||||
//
|
||||
// fn foo(x: u32) {
|
||||
// -------- x_span
|
||||
// |
|
||||
// fn_span
|
||||
//
|
||||
// which is totally weird. Instead we want:
|
||||
//
|
||||
// fn foo(x: u32) {
|
||||
// --------------
|
||||
// | |
|
||||
// | x_span
|
||||
// fn_span
|
||||
//
|
||||
// which is...less weird, at least. In fact, in general, if
|
||||
// the rightmost span overlaps with any other span, we should
|
||||
// use the "hang below" version, so we can at least make it
|
||||
// clear where the span *starts*.
|
||||
let mut labeled_annotations = &labeled_annotations[..];
|
||||
match labeled_annotations.split_last().unwrap() {
|
||||
(last, previous) => {
|
||||
if previous.iter()
|
||||
.chain(&unlabeled_annotations)
|
||||
.all(|a| !overlaps(a, last))
|
||||
{
|
||||
// append the label afterwards; we keep it in a separate
|
||||
// string
|
||||
let highlight_label: String = format!(" {}", last.label.as_ref().unwrap());
|
||||
if last.is_primary {
|
||||
styled_buffer.append(1, &highlight_label, Style::LabelPrimary);
|
||||
} else {
|
||||
styled_buffer.append(1, &highlight_label, Style::LabelSecondary);
|
||||
}
|
||||
labeled_annotations = previous;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If that's the last annotation, we're done
|
||||
if labeled_annotations.is_empty() {
|
||||
return styled_buffer.render(source_kind);
|
||||
}
|
||||
|
||||
for (index, annotation) in labeled_annotations.iter().enumerate() {
|
||||
// Leave:
|
||||
// - 1 extra line
|
||||
// - One line for each thing that comes after
|
||||
let comes_after = labeled_annotations.len() - index - 1;
|
||||
let blank_lines = 3 + comes_after;
|
||||
|
||||
// For each blank line, draw a `|` at our column. The
|
||||
// text ought to be long enough for this.
|
||||
for index in 2..blank_lines {
|
||||
if annotation.is_primary {
|
||||
styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlinePrimary);
|
||||
} else {
|
||||
styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlineSecondary);
|
||||
}
|
||||
}
|
||||
|
||||
if annotation.is_primary {
|
||||
styled_buffer.puts(blank_lines, annotation.start_col,
|
||||
annotation.label.as_ref().unwrap(), Style::LabelPrimary);
|
||||
} else {
|
||||
styled_buffer.puts(blank_lines, annotation.start_col,
|
||||
annotation.label.as_ref().unwrap(), Style::LabelSecondary);
|
||||
}
|
||||
}
|
||||
|
||||
styled_buffer.render(source_kind)
|
||||
}
|
||||
}
|
||||
|
||||
fn prepend_prefixes(rendered_lines: &mut [RenderedLine], format_mode: &FormatMode) {
|
||||
let old_school = match *format_mode {
|
||||
FormatMode::OriginalErrorFormat => true,
|
||||
FormatMode::NewErrorFormat => false,
|
||||
FormatMode::EnvironmentSelected => check_old_skool()
|
||||
};
|
||||
if old_school {
|
||||
return;
|
||||
}
|
||||
|
||||
let prefixes: Vec<_> =
|
||||
rendered_lines.iter()
|
||||
.map(|rl| rl.kind.prefix())
|
||||
.collect();
|
||||
|
||||
// find the max amount of spacing we need; add 1 to
|
||||
// p.text.len() to leave space between the prefix and the
|
||||
// source text
|
||||
let padding_len =
|
||||
prefixes.iter()
|
||||
.map(|p| if p.text.len() == 0 { 0 } else { p.text.len() + 1 })
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
|
||||
// Ensure we insert at least one character of padding, so that the
|
||||
// `-->` arrows can fit etc.
|
||||
let padding_len = cmp::max(padding_len, 1);
|
||||
|
||||
for (mut prefix, line) in prefixes.into_iter().zip(rendered_lines) {
|
||||
let extra_spaces = (prefix.text.len() .. padding_len).map(|_| ' ');
|
||||
prefix.text.extend(extra_spaces);
|
||||
match line.kind {
|
||||
RenderedLineKind::Elision => {
|
||||
line.text.insert(0, prefix);
|
||||
}
|
||||
RenderedLineKind::PrimaryFileName => {
|
||||
// --> filename
|
||||
// 22 |>
|
||||
// ^
|
||||
// padding_len
|
||||
let dashes = (0..padding_len - 1).map(|_| ' ')
|
||||
.chain(Some('-'))
|
||||
.chain(Some('-'))
|
||||
.chain(Some('>'))
|
||||
.chain(Some(' '));
|
||||
line.text.insert(0, StyledString {text: dashes.collect(),
|
||||
style: Style::LineNumber})
|
||||
}
|
||||
RenderedLineKind::OtherFileName => {
|
||||
// ::: filename
|
||||
// 22 |>
|
||||
// ^
|
||||
// padding_len
|
||||
let dashes = (0..padding_len - 1).map(|_| ' ')
|
||||
.chain(Some(':'))
|
||||
.chain(Some(':'))
|
||||
.chain(Some(':'))
|
||||
.chain(Some(' '));
|
||||
line.text.insert(0, StyledString {text: dashes.collect(),
|
||||
style: Style::LineNumber})
|
||||
}
|
||||
_ => {
|
||||
line.text.insert(0, prefix);
|
||||
line.text.insert(1, StyledString {text: String::from("|> "),
|
||||
style: Style::LineNumber})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn trim_lines(rendered_lines: &mut [RenderedLine]) {
|
||||
for line in rendered_lines {
|
||||
while !line.text.is_empty() {
|
||||
line.trim_last();
|
||||
if line.text.last().unwrap().text.is_empty() {
|
||||
line.text.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Line {
|
||||
fn new(line_index: usize) -> Line {
|
||||
Line {
|
||||
line_index: line_index,
|
||||
annotations: vec![]
|
||||
}
|
||||
}
|
||||
|
||||
fn push_annotation(&mut self,
|
||||
start: CharPos,
|
||||
end: CharPos,
|
||||
is_primary: bool,
|
||||
is_minimized: bool,
|
||||
label: Option<String>) {
|
||||
self.annotations.push(Annotation {
|
||||
start_col: start.0,
|
||||
end_col: end.0,
|
||||
is_primary: is_primary,
|
||||
is_minimized: is_minimized,
|
||||
label: label,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn overlaps(a1: &Annotation,
|
||||
a2: &Annotation)
|
||||
-> bool
|
||||
{
|
||||
(a2.start_col .. a2.end_col).contains(a1.start_col) ||
|
||||
(a1.start_col .. a1.end_col).contains(a2.start_col)
|
||||
}
|
||||
ErrorCode,
|
||||
Level(Level),
|
||||
}
|
||||
146
src/librustc_errors/styled_buffer.rs
Normal file
146
src/librustc_errors/styled_buffer.rs
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Code for creating styled buffers
|
||||
|
||||
use snippet::{Style, StyledString};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StyledBuffer {
|
||||
text: Vec<Vec<char>>,
|
||||
styles: Vec<Vec<Style>>,
|
||||
}
|
||||
|
||||
impl StyledBuffer {
|
||||
pub fn new() -> StyledBuffer {
|
||||
StyledBuffer {
|
||||
text: vec![],
|
||||
styles: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_tabs(&mut self, row: usize) {
|
||||
if row < self.text.len() {
|
||||
for i in row+1..self.text.len() {
|
||||
for j in 0..self.text[i].len() {
|
||||
if self.text[row].len() > j &&
|
||||
self.text[row][j] == '\t' &&
|
||||
self.text[i][j] == ' ' {
|
||||
self.text[i][j] = '\t';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&mut self) -> Vec<Vec<StyledString>> {
|
||||
let mut output: Vec<Vec<StyledString>> = vec![];
|
||||
let mut styled_vec: Vec<StyledString> = vec![];
|
||||
|
||||
//before we render, do a little patch-up work to support tabs
|
||||
self.copy_tabs(3);
|
||||
|
||||
for (row, row_style) in self.text.iter().zip(&self.styles) {
|
||||
let mut current_style = Style::NoStyle;
|
||||
let mut current_text = String::new();
|
||||
|
||||
for (&c, &s) in row.iter().zip(row_style) {
|
||||
if s != current_style {
|
||||
if !current_text.is_empty() {
|
||||
styled_vec.push(StyledString {
|
||||
text: current_text,
|
||||
style: current_style,
|
||||
});
|
||||
}
|
||||
current_style = s;
|
||||
current_text = String::new();
|
||||
}
|
||||
current_text.push(c);
|
||||
}
|
||||
if !current_text.is_empty() {
|
||||
styled_vec.push(StyledString {
|
||||
text: current_text,
|
||||
style: current_style,
|
||||
});
|
||||
}
|
||||
|
||||
// We're done with the row, push and keep going
|
||||
output.push(styled_vec);
|
||||
|
||||
styled_vec = vec![];
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn ensure_lines(&mut self, line: usize) {
|
||||
while line >= self.text.len() {
|
||||
self.text.push(vec![]);
|
||||
self.styles.push(vec![]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
|
||||
self.ensure_lines(line);
|
||||
if col < self.text[line].len() {
|
||||
self.text[line][col] = chr;
|
||||
self.styles[line][col] = style;
|
||||
} else {
|
||||
let mut i = self.text[line].len();
|
||||
while i < col {
|
||||
self.text[line].push(' ');
|
||||
self.styles[line].push(Style::NoStyle);
|
||||
i += 1;
|
||||
}
|
||||
self.text[line].push(chr);
|
||||
self.styles[line].push(style);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
|
||||
let mut n = col;
|
||||
for c in string.chars() {
|
||||
self.putc(line, n, c, style);
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_style(&mut self, line: usize, col: usize, style: Style) {
|
||||
if self.styles.len() > line && self.styles[line].len() > col {
|
||||
self.styles[line][col] = style;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepend(&mut self, line: usize, string: &str, style: Style) {
|
||||
self.ensure_lines(line);
|
||||
let string_len = string.len();
|
||||
|
||||
// Push the old content over to make room for new content
|
||||
for _ in 0..string_len {
|
||||
self.styles[line].insert(0, Style::NoStyle);
|
||||
self.text[line].insert(0, ' ');
|
||||
}
|
||||
|
||||
self.puts(line, 0, string, style);
|
||||
}
|
||||
|
||||
pub fn append(&mut self, line: usize, string: &str, style: Style) {
|
||||
if line >= self.text.len() {
|
||||
self.puts(line, 0, string, style);
|
||||
} else {
|
||||
let col = self.text[line].len();
|
||||
self.puts(line, col, string, style);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_lines(&self) -> usize {
|
||||
self.text.len()
|
||||
}
|
||||
}
|
||||
|
|
@ -698,7 +698,8 @@ impl LateLintPass for VariantSizeDifferences {
|
|||
if gens.ty_params.is_empty() { // sizes only make sense for non-generic types
|
||||
let t = cx.tcx.node_id_to_type(it.id);
|
||||
let layout = cx.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| {
|
||||
t.layout(&infcx).unwrap_or_else(|e| {
|
||||
let ty = cx.tcx.erase_regions(&t);
|
||||
ty.layout(&infcx).unwrap_or_else(|e| {
|
||||
bug!("failed to get layout for `{}`: {}", t, e)
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -687,32 +687,6 @@ fn each_child_of_item_or_crate<F, G>(cdata: Cmd,
|
|||
}
|
||||
}
|
||||
|
||||
// As a special case, iterate over all static methods of
|
||||
// associated implementations too. This is a bit of a botch.
|
||||
// --pcwalton
|
||||
for inherent_impl_def_id_doc in reader::tagged_docs(item_doc,
|
||||
tag_items_data_item_inherent_impl) {
|
||||
let inherent_impl_def_id = item_def_id(inherent_impl_def_id_doc, cdata);
|
||||
if let Some(inherent_impl_doc) = cdata.get_item(inherent_impl_def_id.index) {
|
||||
for impl_item_def_id_doc in reader::tagged_docs(inherent_impl_doc,
|
||||
tag_item_impl_item) {
|
||||
let impl_item_def_id = item_def_id(impl_item_def_id_doc,
|
||||
cdata);
|
||||
if let Some(impl_method_doc) = cdata.get_item(impl_item_def_id.index) {
|
||||
if let StaticMethod = item_family(impl_method_doc) {
|
||||
// Hand off the static method to the callback.
|
||||
let static_method_name = item_name(impl_method_doc);
|
||||
let static_method_def_like = item_to_def_like(cdata, impl_method_doc,
|
||||
impl_item_def_id);
|
||||
callback(static_method_def_like,
|
||||
static_method_name,
|
||||
item_visibility(impl_method_doc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for reexport_doc in reexports(item_doc) {
|
||||
let def_id_doc = reader::get_doc(reexport_doc,
|
||||
tag_items_data_item_reexport_def_id);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
use Resolver;
|
||||
use rustc::session::Session;
|
||||
use syntax::ast;
|
||||
use syntax::ext::mtwt;
|
||||
use syntax::ext::hygiene::Mark;
|
||||
use syntax::fold::{self, Folder};
|
||||
use syntax::ptr::P;
|
||||
use syntax::util::move_map::MoveMap;
|
||||
|
|
@ -31,7 +31,7 @@ impl<'a> Resolver<'a> {
|
|||
|
||||
struct NodeIdAssigner<'a> {
|
||||
sess: &'a Session,
|
||||
macros_at_scope: &'a mut HashMap<ast::NodeId, Vec<ast::Mrk>>,
|
||||
macros_at_scope: &'a mut HashMap<ast::NodeId, Vec<Mark>>,
|
||||
}
|
||||
|
||||
impl<'a> Folder for NodeIdAssigner<'a> {
|
||||
|
|
@ -49,7 +49,7 @@ impl<'a> Folder for NodeIdAssigner<'a> {
|
|||
block.stmts = block.stmts.move_flat_map(|stmt| {
|
||||
if let ast::StmtKind::Item(ref item) = stmt.node {
|
||||
if let ast::ItemKind::Mac(..) = item.node {
|
||||
macros.push(mtwt::outer_mark(item.ident.ctxt));
|
||||
macros.push(item.ident.ctxt.data().outer_mark);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ use rustc::ty::subst::{ParamSpace, FnSpace, TypeSpace};
|
|||
use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
|
||||
use rustc::util::nodemap::{NodeMap, NodeSet, FnvHashMap, FnvHashSet};
|
||||
|
||||
use syntax::ext::mtwt;
|
||||
use syntax::ext::hygiene::Mark;
|
||||
use syntax::ast::{self, FloatTy};
|
||||
use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy};
|
||||
use syntax::parse::token::{self, keywords};
|
||||
|
|
@ -654,7 +654,7 @@ enum RibKind<'a> {
|
|||
ModuleRibKind(Module<'a>),
|
||||
|
||||
// We passed through a `macro_rules!` statement with the given expansion
|
||||
MacroDefinition(ast::Mrk),
|
||||
MacroDefinition(Mark),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
@ -933,7 +933,7 @@ pub struct Resolver<'a> {
|
|||
|
||||
// Maps the node id of a statement to the expansions of the `macro_rules!`s
|
||||
// immediately above the statement (if appropriate).
|
||||
macros_at_scope: HashMap<NodeId, Vec<ast::Mrk>>,
|
||||
macros_at_scope: HashMap<NodeId, Vec<Mark>>,
|
||||
|
||||
graph_root: Module<'a>,
|
||||
|
||||
|
|
@ -1434,10 +1434,9 @@ impl<'a> Resolver<'a> {
|
|||
if let MacroDefinition(mac) = self.get_ribs(ns)[i].kind {
|
||||
// If an invocation of this macro created `ident`, give up on `ident`
|
||||
// and switch to `ident`'s source from the macro definition.
|
||||
if let Some((source_ident, source_macro)) = mtwt::source(ident) {
|
||||
if mac == source_macro {
|
||||
ident = source_ident;
|
||||
}
|
||||
let (source_ctxt, source_macro) = ident.ctxt.source();
|
||||
if source_macro == mac {
|
||||
ident.ctxt = source_ctxt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1585,10 +1584,9 @@ impl<'a> Resolver<'a> {
|
|||
MacroDefinition(mac) => {
|
||||
// If an invocation of this macro created `ident`, give up on `ident`
|
||||
// and switch to `ident`'s source from the macro definition.
|
||||
if let Some((source_ident, source_macro)) = mtwt::source(ident) {
|
||||
if mac == source_macro {
|
||||
ident = source_ident;
|
||||
}
|
||||
let (source_ctxt, source_macro) = ident.ctxt.source();
|
||||
if source_macro == mac {
|
||||
ident.ctxt = source_ctxt;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ use llvm::SMDiagnosticRef;
|
|||
use {CrateTranslation, ModuleTranslation};
|
||||
use util::common::time;
|
||||
use util::common::path2cstr;
|
||||
use errors::{self, Handler, Level, RenderSpan};
|
||||
use errors::emitter::CoreEmitter;
|
||||
use errors::{self, Handler, Level, DiagnosticBuilder};
|
||||
use errors::emitter::Emitter;
|
||||
use syntax_pos::MultiSpan;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -100,23 +100,23 @@ impl SharedEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
impl CoreEmitter for SharedEmitter {
|
||||
fn emit_message(&mut self,
|
||||
_rsp: &RenderSpan,
|
||||
msg: &str,
|
||||
code: Option<&str>,
|
||||
lvl: Level,
|
||||
_is_header: bool,
|
||||
_show_snippet: bool) {
|
||||
impl Emitter for SharedEmitter {
|
||||
fn emit(&mut self, db: &DiagnosticBuilder) {
|
||||
self.buffer.lock().unwrap().push(Diagnostic {
|
||||
msg: msg.to_string(),
|
||||
code: code.map(|s| s.to_string()),
|
||||
lvl: lvl,
|
||||
msg: db.message.to_string(),
|
||||
code: db.code.clone(),
|
||||
lvl: db.level,
|
||||
});
|
||||
for child in &db.children {
|
||||
self.buffer.lock().unwrap().push(Diagnostic {
|
||||
msg: child.message.to_string(),
|
||||
code: None,
|
||||
lvl: child.level,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// On android, we by default compile for armv7 processors. This enables
|
||||
// things like double word CAS instructions (rather than emulating them)
|
||||
// which are *far* more efficient. This is obviously undesirable in some
|
||||
|
|
|
|||
|
|
@ -2270,12 +2270,9 @@ fn internalize_symbols(cx: &CrateContextList, reachable: &HashSet<&str>) {
|
|||
let is_decl = llvm::LLVMIsDeclaration(val) != 0;
|
||||
|
||||
if is_decl || is_available_externally {
|
||||
let name = CStr::from_ptr(llvm::LLVMGetValueName(val))
|
||||
.to_bytes()
|
||||
.to_vec();
|
||||
let name = CStr::from_ptr(llvm::LLVMGetValueName(val));
|
||||
declared.insert(name);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2286,21 +2283,21 @@ fn internalize_symbols(cx: &CrateContextList, reachable: &HashSet<&str>) {
|
|||
for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
|
||||
let linkage = llvm::LLVMGetLinkage(val);
|
||||
|
||||
let is_external = linkage == llvm::ExternalLinkage as c_uint;
|
||||
let is_weak_odr = linkage == llvm::WeakODRLinkage as c_uint;
|
||||
let is_decl = llvm::LLVMIsDeclaration(val) != 0;
|
||||
let is_externally_visible = (linkage == llvm::ExternalLinkage as c_uint) ||
|
||||
(linkage == llvm::LinkOnceODRLinkage as c_uint) ||
|
||||
(linkage == llvm::WeakODRLinkage as c_uint);
|
||||
let is_definition = llvm::LLVMIsDeclaration(val) != 0;
|
||||
|
||||
// We only care about external definitions.
|
||||
if (is_external || is_weak_odr) && !is_decl {
|
||||
// If this is a definition (as opposed to just a declaration)
|
||||
// and externally visible, check if we can internalize it
|
||||
if is_definition && is_externally_visible {
|
||||
let name_cstr = CStr::from_ptr(llvm::LLVMGetValueName(val));
|
||||
let name_str = name_cstr.to_str().unwrap();
|
||||
|
||||
let name = CStr::from_ptr(llvm::LLVMGetValueName(val))
|
||||
.to_bytes()
|
||||
.to_vec();
|
||||
let is_referenced_somewhere = declared.contains(&name_cstr);
|
||||
let is_reachable = reachable.contains(name_str);
|
||||
|
||||
let is_declared = declared.contains(&name);
|
||||
let reachable = reachable.contains(str::from_utf8(&name).unwrap());
|
||||
|
||||
if !is_declared && !reachable {
|
||||
if !is_referenced_somewhere && !is_reachable {
|
||||
llvm::SetLinkage(val, llvm::InternalLinkage);
|
||||
llvm::SetDLLStorageClass(val, llvm::DefaultStorageClass);
|
||||
llvm::UnsetComdat(val);
|
||||
|
|
@ -2488,7 +2485,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
// Run the translation item collector and partition the collected items into
|
||||
// codegen units.
|
||||
let (codegen_units, symbol_map) = collect_and_partition_translation_items(&shared_ccx);
|
||||
let codegen_unit_count = codegen_units.len();
|
||||
|
||||
let symbol_map = Rc::new(symbol_map);
|
||||
|
||||
|
|
@ -2620,10 +2616,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
}));
|
||||
}
|
||||
|
||||
if codegen_unit_count > 1 {
|
||||
internalize_symbols(&crate_context_list,
|
||||
&reachable_symbols.iter().map(|x| &x[..]).collect());
|
||||
}
|
||||
internalize_symbols(&crate_context_list,
|
||||
&reachable_symbols.iter().map(|x| &x[..]).collect());
|
||||
|
||||
if sess.target.target.options.is_like_msvc &&
|
||||
sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) {
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ pub fn run_core(search_paths: SearchPaths,
|
|||
None,
|
||||
true,
|
||||
false,
|
||||
codemap.clone());
|
||||
Some(codemap.clone()));
|
||||
|
||||
let dep_graph = DepGraph::new(false);
|
||||
let _ignore = dep_graph.in_ignore();
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ pub fn run(input: &str,
|
|||
None,
|
||||
true,
|
||||
false,
|
||||
codemap.clone());
|
||||
Some(codemap.clone()));
|
||||
|
||||
let dep_graph = DepGraph::new(false);
|
||||
let _ignore = dep_graph.in_ignore();
|
||||
|
|
@ -229,7 +229,7 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
|
|||
let codemap = Rc::new(CodeMap::new());
|
||||
let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()),
|
||||
None,
|
||||
codemap.clone(),
|
||||
Some(codemap.clone()),
|
||||
errors::snippet::FormatMode::EnvironmentSelected);
|
||||
let old = io::set_panic(box Sink(data.clone()));
|
||||
let _bomb = Bomb(data.clone(), old.unwrap_or(box io::stdout()));
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ pub use util::ThinVec;
|
|||
use syntax_pos::{mk_sp, Span, DUMMY_SP, ExpnId};
|
||||
use codemap::{respan, Spanned};
|
||||
use abi::Abi;
|
||||
use ext::hygiene::SyntaxContext;
|
||||
use parse::token::{self, keywords, InternedString};
|
||||
use print::pprust;
|
||||
use ptr::P;
|
||||
|
|
@ -33,15 +34,6 @@ use serialize::{Encodable, Decodable, Encoder, Decoder};
|
|||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Name(pub u32);
|
||||
|
||||
/// A SyntaxContext represents a chain of macro-expandings
|
||||
/// and renamings. Each macro expansion corresponds to
|
||||
/// a fresh u32. This u32 is a reference to a table stored
|
||||
/// in thread-local storage.
|
||||
/// The special value EMPTY_CTXT is used to indicate an empty
|
||||
/// syntax context.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
|
||||
pub struct SyntaxContext(pub u32);
|
||||
|
||||
/// An identifier contains a Name (index into the interner
|
||||
/// table) and a SyntaxContext to track renaming and
|
||||
/// macro expansion per Flatt et al., "Macros That Work Together"
|
||||
|
|
@ -81,20 +73,15 @@ impl Decodable for Name {
|
|||
}
|
||||
}
|
||||
|
||||
pub const EMPTY_CTXT : SyntaxContext = SyntaxContext(0);
|
||||
|
||||
impl Ident {
|
||||
pub fn new(name: Name, ctxt: SyntaxContext) -> Ident {
|
||||
Ident {name: name, ctxt: ctxt}
|
||||
}
|
||||
pub const fn with_empty_ctxt(name: Name) -> Ident {
|
||||
Ident {name: name, ctxt: EMPTY_CTXT}
|
||||
Ident { name: name, ctxt: SyntaxContext::empty() }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Ident {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}#{}", self.name, self.ctxt.0)
|
||||
write!(f, "{}{:?}", self.name, self.ctxt)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,9 +103,6 @@ impl Decodable for Ident {
|
|||
}
|
||||
}
|
||||
|
||||
/// A mark represents a unique id associated with a macro expansion
|
||||
pub type Mrk = u32;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)]
|
||||
pub struct Lifetime {
|
||||
pub id: NodeId,
|
||||
|
|
|
|||
|
|
@ -827,12 +827,6 @@ impl CodeMapper for CodeMap {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use errors::{Level, CodeSuggestion};
|
||||
use errors::emitter::EmitterWriter;
|
||||
use errors::snippet::{SnippetData, RenderedLine, FormatMode};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::io::{self, Write};
|
||||
use std::str::from_utf8;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
|
|
@ -1122,24 +1116,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn splice(start: Span, end: Span) -> Span {
|
||||
Span {
|
||||
lo: start.lo,
|
||||
hi: end.hi,
|
||||
expn_id: NO_EXPANSION,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_string(lines: &[RenderedLine]) -> String {
|
||||
lines.iter()
|
||||
.flat_map(|rl| {
|
||||
rl.text.iter()
|
||||
.map(|s| &s.text[..])
|
||||
.chain(Some("\n"))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn init_expansion_chain(cm: &CodeMap) -> Span {
|
||||
// Creates an expansion chain containing two recursive calls
|
||||
// root -> expA -> expA -> expB -> expB -> end
|
||||
|
|
@ -1219,761 +1195,4 @@ r"blork2.rs:2:1: 2:12
|
|||
";
|
||||
assert_eq!(sstr, res_str);
|
||||
}
|
||||
|
||||
struct Sink(Arc<Mutex<Vec<u8>>>);
|
||||
impl Write for Sink {
|
||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
Write::write(&mut *self.0.lock().unwrap(), data)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
// Diagnostic doesn't align properly in span where line number increases by one digit
|
||||
#[test]
|
||||
fn test_hilight_suggestion_issue_11715() {
|
||||
let data = Arc::new(Mutex::new(Vec::new()));
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())),
|
||||
None,
|
||||
cm.clone(),
|
||||
FormatMode::NewErrorFormat);
|
||||
let content = "abcdefg
|
||||
koksi
|
||||
line3
|
||||
line4
|
||||
cinq
|
||||
line6
|
||||
line7
|
||||
line8
|
||||
line9
|
||||
line10
|
||||
e-lä-vän
|
||||
tolv
|
||||
dreizehn
|
||||
";
|
||||
let file = cm.new_filemap_and_lines("dummy.txt", None, content);
|
||||
let start = file.lines.borrow()[10];
|
||||
let end = file.lines.borrow()[11];
|
||||
let sp = mk_sp(start, end);
|
||||
let lvl = Level::Error;
|
||||
println!("highlight_lines");
|
||||
ew.highlight_lines(&sp.into(), lvl).unwrap();
|
||||
println!("done");
|
||||
let vec = data.lock().unwrap().clone();
|
||||
let vec: &[u8] = &vec;
|
||||
let str = from_utf8(vec).unwrap();
|
||||
println!("r#\"\n{}\"#", str);
|
||||
assert_eq!(str, &r#"
|
||||
--> dummy.txt:11:1
|
||||
|>
|
||||
11 |> e-lä-vän
|
||||
|> ^
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_span_splice() {
|
||||
// Test that a `MultiSpan` containing a single span splices a substition correctly
|
||||
let cm = CodeMap::new();
|
||||
let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
|
||||
let selection = " \n ~~\n~~~\n~~~~~ \n \n";
|
||||
cm.new_filemap_and_lines("blork.rs", None, inputtext);
|
||||
let sp = span_from_selection(inputtext, selection);
|
||||
let msp: MultiSpan = sp.into();
|
||||
|
||||
// check that we are extracting the text we thought we were extracting
|
||||
assert_eq!(&cm.span_to_snippet(sp).unwrap(), "BB\nCCC\nDDDDD");
|
||||
|
||||
let substitute = "ZZZZZZ".to_owned();
|
||||
let expected = "bbbbZZZZZZddddd";
|
||||
let suggest = CodeSuggestion {
|
||||
msp: msp,
|
||||
substitutes: vec![substitute],
|
||||
};
|
||||
assert_eq!(suggest.splice_lines(&cm), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_span_splice() {
|
||||
// Test that a `MultiSpan` containing multiple spans splices a substition correctly
|
||||
let cm = CodeMap::new();
|
||||
let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
|
||||
let selection1 = " \n \n \n \n ~ \n"; // intentionally out of order
|
||||
let selection2 = " \n ~~\n~~~\n~~~~~ \n \n";
|
||||
cm.new_filemap_and_lines("blork.rs", None, inputtext);
|
||||
let sp1 = span_from_selection(inputtext, selection1);
|
||||
let sp2 = span_from_selection(inputtext, selection2);
|
||||
let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]);
|
||||
|
||||
let expected = "bbbbZZZZZZddddd\neXYZe";
|
||||
let suggest = CodeSuggestion {
|
||||
msp: msp,
|
||||
substitutes: vec!["ZZZZZZ".to_owned(),
|
||||
"XYZ".to_owned()]
|
||||
};
|
||||
|
||||
assert_eq!(suggest.splice_lines(&cm), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multispan_highlight() {
|
||||
let data = Arc::new(Mutex::new(Vec::new()));
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())),
|
||||
None,
|
||||
cm.clone(),
|
||||
FormatMode::NewErrorFormat);
|
||||
|
||||
let inp = "_____aaaaaa____bbbbbb__cccccdd_";
|
||||
let sp1 = " ~~~~~~ ";
|
||||
let sp2 = " ~~~~~~ ";
|
||||
let sp3 = " ~~~~~ ";
|
||||
let sp4 = " ~~~~ ";
|
||||
let sp34 = " ~~~~~~~ ";
|
||||
|
||||
let expect_start = &r#"
|
||||
--> dummy.txt:1:6
|
||||
|>
|
||||
1 |> _____aaaaaa____bbbbbb__cccccdd_
|
||||
|> ^^^^^^ ^^^^^^ ^^^^^^^
|
||||
"#[1..];
|
||||
|
||||
let span = |sp, expected| {
|
||||
let sp = span_from_selection(inp, sp);
|
||||
assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected);
|
||||
sp
|
||||
};
|
||||
cm.new_filemap_and_lines("dummy.txt", None, inp);
|
||||
let sp1 = span(sp1, "aaaaaa");
|
||||
let sp2 = span(sp2, "bbbbbb");
|
||||
let sp3 = span(sp3, "ccccc");
|
||||
let sp4 = span(sp4, "ccdd");
|
||||
let sp34 = span(sp34, "cccccdd");
|
||||
|
||||
let spans = vec![sp1, sp2, sp3, sp4];
|
||||
|
||||
let test = |expected, highlight: &mut FnMut()| {
|
||||
data.lock().unwrap().clear();
|
||||
highlight();
|
||||
let vec = data.lock().unwrap().clone();
|
||||
let actual = from_utf8(&vec[..]).unwrap();
|
||||
println!("actual=\n{}", actual);
|
||||
assert_eq!(actual, expected);
|
||||
};
|
||||
|
||||
let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]);
|
||||
test(expect_start, &mut || {
|
||||
diag.highlight_lines(&msp, Level::Error).unwrap();
|
||||
});
|
||||
test(expect_start, &mut || {
|
||||
let msp = MultiSpan::from_spans(spans.clone());
|
||||
diag.highlight_lines(&msp, Level::Error).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_huge_multispan_highlight() {
|
||||
let data = Arc::new(Mutex::new(Vec::new()));
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())),
|
||||
None,
|
||||
cm.clone(),
|
||||
FormatMode::NewErrorFormat);
|
||||
|
||||
let inp = "aaaaa\n\
|
||||
aaaaa\n\
|
||||
aaaaa\n\
|
||||
bbbbb\n\
|
||||
ccccc\n\
|
||||
xxxxx\n\
|
||||
yyyyy\n\
|
||||
_____\n\
|
||||
ddd__eee_\n\
|
||||
elided\n\
|
||||
__f_gg";
|
||||
let file = cm.new_filemap_and_lines("dummy.txt", None, inp);
|
||||
|
||||
let span = |lo, hi, (off_lo, off_hi)| {
|
||||
let lines = file.lines.borrow();
|
||||
let (mut lo, mut hi): (BytePos, BytePos) = (lines[lo], lines[hi]);
|
||||
lo.0 += off_lo;
|
||||
hi.0 += off_hi;
|
||||
mk_sp(lo, hi)
|
||||
};
|
||||
let sp0 = span(4, 6, (0, 5));
|
||||
let sp1 = span(0, 6, (0, 5));
|
||||
let sp2 = span(8, 8, (0, 3));
|
||||
let sp3 = span(8, 8, (5, 8));
|
||||
let sp4 = span(10, 10, (2, 3));
|
||||
let sp5 = span(10, 10, (4, 6));
|
||||
|
||||
let expect0 = &r#"
|
||||
--> dummy.txt:5:1
|
||||
|>
|
||||
5 |> ccccc
|
||||
|> ^
|
||||
...
|
||||
9 |> ddd__eee_
|
||||
|> ^^^ ^^^
|
||||
10 |> elided
|
||||
11 |> __f_gg
|
||||
|> ^ ^^
|
||||
"#[1..];
|
||||
|
||||
let expect = &r#"
|
||||
--> dummy.txt:1:1
|
||||
|>
|
||||
1 |> aaaaa
|
||||
|> ^
|
||||
...
|
||||
9 |> ddd__eee_
|
||||
|> ^^^ ^^^
|
||||
10 |> elided
|
||||
11 |> __f_gg
|
||||
|> ^ ^^
|
||||
"#[1..];
|
||||
|
||||
macro_rules! test {
|
||||
($expected: expr, $highlight: expr) => ({
|
||||
data.lock().unwrap().clear();
|
||||
$highlight();
|
||||
let vec = data.lock().unwrap().clone();
|
||||
let actual = from_utf8(&vec[..]).unwrap();
|
||||
println!("actual:");
|
||||
println!("{}", actual);
|
||||
println!("expected:");
|
||||
println!("{}", $expected);
|
||||
assert_eq!(&actual[..], &$expected[..]);
|
||||
});
|
||||
}
|
||||
|
||||
let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]);
|
||||
let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]);
|
||||
|
||||
test!(expect0, || {
|
||||
diag.highlight_lines(&msp0, Level::Error).unwrap();
|
||||
});
|
||||
test!(expect, || {
|
||||
diag.highlight_lines(&msp, Level::Error).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tab() {
|
||||
let file_text = "
|
||||
fn foo() {
|
||||
\tbar;
|
||||
}
|
||||
";
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span_bar = cm.span_substr(&foo, file_text, "bar", 0);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, Some(span_bar), FormatMode::NewErrorFormat);
|
||||
snippet.push(span_bar, true, None);
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
let text = make_string(&lines);
|
||||
assert_eq!(&text[..], &"
|
||||
--> foo.rs:3:2
|
||||
|>
|
||||
3 |> \tbar;
|
||||
|> \t^^^
|
||||
"[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_line() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
vec.push(vec.pop().unwrap());
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
|
||||
let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
|
||||
let span_semi = cm.span_substr(&foo, file_text, ";", 0);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
|
||||
snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
|
||||
snippet.push(span_vec1, false, Some(format!("error occurs here")));
|
||||
snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
|
||||
let text: String = make_string(&lines);
|
||||
|
||||
println!("text=\n{}", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> vec.push(vec.pop().unwrap());
|
||||
|> --- --- - previous borrow ends here
|
||||
|> | |
|
||||
|> | error occurs here
|
||||
|> previous borrow of `vec` occurs here
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_files() {
|
||||
let file_text_foo = r#"
|
||||
fn foo() {
|
||||
vec.push(vec.pop().unwrap());
|
||||
}
|
||||
"#;
|
||||
|
||||
let file_text_bar = r#"
|
||||
fn bar() {
|
||||
// these blank links here
|
||||
// serve to ensure that the line numbers
|
||||
// from bar.rs
|
||||
// require more digits
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
vec.push();
|
||||
|
||||
// this line will get elided
|
||||
|
||||
vec.pop().unwrap());
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo_map = cm.new_filemap_and_lines("foo.rs", None, file_text_foo);
|
||||
let span_foo_vec0 = cm.span_substr(&foo_map, file_text_foo, "vec", 0);
|
||||
let span_foo_vec1 = cm.span_substr(&foo_map, file_text_foo, "vec", 1);
|
||||
let span_foo_semi = cm.span_substr(&foo_map, file_text_foo, ";", 0);
|
||||
|
||||
let bar_map = cm.new_filemap_and_lines("bar.rs", None, file_text_bar);
|
||||
let span_bar_vec0 = cm.span_substr(&bar_map, file_text_bar, "vec", 0);
|
||||
let span_bar_vec1 = cm.span_substr(&bar_map, file_text_bar, "vec", 1);
|
||||
let span_bar_semi = cm.span_substr(&bar_map, file_text_bar, ";", 0);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, Some(span_foo_vec1), FormatMode::NewErrorFormat);
|
||||
snippet.push(span_foo_vec0, false, Some(format!("a")));
|
||||
snippet.push(span_foo_vec1, true, Some(format!("b")));
|
||||
snippet.push(span_foo_semi, false, Some(format!("c")));
|
||||
snippet.push(span_bar_vec0, false, Some(format!("d")));
|
||||
snippet.push(span_bar_vec1, false, Some(format!("e")));
|
||||
snippet.push(span_bar_semi, false, Some(format!("f")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
|
||||
let text: String = make_string(&lines);
|
||||
|
||||
println!("text=\n{}", text);
|
||||
|
||||
// Note that the `|>` remain aligned across both files:
|
||||
assert_eq!(&text[..], &r#"
|
||||
--> foo.rs:3:14
|
||||
|>
|
||||
3 |> vec.push(vec.pop().unwrap());
|
||||
|> --- ^^^ - c
|
||||
|> | |
|
||||
|> | b
|
||||
|> a
|
||||
::: bar.rs
|
||||
|>
|
||||
17 |> vec.push();
|
||||
|> --- - f
|
||||
|> |
|
||||
|> d
|
||||
...
|
||||
21 |> vec.pop().unwrap());
|
||||
|> --- e
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_line() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
let name = find_id(&data, 22).unwrap();
|
||||
|
||||
// Add one more item we forgot to the vector. Silly us.
|
||||
data.push(Data { name: format!("Hera"), id: 66 });
|
||||
|
||||
// Print everything out.
|
||||
println!("Name: {:?}", name);
|
||||
println!("Data: {:?}", data);
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span_data0 = cm.span_substr(&foo, file_text, "data", 0);
|
||||
let span_data1 = cm.span_substr(&foo, file_text, "data", 1);
|
||||
let span_rbrace = cm.span_substr(&foo, file_text, "}", 3);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
|
||||
snippet.push(span_data0, false, Some(format!("immutable borrow begins here")));
|
||||
snippet.push(span_data1, false, Some(format!("mutable borrow occurs here")));
|
||||
snippet.push(span_rbrace, false, Some(format!("immutable borrow ends here")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
|
||||
let text: String = make_string(&lines);
|
||||
|
||||
println!("text=\n{}", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> let name = find_id(&data, 22).unwrap();
|
||||
|> ---- immutable borrow begins here
|
||||
...
|
||||
6 |> data.push(Data { name: format!("Hera"), id: 66 });
|
||||
|> ---- mutable borrow occurs here
|
||||
...
|
||||
11 |> }
|
||||
|> - immutable borrow ends here
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlapping() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
vec.push(vec.pop().unwrap());
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span0 = cm.span_substr(&foo, file_text, "vec.push", 0);
|
||||
let span1 = cm.span_substr(&foo, file_text, "vec", 0);
|
||||
let span2 = cm.span_substr(&foo, file_text, "ec.push", 0);
|
||||
let span3 = cm.span_substr(&foo, file_text, "unwrap", 0);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
|
||||
snippet.push(span0, false, Some(format!("A")));
|
||||
snippet.push(span1, false, Some(format!("B")));
|
||||
snippet.push(span2, false, Some(format!("C")));
|
||||
snippet.push(span3, false, Some(format!("D")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
let text: String = make_string(&lines);
|
||||
|
||||
println!("text=r#\"\n{}\".trim_left()", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> vec.push(vec.pop().unwrap());
|
||||
|> -------- ------ D
|
||||
|> ||
|
||||
|> |C
|
||||
|> A
|
||||
|> B
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_line_out_of_order() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
vec.push(vec.pop().unwrap());
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
|
||||
let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
|
||||
let span_semi = cm.span_substr(&foo, file_text, ";", 0);
|
||||
|
||||
// intentionally don't push the snippets left to right
|
||||
let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
|
||||
snippet.push(span_vec1, false, Some(format!("error occurs here")));
|
||||
snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
|
||||
snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
let text: String = make_string(&lines);
|
||||
|
||||
println!("text=r#\"\n{}\".trim_left()", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> vec.push(vec.pop().unwrap());
|
||||
|> --- --- - previous borrow ends here
|
||||
|> | |
|
||||
|> | error occurs here
|
||||
|> previous borrow of `vec` occurs here
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn elide_unnecessary_lines() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
let mut vec = vec![0, 1, 2];
|
||||
let mut vec2 = vec;
|
||||
vec2.push(3);
|
||||
vec2.push(4);
|
||||
vec2.push(5);
|
||||
vec2.push(6);
|
||||
vec.push(7);
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
let span_vec0 = cm.span_substr(&foo, file_text, "vec", 3);
|
||||
let span_vec1 = cm.span_substr(&foo, file_text, "vec", 8);
|
||||
|
||||
let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
|
||||
snippet.push(span_vec0, false, Some(format!("`vec` moved here because it \
|
||||
has type `collections::vec::Vec<i32>`")));
|
||||
snippet.push(span_vec1, false, Some(format!("use of moved value: `vec`")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
println!("{:#?}", lines);
|
||||
let text: String = make_string(&lines);
|
||||
println!("text=r#\"\n{}\".trim_left()", text);
|
||||
assert_eq!(&text[..], &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
4 |> let mut vec2 = vec;
|
||||
|> --- `vec` moved here because it has type `collections::vec::Vec<i32>`
|
||||
...
|
||||
9 |> vec.push(7);
|
||||
|> --- use of moved value: `vec`
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spans_without_labels() {
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
let mut vec = vec![0, 1, 2];
|
||||
let mut vec2 = vec;
|
||||
vec2.push(3);
|
||||
vec2.push(4);
|
||||
vec2.push(5);
|
||||
vec2.push(6);
|
||||
vec.push(7);
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
|
||||
for i in 0..4 {
|
||||
let span_veci = cm.span_substr(&foo, file_text, "vec", i);
|
||||
snippet.push(span_veci, false, None);
|
||||
}
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
println!("text=&r#\"\n{}\n\"#[1..]", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> let mut vec = vec![0, 1, 2];
|
||||
|> --- ---
|
||||
4 |> let mut vec2 = vec;
|
||||
|> --- ---
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_long_selection() {
|
||||
let file_text = r#"
|
||||
impl SomeTrait for () {
|
||||
fn foo(x: u32) {
|
||||
// impl 1
|
||||
// impl 2
|
||||
// impl 3
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
|
||||
let fn_span = cm.span_substr(&foo, file_text, "fn", 0);
|
||||
let rbrace_span = cm.span_substr(&foo, file_text, "}", 0);
|
||||
snippet.push(splice(fn_span, rbrace_span), false, None);
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> fn foo(x: u32) {
|
||||
|> -
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_overlap_label() {
|
||||
// Test that we don't put `x_span` to the right of its highlight,
|
||||
// since there is another highlight that overlaps it.
|
||||
|
||||
let file_text = r#"
|
||||
fn foo(x: u32) {
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
|
||||
let fn_span = cm.span_substr(&foo, file_text, "fn foo(x: u32)", 0);
|
||||
let x_span = cm.span_substr(&foo, file_text, "x", 0);
|
||||
snippet.push(fn_span, false, Some(format!("fn_span")));
|
||||
snippet.push(x_span, false, Some(format!("x_span")));
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
2 |> fn foo(x: u32) {
|
||||
|> --------------
|
||||
|> | |
|
||||
|> | x_span
|
||||
|> fn_span
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_overlap_label2() {
|
||||
// Test that we don't put `x_span` to the right of its highlight,
|
||||
// since there is another highlight that overlaps it. In this
|
||||
// case, the overlap is only at the beginning, but it's still
|
||||
// better to show the beginning more clearly.
|
||||
|
||||
let file_text = r#"
|
||||
fn foo(x: u32) {
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
|
||||
let fn_span = cm.span_substr(&foo, file_text, "fn foo(x", 0);
|
||||
let x_span = cm.span_substr(&foo, file_text, "x: u32)", 0);
|
||||
snippet.push(fn_span, false, Some(format!("fn_span")));
|
||||
snippet.push(x_span, false, Some(format!("x_span")));
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
2 |> fn foo(x: u32) {
|
||||
|> --------------
|
||||
|> | |
|
||||
|> | x_span
|
||||
|> fn_span
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_overlap_label3() {
|
||||
// Test that we don't put `x_span` to the right of its highlight,
|
||||
// since there is another highlight that overlaps it. In this
|
||||
// case, the overlap is only at the beginning, but it's still
|
||||
// better to show the beginning more clearly.
|
||||
|
||||
let file_text = r#"
|
||||
fn foo() {
|
||||
let closure = || {
|
||||
inner
|
||||
};
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
|
||||
|
||||
let closure_span = {
|
||||
let closure_start_span = cm.span_substr(&foo, file_text, "||", 0);
|
||||
let closure_end_span = cm.span_substr(&foo, file_text, "}", 0);
|
||||
splice(closure_start_span, closure_end_span)
|
||||
};
|
||||
|
||||
let inner_span = cm.span_substr(&foo, file_text, "inner", 0);
|
||||
|
||||
snippet.push(closure_span, false, Some(format!("foo")));
|
||||
snippet.push(inner_span, false, Some(format!("bar")));
|
||||
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
::: foo.rs
|
||||
|>
|
||||
3 |> let closure = || {
|
||||
|> - foo
|
||||
4 |> inner
|
||||
|> ----- bar
|
||||
"#[1..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_empty() {
|
||||
// In one of the unit tests, we found that the parser sometimes
|
||||
// gives empty spans, and in particular it supplied an EOF span
|
||||
// like this one, which points at the very end. We want to
|
||||
// fallback gracefully in this case.
|
||||
|
||||
let file_text = r#"
|
||||
fn main() {
|
||||
struct Foo;
|
||||
|
||||
impl !Sync for Foo {}
|
||||
|
||||
unsafe impl Send for &'static Foo {
|
||||
// error: cross-crate traits with a default impl, like `core::marker::Send`,
|
||||
// can only be implemented for a struct/enum type, not
|
||||
// `&'static Foo`
|
||||
}"#;
|
||||
|
||||
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
|
||||
|
||||
let mut rbrace_span = cm.span_substr(&foo, file_text, "}", 1);
|
||||
rbrace_span.lo = rbrace_span.hi;
|
||||
|
||||
let mut snippet = SnippetData::new(cm.clone(),
|
||||
Some(rbrace_span),
|
||||
FormatMode::NewErrorFormat);
|
||||
snippet.push(rbrace_span, false, None);
|
||||
let lines = snippet.render_lines();
|
||||
let text: String = make_string(&lines);
|
||||
println!("r#\"\n{}\"", text);
|
||||
assert_eq!(text, &r#"
|
||||
--> foo.rs:11:2
|
||||
|>
|
||||
11 |> }
|
||||
|> -
|
||||
"#[1..]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -816,6 +816,12 @@ impl<'a> ExtCtxt<'a> {
|
|||
/// compilation on error, merely emits a non-fatal error and returns None.
|
||||
pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
|
||||
-> Option<(InternedString, ast::StrStyle)> {
|
||||
// Update `expr.span`'s expn_id now in case expr is an `include!` macro invocation.
|
||||
let expr = expr.map(|mut expr| {
|
||||
expr.span.expn_id = cx.backtrace;
|
||||
expr
|
||||
});
|
||||
|
||||
// we want to be able to handle e.g. concat("foo", "bar")
|
||||
let expr = cx.expander().fold_expr(expr);
|
||||
match expr.node {
|
||||
|
|
|
|||
|
|
@ -9,20 +9,19 @@
|
|||
// except according to those terms.
|
||||
|
||||
use ast::{Block, Crate, Ident, Mac_, Name, PatKind};
|
||||
use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
|
||||
use ast::{MacStmtStyle, Stmt, StmtKind, ItemKind};
|
||||
use ast;
|
||||
use attr::HasAttrs;
|
||||
use ext::mtwt;
|
||||
use attr;
|
||||
use ext::hygiene::Mark;
|
||||
use attr::{self, HasAttrs};
|
||||
use attr::AttrMetaMethods;
|
||||
use codemap::{dummy_spanned, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
|
||||
use codemap::{dummy_spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
|
||||
use syntax_pos::{self, Span, ExpnId};
|
||||
use config::StripUnconfigured;
|
||||
use ext::base::*;
|
||||
use feature_gate::{self, Features};
|
||||
use fold;
|
||||
use fold::*;
|
||||
use parse::token::{fresh_mark, intern, keywords};
|
||||
use parse::token::{intern, keywords};
|
||||
use ptr::P;
|
||||
use tokenstream::TokenTree;
|
||||
use util::small_vector::SmallVector;
|
||||
|
|
@ -130,9 +129,9 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
|
|||
// It would almost certainly be cleaner to pass the whole macro invocation in,
|
||||
// rather than pulling it apart and marking the tts and the ctxt separately.
|
||||
let Mac_ { path, tts, .. } = mac.node;
|
||||
let mark = fresh_mark();
|
||||
let mark = Mark::fresh();
|
||||
|
||||
fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mrk,
|
||||
fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mark,
|
||||
attrs: Vec<ast::Attribute>, call_site: Span, fld: &'a mut MacroExpander)
|
||||
-> Option<Box<MacResult + 'a>> {
|
||||
// Detect use of feature-gated or invalid attributes on macro invoations
|
||||
|
|
@ -708,30 +707,17 @@ pub fn expand_crate(mut cx: ExtCtxt,
|
|||
return (ret, cx.syntax_env.names);
|
||||
}
|
||||
|
||||
// HYGIENIC CONTEXT EXTENSION:
|
||||
// all of these functions are for walking over
|
||||
// ASTs and making some change to the context of every
|
||||
// element that has one. a CtxtFn is a trait-ified
|
||||
// version of a closure in (SyntaxContext -> SyntaxContext).
|
||||
// the ones defined here include:
|
||||
// Marker - add a mark to a context
|
||||
|
||||
// A Marker adds the given mark to the syntax context and
|
||||
// sets spans' `expn_id` to the given expn_id (unless it is `None`).
|
||||
struct Marker { mark: Mrk, expn_id: Option<ExpnId> }
|
||||
struct Marker { mark: Mark, expn_id: Option<ExpnId> }
|
||||
|
||||
impl Folder for Marker {
|
||||
fn fold_ident(&mut self, id: Ident) -> Ident {
|
||||
ast::Ident::new(id.name, mtwt::apply_mark(self.mark, id.ctxt))
|
||||
fn fold_ident(&mut self, mut ident: Ident) -> Ident {
|
||||
ident.ctxt = ident.ctxt.apply_mark(self.mark);
|
||||
ident
|
||||
}
|
||||
fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac {
|
||||
Spanned {
|
||||
node: Mac_ {
|
||||
path: self.fold_path(node.path),
|
||||
tts: self.fold_tts(&node.tts),
|
||||
},
|
||||
span: self.new_span(span),
|
||||
}
|
||||
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
||||
noop_fold_mac(mac, self)
|
||||
}
|
||||
|
||||
fn new_span(&mut self, mut span: Span) -> Span {
|
||||
|
|
@ -743,7 +729,7 @@ impl Folder for Marker {
|
|||
}
|
||||
|
||||
// apply a given mark to the given token trees. Used prior to expansion of a macro.
|
||||
fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
|
||||
fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec<TokenTree> {
|
||||
noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
|
||||
}
|
||||
|
||||
|
|
|
|||
116
src/libsyntax/ext/hygiene.rs
Normal file
116
src/libsyntax/ext/hygiene.rs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Machinery for hygienic macros, inspired by the MTWT[1] paper.
|
||||
//!
|
||||
//! [1] Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler.
|
||||
//! 2012. *Macros that work together: Compile-time bindings, partial expansion,
|
||||
//! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
|
||||
//! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
/// A SyntaxContext represents a chain of macro expansions (represented by marks).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Default)]
|
||||
pub struct SyntaxContext(u32);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SyntaxContextData {
|
||||
pub outer_mark: Mark,
|
||||
pub prev_ctxt: SyntaxContext,
|
||||
}
|
||||
|
||||
/// A mark represents a unique id associated with a macro expansion.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
|
||||
pub struct Mark(u32);
|
||||
|
||||
impl Mark {
|
||||
pub fn fresh() -> Self {
|
||||
HygieneData::with(|data| {
|
||||
let next_mark = Mark(data.next_mark.0 + 1);
|
||||
::std::mem::replace(&mut data.next_mark, next_mark)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct HygieneData {
|
||||
syntax_contexts: Vec<SyntaxContextData>,
|
||||
markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
|
||||
next_mark: Mark,
|
||||
}
|
||||
|
||||
impl HygieneData {
|
||||
fn new() -> Self {
|
||||
HygieneData {
|
||||
syntax_contexts: vec![SyntaxContextData {
|
||||
outer_mark: Mark(0), // the null mark
|
||||
prev_ctxt: SyntaxContext(0), // the empty context
|
||||
}],
|
||||
markings: HashMap::new(),
|
||||
next_mark: Mark(1),
|
||||
}
|
||||
}
|
||||
|
||||
fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
|
||||
thread_local! {
|
||||
static HYGIENE_DATA: RefCell<HygieneData> = RefCell::new(HygieneData::new());
|
||||
}
|
||||
HYGIENE_DATA.with(|data| f(&mut *data.borrow_mut()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_hygiene_data() {
|
||||
HygieneData::with(|data| *data = HygieneData::new())
|
||||
}
|
||||
|
||||
impl SyntaxContext {
|
||||
pub const fn empty() -> Self {
|
||||
SyntaxContext(0)
|
||||
}
|
||||
|
||||
pub fn data(self) -> SyntaxContextData {
|
||||
HygieneData::with(|data| data.syntax_contexts[self.0 as usize])
|
||||
}
|
||||
|
||||
/// Extend a syntax context with a given mark
|
||||
pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
|
||||
// Applying the same mark twice is a no-op
|
||||
let ctxt_data = self.data();
|
||||
if mark == ctxt_data.outer_mark {
|
||||
return ctxt_data.prev_ctxt;
|
||||
}
|
||||
|
||||
HygieneData::with(|data| {
|
||||
let syntax_contexts = &mut data.syntax_contexts;
|
||||
*data.markings.entry((self, mark)).or_insert_with(|| {
|
||||
syntax_contexts.push(SyntaxContextData {
|
||||
outer_mark: mark,
|
||||
prev_ctxt: self,
|
||||
});
|
||||
SyntaxContext(syntax_contexts.len() as u32 - 1)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// If `ident` is macro expanded, return the source ident from the macro definition
|
||||
/// and the mark of the expansion that created the macro definition.
|
||||
pub fn source(self) -> (Self /* source context */, Mark /* source macro */) {
|
||||
let macro_def_ctxt = self.data().prev_ctxt.data();
|
||||
(macro_def_ctxt.prev_ctxt, macro_def_ctxt.outer_mark)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SyntaxContext {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "#{}", self.0)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Machinery for hygienic macros, as described in the MTWT[1] paper.
|
||||
//!
|
||||
//! [1] Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler.
|
||||
//! 2012. *Macros that work together: Compile-time bindings, partial expansion,
|
||||
//! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
|
||||
//! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093
|
||||
|
||||
pub use self::SyntaxContext_::*;
|
||||
|
||||
use ast::{Ident, Mrk, SyntaxContext};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// The SCTable contains a table of SyntaxContext_'s. It
|
||||
/// represents a flattened tree structure, to avoid having
|
||||
/// managed pointers everywhere (that caused an ICE).
|
||||
/// The `marks` ensures that adding the same mark to the
|
||||
/// same context gives you back the same context as before.
|
||||
pub struct SCTable {
|
||||
table: RefCell<Vec<SyntaxContext_>>,
|
||||
marks: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)]
|
||||
pub enum SyntaxContext_ {
|
||||
EmptyCtxt,
|
||||
Mark (Mrk,SyntaxContext),
|
||||
}
|
||||
|
||||
/// Extend a syntax context with a given mark
|
||||
pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext {
|
||||
with_sctable(|table| apply_mark_internal(m, ctxt, table))
|
||||
}
|
||||
|
||||
/// Extend a syntax context with a given mark and sctable (explicit memoization)
|
||||
fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext {
|
||||
let ctxts = &mut *table.table.borrow_mut();
|
||||
match ctxts[ctxt.0 as usize] {
|
||||
// Applying the same mark twice is a no-op.
|
||||
Mark(outer_mark, prev_ctxt) if outer_mark == m => return prev_ctxt,
|
||||
_ => *table.marks.borrow_mut().entry((ctxt, m)).or_insert_with(|| {
|
||||
SyntaxContext(idx_push(ctxts, Mark(m, ctxt)))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the SCTable from TLS, create one if it doesn't yet exist.
|
||||
pub fn with_sctable<T, F>(op: F) -> T where
|
||||
F: FnOnce(&SCTable) -> T,
|
||||
{
|
||||
thread_local!(static SCTABLE_KEY: SCTable = new_sctable_internal());
|
||||
SCTABLE_KEY.with(move |slot| op(slot))
|
||||
}
|
||||
|
||||
// Make a fresh syntax context table with EmptyCtxt in slot zero.
|
||||
fn new_sctable_internal() -> SCTable {
|
||||
SCTable {
|
||||
table: RefCell::new(vec![EmptyCtxt]),
|
||||
marks: RefCell::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the tables from TLD to reclaim memory.
|
||||
pub fn clear_tables() {
|
||||
with_sctable(|table| {
|
||||
*table.table.borrow_mut() = Vec::new();
|
||||
*table.marks.borrow_mut() = HashMap::new();
|
||||
});
|
||||
}
|
||||
|
||||
/// Reset the tables to their initial state
|
||||
pub fn reset_tables() {
|
||||
with_sctable(|table| {
|
||||
*table.table.borrow_mut() = vec![EmptyCtxt];
|
||||
*table.marks.borrow_mut() = HashMap::new();
|
||||
});
|
||||
}
|
||||
|
||||
/// Add a value to the end of a vec, return its index
|
||||
fn idx_push<T>(vec: &mut Vec<T>, val: T) -> u32 {
|
||||
vec.push(val);
|
||||
(vec.len() - 1) as u32
|
||||
}
|
||||
|
||||
/// Return the outer mark for a context with a mark at the outside.
|
||||
/// FAILS when outside is not a mark.
|
||||
pub fn outer_mark(ctxt: SyntaxContext) -> Mrk {
|
||||
with_sctable(|sctable| {
|
||||
match (*sctable.table.borrow())[ctxt.0 as usize] {
|
||||
Mark(mrk, _) => mrk,
|
||||
_ => panic!("can't retrieve outer mark when outside is not a mark")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// If `ident` is macro expanded, return the source ident from the macro definition
|
||||
/// and the mark of the expansion that created the macro definition.
|
||||
pub fn source(ident: Ident) -> Option<(Ident /* source ident */, Mrk /* source macro */)> {
|
||||
with_sctable(|sctable| {
|
||||
let ctxts = sctable.table.borrow();
|
||||
if let Mark(_expansion_mark, macro_ctxt) = ctxts[ident.ctxt.0 as usize] {
|
||||
if let Mark(definition_mark, orig_ctxt) = ctxts[macro_ctxt.0 as usize] {
|
||||
return Some((Ident::new(ident.name, orig_ctxt), definition_mark));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ast::{EMPTY_CTXT, Mrk, SyntaxContext};
|
||||
use super::{apply_mark_internal, new_sctable_internal, Mark, SCTable};
|
||||
|
||||
// extend a syntax context with a sequence of marks given
|
||||
// in a vector. v[0] will be the outermost mark.
|
||||
fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
|
||||
-> SyntaxContext {
|
||||
mrks.iter().rev().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
|
||||
{apply_mark_internal(*mrk,tail,table)})
|
||||
}
|
||||
|
||||
#[test] fn unfold_marks_test() {
|
||||
let mut t = new_sctable_internal();
|
||||
|
||||
assert_eq!(unfold_marks(vec!(3,7),EMPTY_CTXT,&mut t),SyntaxContext(2));
|
||||
{
|
||||
let table = t.table.borrow();
|
||||
assert!((*table)[1] == Mark(7,EMPTY_CTXT));
|
||||
assert!((*table)[2] == Mark(3,SyntaxContext(1)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hashing_tests () {
|
||||
let mut t = new_sctable_internal();
|
||||
assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(1));
|
||||
assert_eq!(apply_mark_internal(13,EMPTY_CTXT,&mut t),SyntaxContext(2));
|
||||
// using the same one again should result in the same index:
|
||||
assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(1));
|
||||
// I'm assuming that the rename table will behave the same....
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
use codemap::CodeMap;
|
||||
use syntax_pos::{self, MacroBacktrace, Span, SpanLabel, MultiSpan};
|
||||
use errors::registry::Registry;
|
||||
use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper};
|
||||
use errors::{DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper};
|
||||
use errors::emitter::Emitter;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
|
@ -53,14 +53,7 @@ impl JsonEmitter {
|
|||
}
|
||||
|
||||
impl Emitter for JsonEmitter {
|
||||
fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, level: Level) {
|
||||
let data = Diagnostic::new(span, msg, code, level, self);
|
||||
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
|
||||
panic!("failed to print diagnostics: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_struct(&mut self, db: &DiagnosticBuilder) {
|
||||
fn emit(&mut self, db: &DiagnosticBuilder) {
|
||||
let data = Diagnostic::from_diagnostic_builder(db, self);
|
||||
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
|
||||
panic!("failed to print diagnostics: {:?}", e);
|
||||
|
|
@ -146,22 +139,6 @@ struct DiagnosticCode {
|
|||
}
|
||||
|
||||
impl<'a> Diagnostic<'a> {
|
||||
fn new(msp: &MultiSpan,
|
||||
msg: &'a str,
|
||||
code: Option<&str>,
|
||||
level: Level,
|
||||
je: &JsonEmitter)
|
||||
-> Diagnostic<'a> {
|
||||
Diagnostic {
|
||||
message: msg,
|
||||
code: DiagnosticCode::map_opt_string(code.map(|c| c.to_owned()), je),
|
||||
level: level.to_str(),
|
||||
spans: DiagnosticSpan::from_multispan(msp, je),
|
||||
children: vec![],
|
||||
rendered: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder,
|
||||
je: &JsonEmitter)
|
||||
-> Diagnostic<'c> {
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ pub mod ext {
|
|||
pub mod base;
|
||||
pub mod build;
|
||||
pub mod expand;
|
||||
pub mod mtwt;
|
||||
pub mod hygiene;
|
||||
pub mod quote;
|
||||
pub mod source_util;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,23 +18,43 @@ use parse::token;
|
|||
use parse::parser::{Parser, TokenType};
|
||||
use ptr::P;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
enum InnerAttributeParsePolicy<'a> {
|
||||
Permitted,
|
||||
NotPermitted { reason: &'a str },
|
||||
}
|
||||
|
||||
const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &'static str = "an inner attribute is not \
|
||||
permitted in this context";
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
/// Parse attributes that appear before an item
|
||||
pub fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
|
||||
let mut attrs: Vec<ast::Attribute> = Vec::new();
|
||||
let mut just_parsed_doc_comment = false;
|
||||
loop {
|
||||
debug!("parse_outer_attributes: self.token={:?}", self.token);
|
||||
match self.token {
|
||||
token::Pound => {
|
||||
attrs.push(self.parse_attribute(false)?);
|
||||
let inner_error_reason = if just_parsed_doc_comment {
|
||||
"an inner attribute is not permitted following an outer doc comment"
|
||||
} else if !attrs.is_empty() {
|
||||
"an inner attribute is not permitted following an outer attribute"
|
||||
} else {
|
||||
DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
|
||||
};
|
||||
let inner_parse_policy =
|
||||
InnerAttributeParsePolicy::NotPermitted { reason: inner_error_reason };
|
||||
attrs.push(self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?);
|
||||
just_parsed_doc_comment = false;
|
||||
}
|
||||
token::DocComment(s) => {
|
||||
let attr = ::attr::mk_sugared_doc_attr(
|
||||
attr::mk_attr_id(),
|
||||
self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)),
|
||||
self.span.lo,
|
||||
self.span.hi
|
||||
);
|
||||
attr::mk_attr_id(),
|
||||
self.id_to_interned_str(ast::Ident::with_empty_ctxt(s)),
|
||||
self.span.lo,
|
||||
self.span.hi
|
||||
);
|
||||
if attr.node.style != ast::AttrStyle::Outer {
|
||||
let mut err = self.fatal("expected outer doc comment");
|
||||
err.note("inner doc comments like this (starting with \
|
||||
|
|
@ -43,6 +63,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
attrs.push(attr);
|
||||
self.bump();
|
||||
just_parsed_doc_comment = true;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
|
|
@ -55,26 +76,46 @@ impl<'a> Parser<'a> {
|
|||
/// If permit_inner is true, then a leading `!` indicates an inner
|
||||
/// attribute
|
||||
pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
|
||||
debug!("parse_attributes: permit_inner={:?} self.token={:?}",
|
||||
debug!("parse_attribute: permit_inner={:?} self.token={:?}",
|
||||
permit_inner,
|
||||
self.token);
|
||||
let inner_parse_policy = if permit_inner {
|
||||
InnerAttributeParsePolicy::Permitted
|
||||
} else {
|
||||
InnerAttributeParsePolicy::NotPermitted
|
||||
{ reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG }
|
||||
};
|
||||
self.parse_attribute_with_inner_parse_policy(inner_parse_policy)
|
||||
}
|
||||
|
||||
/// The same as `parse_attribute`, except it takes in an `InnerAttributeParsePolicy`
|
||||
/// that prescribes how to handle inner attributes.
|
||||
fn parse_attribute_with_inner_parse_policy(&mut self,
|
||||
inner_parse_policy: InnerAttributeParsePolicy)
|
||||
-> PResult<'a, ast::Attribute> {
|
||||
debug!("parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}",
|
||||
inner_parse_policy,
|
||||
self.token);
|
||||
let (span, value, mut style) = match self.token {
|
||||
token::Pound => {
|
||||
let lo = self.span.lo;
|
||||
self.bump();
|
||||
|
||||
if permit_inner {
|
||||
if inner_parse_policy == InnerAttributeParsePolicy::Permitted {
|
||||
self.expected_tokens.push(TokenType::Token(token::Not));
|
||||
}
|
||||
let style = if self.token == token::Not {
|
||||
self.bump();
|
||||
if !permit_inner {
|
||||
if let InnerAttributeParsePolicy::NotPermitted { reason } = inner_parse_policy
|
||||
{
|
||||
let span = self.span;
|
||||
self.diagnostic()
|
||||
.struct_span_err(span,
|
||||
"an inner attribute is not permitted in this context")
|
||||
.help("place inner attribute at the top of the module or \
|
||||
block")
|
||||
.struct_span_err(span, reason)
|
||||
.note("inner attributes and doc comments, like `#![no_std]` or \
|
||||
`//! My crate`, annotate the item enclosing them, and are \
|
||||
usually found at the beginning of source files. Outer \
|
||||
attributes and doc comments, like `#[test]` and
|
||||
`/// My function`, annotate the item following them.")
|
||||
.emit()
|
||||
}
|
||||
ast::AttrStyle::Inner
|
||||
|
|
@ -95,7 +136,8 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
if permit_inner && self.token == token::Semi {
|
||||
if inner_parse_policy == InnerAttributeParsePolicy::Permitted &&
|
||||
self.token == token::Semi {
|
||||
self.bump();
|
||||
self.span_warn(span,
|
||||
"this inner attribute syntax is deprecated. The new syntax is \
|
||||
|
|
|
|||
|
|
@ -1686,7 +1686,7 @@ mod tests {
|
|||
// FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
|
||||
let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()),
|
||||
None,
|
||||
cm,
|
||||
Some(cm),
|
||||
errors::snippet::FormatMode::EnvironmentSelected);
|
||||
errors::Handler::with_emitter(true, false, Box::new(emitter))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,11 @@ pub struct ParseSess {
|
|||
impl ParseSess {
|
||||
pub fn new() -> ParseSess {
|
||||
let cm = Rc::new(CodeMap::new());
|
||||
let handler = Handler::with_tty_emitter(ColorConfig::Auto, None, true, false, cm.clone());
|
||||
let handler = Handler::with_tty_emitter(ColorConfig::Auto,
|
||||
None,
|
||||
true,
|
||||
false,
|
||||
Some(cm.clone()));
|
||||
ParseSess::with_span_handler(handler, cm)
|
||||
}
|
||||
|
||||
|
|
@ -224,10 +228,18 @@ pub fn filemap_to_parser<'a>(sess: &'a ParseSess,
|
|||
// compiler expands into it
|
||||
pub fn new_parser_from_tts<'a>(sess: &'a ParseSess,
|
||||
cfg: ast::CrateConfig,
|
||||
tts: Vec<tokenstream::TokenTree>) -> Parser<'a> {
|
||||
tts: Vec<tokenstream::TokenTree>)
|
||||
-> Parser<'a> {
|
||||
tts_to_parser(sess, tts, cfg)
|
||||
}
|
||||
|
||||
pub fn new_parser_from_ts<'a>(sess: &'a ParseSess,
|
||||
cfg: ast::CrateConfig,
|
||||
ts: tokenstream::TokenStream)
|
||||
-> Parser<'a> {
|
||||
tts_to_parser(sess, ts.tts, cfg)
|
||||
}
|
||||
|
||||
|
||||
// base abstractions
|
||||
|
||||
|
|
|
|||
|
|
@ -3789,13 +3789,8 @@ impl<'a> Parser<'a> {
|
|||
|
||||
/// Parse a statement. This stops just before trailing semicolons on everything but items.
|
||||
/// e.g. a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
|
||||
///
|
||||
/// Also, if a macro begins an expression statement, this only parses the macro. For example,
|
||||
/// ```rust
|
||||
/// vec![1].into_iter(); //< `parse_stmt` only parses the "vec![1]"
|
||||
/// ```
|
||||
pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
|
||||
Ok(self.parse_stmt_())
|
||||
Ok(self.parse_stmt_(true))
|
||||
}
|
||||
|
||||
// Eat tokens until we can be relatively sure we reached the end of the
|
||||
|
|
@ -3859,15 +3854,15 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_stmt_(&mut self) -> Option<Stmt> {
|
||||
self.parse_stmt_without_recovery().unwrap_or_else(|mut e| {
|
||||
fn parse_stmt_(&mut self, macro_expanded: bool) -> Option<Stmt> {
|
||||
self.parse_stmt_without_recovery(macro_expanded).unwrap_or_else(|mut e| {
|
||||
e.emit();
|
||||
self.recover_stmt_(SemiColonMode::Break);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_stmt_without_recovery(&mut self) -> PResult<'a, Option<Stmt>> {
|
||||
fn parse_stmt_without_recovery(&mut self, macro_expanded: bool) -> PResult<'a, Option<Stmt>> {
|
||||
maybe_whole!(Some deref self, NtStmt);
|
||||
|
||||
let attrs = self.parse_outer_attributes()?;
|
||||
|
|
@ -3930,10 +3925,34 @@ impl<'a> Parser<'a> {
|
|||
|
||||
if id.name == keywords::Invalid.name() {
|
||||
let mac = spanned(lo, hi, Mac_ { path: pth, tts: tts });
|
||||
let node = if delim == token::Brace ||
|
||||
self.token == token::Semi || self.token == token::Eof {
|
||||
StmtKind::Mac(P((mac, style, attrs.into())))
|
||||
}
|
||||
// We used to incorrectly stop parsing macro-expanded statements here.
|
||||
// If the next token will be an error anyway but could have parsed with the
|
||||
// earlier behavior, stop parsing here and emit a warning to avoid breakage.
|
||||
else if macro_expanded && self.token.can_begin_expr() && match self.token {
|
||||
// These can continue an expression, so we can't stop parsing and warn.
|
||||
token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) |
|
||||
token::BinOp(token::Minus) | token::BinOp(token::Star) |
|
||||
token::BinOp(token::And) | token::BinOp(token::Or) |
|
||||
token::AndAnd | token::OrOr |
|
||||
token::DotDot | token::DotDotDot => false,
|
||||
_ => true,
|
||||
} {
|
||||
self.warn_missing_semicolon();
|
||||
StmtKind::Mac(P((mac, style, attrs.into())))
|
||||
} else {
|
||||
let e = self.mk_mac_expr(lo, hi, mac.node, ThinVec::new());
|
||||
let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
|
||||
let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
|
||||
StmtKind::Expr(e)
|
||||
};
|
||||
Stmt {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: StmtKind::Mac(P((mac, style, attrs.into()))),
|
||||
span: mk_sp(lo, hi),
|
||||
node: node,
|
||||
}
|
||||
} else {
|
||||
// if it has a special ident, it's definitely an item
|
||||
|
|
@ -4061,49 +4080,12 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
/// Parse a statement, including the trailing semicolon.
|
||||
/// This parses expression statements that begin with macros correctly (c.f. `parse_stmt`).
|
||||
pub fn parse_full_stmt(&mut self, macro_expanded: bool) -> PResult<'a, Option<Stmt>> {
|
||||
let mut stmt = match self.parse_stmt_() {
|
||||
let mut stmt = match self.parse_stmt_(macro_expanded) {
|
||||
Some(stmt) => stmt,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
if let StmtKind::Mac(mac) = stmt.node {
|
||||
if mac.1 != MacStmtStyle::NoBraces ||
|
||||
self.token == token::Semi || self.token == token::Eof {
|
||||
stmt.node = StmtKind::Mac(mac);
|
||||
} else {
|
||||
// We used to incorrectly stop parsing macro-expanded statements here.
|
||||
// If the next token will be an error anyway but could have parsed with the
|
||||
// earlier behavior, stop parsing here and emit a warning to avoid breakage.
|
||||
if macro_expanded && self.token.can_begin_expr() && match self.token {
|
||||
// These tokens can continue an expression, so we can't stop parsing and warn.
|
||||
token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) |
|
||||
token::BinOp(token::Minus) | token::BinOp(token::Star) |
|
||||
token::BinOp(token::And) | token::BinOp(token::Or) |
|
||||
token::AndAnd | token::OrOr |
|
||||
token::DotDot | token::DotDotDot => false,
|
||||
_ => true,
|
||||
} {
|
||||
self.warn_missing_semicolon();
|
||||
stmt.node = StmtKind::Mac(mac);
|
||||
return Ok(Some(stmt));
|
||||
}
|
||||
|
||||
let (mac, _style, attrs) = mac.unwrap();
|
||||
let e = self.mk_mac_expr(stmt.span.lo, stmt.span.hi, mac.node, ThinVec::new());
|
||||
let e = self.parse_dot_or_call_expr_with(e, stmt.span.lo, attrs)?;
|
||||
let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
|
||||
stmt.node = StmtKind::Expr(e);
|
||||
}
|
||||
}
|
||||
|
||||
stmt = self.handle_trailing_semicolon(stmt, macro_expanded)?;
|
||||
Ok(Some(stmt))
|
||||
}
|
||||
|
||||
fn handle_trailing_semicolon(&mut self, mut stmt: Stmt, macro_expanded: bool)
|
||||
-> PResult<'a, Stmt> {
|
||||
match stmt.node {
|
||||
StmtKind::Expr(ref expr) if self.token != token::Eof => {
|
||||
// expression without semicolon
|
||||
|
|
@ -4133,7 +4115,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
stmt.span.hi = self.last_span.hi;
|
||||
Ok(stmt)
|
||||
Ok(Some(stmt))
|
||||
}
|
||||
|
||||
fn warn_missing_semicolon(&self) {
|
||||
|
|
|
|||
|
|
@ -633,8 +633,3 @@ pub fn fresh_name(src: ast::Ident) -> ast::Name {
|
|||
/*let num = rand::thread_rng().gen_uint_range(0,0xffff);
|
||||
gensym(format!("{}_{}",ident_to_string(src),num))*/
|
||||
}
|
||||
|
||||
// create a fresh mark.
|
||||
pub fn fresh_mark() -> ast::Mrk {
|
||||
gensym("mark").0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use std::rc::Rc;
|
|||
|
||||
use codemap::{self, CodeMap, ExpnInfo, NameAndSpan, MacroAttribute};
|
||||
use errors;
|
||||
use errors::snippet::{RenderedLine, SnippetData};
|
||||
use errors::snippet::{SnippetData};
|
||||
use config;
|
||||
use entry::{self, EntryPointType};
|
||||
use ext::base::{ExtCtxt, DummyMacroLoader};
|
||||
|
|
|
|||
|
|
@ -568,7 +568,7 @@ impl Sub for CharPos {
|
|||
//
|
||||
|
||||
/// A source code location used for error reporting
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Loc {
|
||||
/// Information about the original source
|
||||
pub file: Rc<FileMap>,
|
||||
|
|
|
|||
31
src/test/compile-fail/issue-34839.rs
Normal file
31
src/test/compile-fail/issue-34839.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
trait RegularExpression: Sized {
|
||||
type Text;
|
||||
}
|
||||
|
||||
struct ExecNoSyncStr<'a>(&'a u8);
|
||||
|
||||
impl<'c> RegularExpression for ExecNoSyncStr<'c> {
|
||||
type Text = u8;
|
||||
}
|
||||
|
||||
struct FindCaptures<'t, R>(&'t R::Text) where R: RegularExpression, R::Text: 't;
|
||||
|
||||
enum FindCapturesInner<'r, 't> {
|
||||
Dynamic(FindCaptures<'t, ExecNoSyncStr<'r>>),
|
||||
}
|
||||
|
||||
#[rustc_error]
|
||||
fn main() {} //~ ERROR compilation successful
|
||||
|
|
@ -13,3 +13,7 @@
|
|||
macro_rules! m {
|
||||
() => { include!("file.txt"); }
|
||||
}
|
||||
|
||||
macro_rules! n {
|
||||
() => { unsafe { asm!(include_str!("file.txt")); } }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(asm, rustc_attrs)]
|
||||
#![allow(unused)]
|
||||
|
||||
#[macro_use]
|
||||
mod foo;
|
||||
|
||||
m!();
|
||||
fn f() { n!(); }
|
||||
|
||||
#[rustc_error]
|
||||
fn main() {} //~ ERROR compilation successful
|
||||
|
|
|
|||
17
src/test/compile-fail/macro-stmt-matchers.rs
Normal file
17
src/test/compile-fail/macro-stmt-matchers.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_error]
|
||||
fn main() { //~ ERROR compilation successful
|
||||
macro_rules! m { ($s:stmt;) => { $s } }
|
||||
m!(vec![].push(0););
|
||||
}
|
||||
|
|
@ -18,7 +18,8 @@ fn foo<F>(f: F) where F: FnMut(Foo) {}
|
|||
fn main() {
|
||||
foo(|s| s.is_empty());
|
||||
//~^ ERROR no method named `is_empty` found
|
||||
//~^^ HELP #1: `core::slice::SliceExt`
|
||||
//~^^^ HELP #2: `core::str::StrExt`
|
||||
//~^^^^ HELP items from traits can only be used if the trait is implemented and in scope; the following traits define an item `is_empty`, perhaps you need to implement one of them:
|
||||
//~^^ HELP #1: `std::iter::ExactSizeIterator`
|
||||
//~^^^ HELP #2: `core::slice::SliceExt`
|
||||
//~^^^^ HELP #3: `core::str::StrExt`
|
||||
//~^^^^^ HELP items from traits can only be used if the trait is implemented and in scope; the following traits define an item `is_empty`, perhaps you need to implement one of them:
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,6 @@ use use_from_trait_xc::Bar::new as bnew;
|
|||
//~^ ERROR unresolved import `use_from_trait_xc::Bar::new`
|
||||
|
||||
use use_from_trait_xc::Baz::new as baznew;
|
||||
//~^ ERROR `baznew` is not directly importable
|
||||
//~^ ERROR unresolved import `use_from_trait_xc::Baz::new`
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
20
src/test/parse-fail/inner-attr-after-doc-comment.rs
Normal file
20
src/test/parse-fail/inner-attr-after-doc-comment.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z parse-only
|
||||
|
||||
#![feature(lang_items)]
|
||||
/**
|
||||
* My module
|
||||
*/
|
||||
|
||||
#![recursion_limit="100"]
|
||||
//~^ ERROR an inner attribute is not permitted following an outer doc comment
|
||||
fn main() {}
|
||||
16
src/test/parse-fail/inner-attr.rs
Normal file
16
src/test/parse-fail/inner-attr.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z parse-only
|
||||
|
||||
#[feature(lang_items)]
|
||||
|
||||
#![recursion_limit="100"] //~ ERROR an inner attribute is not permitted following an outer attribute
|
||||
fn main() {}
|
||||
19
src/test/ui/codemap_tests/empty_span.rs
Normal file
19
src/test/ui/codemap_tests/empty_span.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
#![feature(optin_builtin_traits)]
|
||||
fn main() {
|
||||
struct Foo;
|
||||
|
||||
impl !Sync for Foo {}
|
||||
|
||||
unsafe impl Send for &'static Foo { }
|
||||
}
|
||||
8
src/test/ui/codemap_tests/empty_span.stderr
Normal file
8
src/test/ui/codemap_tests/empty_span.stderr
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
error[E0321]: cross-crate traits with a default impl, like `std::marker::Send`, can only be implemented for a struct/enum type, not `&'static main::Foo`
|
||||
--> $DIR/empty_span.rs:18:5
|
||||
|
|
||||
18 | unsafe impl Send for &'static Foo { }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
104
src/test/ui/codemap_tests/huge_multispan_highlight.rs
Normal file
104
src/test/ui/codemap_tests/huge_multispan_highlight.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
|
||||
fn main() {
|
||||
let x = "foo";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let y = &mut x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
11
src/test/ui/codemap_tests/huge_multispan_highlight.stderr
Normal file
11
src/test/ui/codemap_tests/huge_multispan_highlight.stderr
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
error: cannot borrow immutable local variable `x` as mutable
|
||||
--> $DIR/huge_multispan_highlight.rs:100:18
|
||||
|
|
||||
14 | let x = "foo";
|
||||
| - use `mut x` here to make mutable
|
||||
...
|
||||
100 | let y = &mut x;
|
||||
| ^ cannot borrow mutably
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
104
src/test/ui/codemap_tests/issue-11715.rs
Normal file
104
src/test/ui/codemap_tests/issue-11715.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fn main() {
|
||||
let mut x = "foo";
|
||||
let y = &mut x;
|
||||
let z = &mut x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
12
src/test/ui/codemap_tests/issue-11715.stderr
Normal file
12
src/test/ui/codemap_tests/issue-11715.stderr
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
error[E0499]: cannot borrow `x` as mutable more than once at a time
|
||||
--> $DIR/issue-11715.rs:100:18
|
||||
|
|
||||
99 | let y = &mut x;
|
||||
| - first mutable borrow occurs here
|
||||
100 | let z = &mut x;
|
||||
| ^ second mutable borrow occurs here
|
||||
101 | }
|
||||
| - first borrow ends here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
16
src/test/ui/codemap_tests/one_line.rs
Normal file
16
src/test/ui/codemap_tests/one_line.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
|
||||
fn main() {
|
||||
let mut v = vec![Some("foo"), Some("bar")];
|
||||
v.push(v.pop().unwrap());
|
||||
}
|
||||
11
src/test/ui/codemap_tests/one_line.stderr
Normal file
11
src/test/ui/codemap_tests/one_line.stderr
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
error[E0499]: cannot borrow `v` as mutable more than once at a time
|
||||
--> $DIR/one_line.rs:15:12
|
||||
|
|
||||
15 | v.push(v.pop().unwrap());
|
||||
| - ^ - first borrow ends here
|
||||
| | |
|
||||
| | second mutable borrow occurs here
|
||||
| first mutable borrow occurs here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
24
src/test/ui/codemap_tests/overlapping_spans.rs
Normal file
24
src/test/ui/codemap_tests/overlapping_spans.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
#[derive(Debug)]
|
||||
struct Foo { }
|
||||
|
||||
struct S {f:String}
|
||||
impl Drop for S {
|
||||
fn drop(&mut self) { println!("{}", self.f); }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
match (S {f:"foo".to_string()}) {
|
||||
S {f:_s} => {}
|
||||
}
|
||||
}
|
||||
11
src/test/ui/codemap_tests/overlapping_spans.stderr
Normal file
11
src/test/ui/codemap_tests/overlapping_spans.stderr
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
|
||||
--> $DIR/overlapping_spans.rs:22:9
|
||||
|
|
||||
22 | S {f:_s} => {}
|
||||
| ^^^^^--^
|
||||
| | |
|
||||
| | hint: to prevent move, use `ref _s` or `ref mut _s`
|
||||
| cannot move out of here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
16
src/test/ui/codemap_tests/tab.rs
Normal file
16
src/test/ui/codemap_tests/tab.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
// ignore-tidy-tab
|
||||
fn main() {
|
||||
bar;
|
||||
}
|
||||
|
||||
8
src/test/ui/codemap_tests/tab.stderr
Normal file
8
src/test/ui/codemap_tests/tab.stderr
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
error[E0425]: unresolved name `bar`
|
||||
--> $DIR/tab.rs:14:2
|
||||
|
|
||||
14 | \tbar;
|
||||
| \t^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
18
src/test/ui/codemap_tests/two_files.rs
Normal file
18
src/test/ui/codemap_tests/two_files.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
include!("two_files_data.rs");
|
||||
|
||||
struct Baz { }
|
||||
|
||||
impl Bar for Baz { }
|
||||
|
||||
fn main() { }
|
||||
13
src/test/ui/codemap_tests/two_files.stderr
Normal file
13
src/test/ui/codemap_tests/two_files.stderr
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
error[E0404]: `Bar` is not a trait
|
||||
--> $DIR/two_files.rs:16:6
|
||||
|
|
||||
16 | impl Bar for Baz { }
|
||||
| ^^^ `Bar` is not a trait
|
||||
|
|
||||
::: $DIR/two_files_data.rs
|
||||
|
|
||||
15 | type Bar = Foo;
|
||||
| --------------- type aliases cannot be used for traits
|
||||
|
||||
error: cannot continue compilation due to previous error
|
||||
|
||||
16
src/test/ui/codemap_tests/two_files_data.rs
Normal file
16
src/test/ui/codemap_tests/two_files_data.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
// ignore-test
|
||||
trait Foo { }
|
||||
|
||||
type Bar = Foo;
|
||||
|
||||
14
src/test/ui/codemap_tests/unicode.rs
Normal file
14
src/test/ui/codemap_tests/unicode.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// rustc-env:RUST_NEW_ERROR_FORMAT
|
||||
extern "路濫狼á́́" fn foo() {}
|
||||
|
||||
fn main() { }
|
||||
8
src/test/ui/codemap_tests/unicode.stderr
Normal file
8
src/test/ui/codemap_tests/unicode.stderr
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
error: invalid ABI: expected one of [cdecl, stdcall, fastcall, vectorcall, aapcs, win64, Rust, C, system, rust-intrinsic, rust-call, platform-intrinsic], found `路濫狼á́́`
|
||||
--> $DIR/unicode.rs:12:8
|
||||
|
|
||||
12 | extern "路濫狼á́́" fn foo() {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
@ -1,15 +1,16 @@
|
|||
error: mismatched types [--explain E0308]
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-26480.rs:27:19
|
||||
|>
|
||||
27 |> $arr.len() * size_of($arr[0]));
|
||||
|> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u64, found usize
|
||||
$DIR/issue-26480.rs:38:5: 38:19: note: in this expansion of write! (defined in $DIR/issue-26480.rs)
|
||||
|
|
||||
27 | $arr.len() * size_of($arr[0]));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u64, found usize
|
||||
$DIR/issue-26480.rs:38:5: 38:19 note: in this expansion of write! (defined in $DIR/issue-26480.rs)
|
||||
|
||||
error: non-scalar cast: `_` as `()`
|
||||
--> $DIR/issue-26480.rs:33:19
|
||||
|>
|
||||
33 |> ($x:expr) => ($x as ())
|
||||
|> ^^^^^^^^
|
||||
$DIR/issue-26480.rs:39:5: 39:14: note: in this expansion of cast! (defined in $DIR/issue-26480.rs)
|
||||
|
|
||||
33 | ($x:expr) => ($x as ())
|
||||
| ^^^^^^^^
|
||||
$DIR/issue-26480.rs:39:5: 39:14 note: in this expansion of cast! (defined in $DIR/issue-26480.rs)
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
error: mismatched types [--explain E0308]
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/main.rs:14:18
|
||||
|>
|
||||
14 |> let x: u32 = (
|
||||
|> ^ expected u32, found ()
|
||||
note: expected type `u32`
|
||||
note: found type `()`
|
||||
|
|
||||
14 | let x: u32 = (
|
||||
| ^ expected u32, found ()
|
||||
|
|
||||
= note: expected type `u32`
|
||||
= note: found type `()`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue