diff options
78 files changed, 948 insertions, 460 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e4870aa6a89..609bf03fb6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -131,8 +131,12 @@ Some common make targets are: & everything builds in the correct manner. - `make check-stage1-std NO_REBUILD=1` - test the standard library without rebuilding the entire compiler -- `make check TESTNAME=<path-to-test-file>.rs` - Run a single test file -- `make check-stage1-rpass TESTNAME=<path-to-test-file>.rs` - Run a single +- `make check TESTNAME=<substring-of-test-name>` - Run a matching set of tests. + - `TESTNAME` should be a substring of the tests to match against e.g. it could + be the fully qualified test name, or just a part of it. + `TESTNAME=collections::hash::map::test_map::test_capacity_not_less_than_len` + or `TESTNAME=test_capacity_not_less_than_len`. +- `make check-stage1-rpass TESTNAME=<substring-of-test-name>` - Run a single rpass test with the stage1 compiler (this will be quicker than running the command above as we only build the stage1 compiler, not the entire thing). You can also leave off the `-rpass` to run all stage1 test types. diff --git a/mk/cfg/aarch64-linux-android.mk b/mk/cfg/aarch64-linux-android.mk index 274f73834d4..140c4af8297 100644 --- a/mk/cfg/aarch64-linux-android.mk +++ b/mk/cfg/aarch64-linux-android.mk @@ -1,5 +1,4 @@ # aarch64-linux-android configuration -# CROSS_PREFIX_aarch64-linux-android- CC_aarch64-linux-android=$(CFG_AARCH64_LINUX_ANDROID_NDK)/bin/aarch64-linux-android-gcc CXX_aarch64-linux-android=$(CFG_AARCH64_LINUX_ANDROID_NDK)/bin/aarch64-linux-android-g++ CPP_aarch64-linux-android=$(CFG_AARCH64_LINUX_ANDROID_NDK)/bin/aarch64-linux-android-gcc -E diff --git a/mk/cfg/x86_64-apple-ios.mk b/mk/cfg/x86_64-apple-ios.mk index dd6080fdb0b..3faf0c6a3f2 100644 --- a/mk/cfg/x86_64-apple-ios.mk +++ b/mk/cfg/x86_64-apple-ios.mk @@ -29,4 +29,4 @@ CFG_UNIXY_x86_64-apple-ios := 1 CFG_LDPATH_x86_64-apple-ios := CFG_RUN_x86_64-apple-ios = $(2) CFG_RUN_TARG_x86_64-apple-ios = $(call CFG_RUN_x86_64-apple-ios,,$(2)) -CFG_GNU_TRIPLE_i386-apple-ios := x86_64-apple-ios +CFG_GNU_TRIPLE_x86_64-apple-ios := x86_64-apple-ios diff --git a/mk/cfg/x86_64-unknown-bitrig.mk b/mk/cfg/x86_64-unknown-bitrig.mk index afffec1a53a..3ed94011c48 100644 --- a/mk/cfg/x86_64-unknown-bitrig.mk +++ b/mk/cfg/x86_64-unknown-bitrig.mk @@ -9,7 +9,7 @@ CFG_LIB_GLOB_x86_64-unknown-bitrig=lib$(1)-*.so CFG_LIB_DSYM_GLOB_x86_64-unknown-bitrig=$(1)-*.dylib.dSYM CFG_JEMALLOC_CFLAGS_x86_64-unknown-bitrig := -m64 -I/usr/include $(CFLAGS) CFG_GCCISH_CFLAGS_x86_64-unknown-bitrig := -Wall -Werror -fPIE -fPIC -m64 -I/usr/include $(CFLAGS) -CFG_GCCISH_LINK_FLAGS_x86_64-unknown-bitrig := -shared -pic -pthread -m64 $(LDFLAGS) +CFG_GCCISH_LINK_FLAGS_x86_64-unknown-bitrig := -shared -pic -pthread -m64 CFG_GCCISH_DEF_FLAG_x86_64-unknown-bitrig := -Wl,--export-dynamic,--dynamic-list= CFG_LLC_FLAGS_x86_64-unknown-bitrig := CFG_INSTALL_NAME_x86_64-unknown-bitrig = diff --git a/mk/crates.mk b/mk/crates.mk index f47c4857ef8..4db8f32bb16 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -139,13 +139,13 @@ ONLY_RLIB_alloc_system := 1 # Documented-by-default crates DOC_CRATES := std alloc collections core libc rustc_unicode -ifeq ($(CFG_DISABLE_JEMALLOC),) +ifdef CFG_DISABLE_JEMALLOC +RUSTFLAGS_rustc_back := --cfg disable_jemalloc +else TARGET_CRATES += alloc_jemalloc DEPS_std += alloc_jemalloc DEPS_alloc_jemalloc := core libc native:jemalloc ONLY_RLIB_alloc_jemalloc := 1 -else -RUSTFLAGS_rustc_back := --cfg disable_jemalloc endif ################################################################################ diff --git a/mk/docs.mk b/mk/docs.mk index f76368e3d0b..81aabe60ff3 100644 --- a/mk/docs.mk +++ b/mk/docs.mk @@ -71,7 +71,7 @@ DOC_L10N_TARGETS := # If NO_REBUILD is set then break the dependencies on rustdoc so we # build the documentation without having to rebuild rustdoc. -ifeq ($(NO_REBUILD),) +ifndef NO_REBUILD HTML_DEPS := $(RUSTDOC_EXE) else HTML_DEPS := @@ -152,7 +152,7 @@ define DEF_LIB_DOC # If NO_REBUILD is set then break the dependencies on rustdoc so we # build crate documentation without having to rebuild rustdoc. -ifeq ($(NO_REBUILD),) +ifndef NO_REBUILD LIB_DOC_DEP_$(1) = \ $$(CRATEFILE_$(1)) \ $$(RSINPUTS_$(1)) \ diff --git a/mk/grammar.mk b/mk/grammar.mk index 0d527bd0688..8956983e3be 100644 --- a/mk/grammar.mk +++ b/mk/grammar.mk @@ -41,8 +41,8 @@ $(BG)RustLexer.class: $(BG) $(SG)RustLexer.g4 check-build-lexer-verifier: $(BG)verify -ifeq ($(NO_REBUILD),) -VERIFY_DEPS := rustc-stage2-H-$(CFG_BUILD) $(LD)stamp.rustc +ifndef NO_REBUILD +VERIFY_DEPS := rustc-stage2-H-$(CFG_BUILD) $(LD)stamp.rustc else VERIFY_DEPS := endif diff --git a/mk/host.mk b/mk/host.mk index 59a00950b5c..2faed75ac07 100644 --- a/mk/host.mk +++ b/mk/host.mk @@ -18,7 +18,7 @@ # $(5) - the name of the crate being processed define CP_HOST_STAGE_N_CRATE -ifeq ($$(ONLY_RLIB_$(5)),) +ifndef ONLY_RLIB_$(5) $$(HLIB$(2)_H_$(4))/stamp.$(5): \ $$(TLIB$(1)_T_$(3)_H_$(4))/stamp.$(5) \ $$(RUST_DEPS_$(5):%=$$(HLIB$(2)_H_$(4))/stamp.%) \ diff --git a/mk/install.mk b/mk/install.mk index af6f3ff6ad2..6d9d86c3eb8 100644 --- a/mk/install.mk +++ b/mk/install.mk @@ -121,7 +121,7 @@ install-runtime-target-$(1)-cleanup: endef $(foreach target,$(CFG_TARGET), \ - $(if $(findstring $(CFG_ADB_DEVICE_STATUS),"true"), \ + $(if $(findstring $(CFG_ADB_DEVICE_STATUS),true), \ $(eval $(call INSTALL_RUNTIME_TARGET_N,$(taget),$(CFG_BUILD))) \ $(eval $(call INSTALL_RUNTIME_TARGET_CLEANUP_N,$(target))) \ )) diff --git a/mk/llvm.mk b/mk/llvm.mk index a4174efa5ef..d250ede9958 100644 --- a/mk/llvm.mk +++ b/mk/llvm.mk @@ -71,7 +71,7 @@ $$(LLVM_STAMP_$(1)): $$(S)src/rustllvm/llvm-auto-clean-trigger @$$(call E, make: done cleaning llvm) touch -r $$@.start_time $$@ && rm $$@.start_time -ifeq ($$(CFG_ENABLE_LLVM_STATIC_STDCPP),1) +ifdef CFG_ENABLE_LLVM_STATIC_STDCPP LLVM_STDCPP_RUSTFLAGS_$(1) = -L "$$(dir $$(shell $$(CC_$(1)) $$(CFG_GCCISH_CFLAGS_$(1)) \ -print-file-name=lib$(CFG_STDCPP_NAME).a))" else @@ -95,9 +95,6 @@ endef $(foreach host,$(CFG_HOST), \ $(eval $(call DEF_LLVM_RULES,$(host)))) -$(foreach host,$(CFG_HOST), \ - $(eval LLVM_CONFIGS := $(LLVM_CONFIGS) $(LLVM_CONFIG_$(host)))) - # This can't be done in target.mk because it's included before this file. define LLVM_LINKAGE_DEPS $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.rustc_llvm: $$(LLVM_LINKAGE_PATH_$(2)) diff --git a/mk/main.mk b/mk/main.mk index 9d75771dc80..b272a80eaf7 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -86,13 +86,13 @@ CFG_INFO := $(info cfg: version $(CFG_VERSION)) MKFILE_DEPS := config.stamp $(call rwildcard,$(CFG_SRC_DIR)mk/,*) MKFILES_FOR_TARBALL:=$(MKFILE_DEPS) -ifneq ($(NO_MKFILE_DEPS),) +ifdef NO_MKFILE_DEPS MKFILE_DEPS := endif NON_BUILD_HOST = $(filter-out $(CFG_BUILD),$(CFG_HOST)) NON_BUILD_TARGET = $(filter-out $(CFG_BUILD),$(CFG_TARGET)) -ifneq ($(MAKE_RESTARTS),) +ifdef MAKE_RESTARTS CFG_INFO := $(info cfg: make restarts: $(MAKE_RESTARTS)) endif @@ -107,28 +107,40 @@ ifneq ($(wildcard $(NON_BUILD_TARGET)),) CFG_INFO := $(info cfg: non-build target triples $(NON_BUILD_TARGET)) endif -CFG_RUSTC_FLAGS := $(RUSTFLAGS) +CFG_RUSTC_FLAGS := +ifdef RUSTFLAGS + CFG_RUSTC_FLAGS += $(RUSTFLAGS) +endif CFG_GCCISH_CFLAGS := CFG_GCCISH_LINK_FLAGS := CFG_JEMALLOC_FLAGS := +ifdef JEMALLOC_FLAGS + CFG_JEMALLOC_FLAGS += $(JEMALLOC_FLAGS) +endif ifdef CFG_DISABLE_OPTIMIZE $(info cfg: disabling rustc optimization (CFG_DISABLE_OPTIMIZE)) - CFG_RUSTC_FLAGS += CFG_JEMALLOC_FLAGS += --enable-debug else # The rtopt cfg turns off runtime sanity checks CFG_RUSTC_FLAGS += -O --cfg rtopt endif -CFG_JEMALLOC_FLAGS += $(JEMALLOC_FLAGS) - ifdef CFG_ENABLE_DEBUG_ASSERTIONS $(info cfg: enabling debug assertions (CFG_ENABLE_DEBUG_ASSERTIONS)) CFG_RUSTC_FLAGS += -C debug-assertions=on endif +define DEF_RUSTFLAGS_STAGE +RUSTFLAGS_STAGE$(1) := +endef + +STAGES = 0 1 2 3 + +$(foreach stage,$(STAGES), \ + $(eval $(call DEF_RUSTFLAGS_STAGE,$(stage)))) + ifdef CFG_ENABLE_DEBUGINFO $(info cfg: enabling debuginfo (CFG_ENABLE_DEBUGINFO)) CFG_RUSTC_FLAGS += -g @@ -186,9 +198,9 @@ endif ifndef CFG_DISABLE_VALGRIND_RPASS - $(info cfg: enabling valgrind run-pass tests (CFG_ENABLE_VALGRIND_RPASS)) + $(info cfg: enabling valgrind run-pass tests) $(info cfg: valgrind-rpass command set to $(CFG_VALGRIND)) - CFG_VALGRIND_RPASS :=$(CFG_VALGRIND) + CFG_VALGRIND_RPASS := $(CFG_VALGRIND) else $(info cfg: disabling valgrind run-pass tests) CFG_VALGRIND_RPASS := @@ -372,8 +384,6 @@ export CFG_BOOTSTRAP_KEY TRIPLE_TO_DEBUGGER_SCRIPT_SETTING=\ $(if $(findstring windows,$(1)),none,$(if $(findstring darwin,$(1)),lldb,gdb)) -STAGES = 0 1 2 3 - define SREQ # $(1) is the stage number # $(2) is the target triple diff --git a/mk/platform.mk b/mk/platform.mk index 9c74e657a84..d702cca209d 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -82,12 +82,11 @@ AR := ar define SET_FROM_CFG ifdef CFG_$(1) ifeq ($(origin $(1)),undefined) - $$(info cfg: using $(1)=$(CFG_$(1)) (CFG_$(1))) - $(1)=$(CFG_$(1)) - endif - ifeq ($(origin $(1)),default) - $$(info cfg: using $(1)=$(CFG_$(1)) (CFG_$(1))) - $(1)=$(CFG_$(1)) + $$(info cfg: using $(1)=$$(CFG_$(1)) (CFG_$(1))) + $(1)=$$(CFG_$(1)) + else ifeq ($(origin $(1)),default) + $$(info cfg: using $(1)=$$(CFG_$(1)) (CFG_$(1))) + $(1)=$$(CFG_$(1)) endif endif endef @@ -101,7 +100,9 @@ include $(wildcard $(CFG_SRC_DIR)mk/cfg/*.mk) define ADD_INSTALLED_OBJECTS INSTALLED_OBJECTS_$(1) += $$(CFG_INSTALLED_OBJECTS_$(1)) - REQUIRED_OBJECTS_$(1) += $$(CFG_THIRD_PARTY_OBJECTS_$(1)) + ifdef CFG_THIRD_PARTY_OBJECTS_$(1) + REQUIRED_OBJECTS_$(1) += $$(CFG_THIRD_PARTY_OBJECTS_$(1)) + endif INSTALLED_OBJECTS_$(1) += $$(call CFG_STATIC_LIB_NAME_$(1),compiler-rt) REQUIRED_OBJECTS_$(1) += $$(call CFG_STATIC_LIB_NAME_$(1),compiler-rt) endef @@ -163,15 +164,15 @@ define CFG_MAKE_TOOLCHAIN # Prepend the tools with their prefix if cross compiling ifneq ($(CFG_BUILD),$(1)) ifneq ($$(findstring msvc,$(1)),msvc) - CC_$(1)=$(CROSS_PREFIX_$(1))$(CC_$(1)) - CXX_$(1)=$(CROSS_PREFIX_$(1))$(CXX_$(1)) - CPP_$(1)=$(CROSS_PREFIX_$(1))$(CPP_$(1)) - AR_$(1)=$(CROSS_PREFIX_$(1))$(AR_$(1)) - LINK_$(1)=$(CROSS_PREFIX_$(1))$(LINK_$(1)) - RUSTC_CROSS_FLAGS_$(1)=-C linker=$$(call FIND_COMPILER,$$(LINK_$(1))) \ - -C ar=$$(call FIND_COMPILER,$$(AR_$(1))) $(RUSTC_CROSS_FLAGS_$(1)) - - RUSTC_FLAGS_$(1)=$$(RUSTC_CROSS_FLAGS_$(1)) $(RUSTC_FLAGS_$(1)) + CC_$(1)=$(CROSS_PREFIX_$(1))$(CC_$(1)) + CXX_$(1)=$(CROSS_PREFIX_$(1))$(CXX_$(1)) + CPP_$(1)=$(CROSS_PREFIX_$(1))$(CPP_$(1)) + AR_$(1)=$(CROSS_PREFIX_$(1))$(AR_$(1)) + LINK_$(1)=$(CROSS_PREFIX_$(1))$(LINK_$(1)) + RUSTC_CROSS_FLAGS_$(1)=-C linker=$$(call FIND_COMPILER,$$(LINK_$(1))) \ + -C ar=$$(call FIND_COMPILER,$$(AR_$(1))) $(RUSTC_CROSS_FLAGS_$(1)) + + RUSTC_FLAGS_$(1)=$$(RUSTC_CROSS_FLAGS_$(1)) $(RUSTC_FLAGS_$(1)) endif endif diff --git a/mk/rt.mk b/mk/rt.mk index 9dbbcbebb97..394fbe1ba0f 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -107,8 +107,6 @@ $$(RT_OUTPUT_DIR_$(1))/$$(NATIVE_$(2)_$(1)): $$(OBJS_$(2)_$(1)) endef -$(foreach target,$(CFG_TARGET), \ - $(eval $(call RUNTIME_RULES,$(target)))) $(foreach lib,$(NATIVE_LIBS), \ $(foreach target,$(CFG_TARGET), \ $(eval $(call THIRD_PARTY_LIB,$(target),$(lib))))) @@ -171,7 +169,7 @@ endif # See #17183 for details, this file is touched during the build process so we # don't want to consider it as a dependency. -JEMALLOC_DEPS := $(filter-out $(S)src/jemalloc/VERSION,$(JEMALLOC_DEPS)) +JEMALLOC_DEPS := $(filter-out $(S)src/jemalloc/VERSION,$$(JEMALLOC_DEPS)) JEMALLOC_NAME_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),jemalloc) ifeq ($$(CFG_WINDOWSY_$(1)),1) diff --git a/mk/target.mk b/mk/target.mk index 32a3eb5c20d..1fcc87f0c35 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -138,12 +138,12 @@ define TARGET_RUSTRT_STARTUP_OBJ $$(TLIB$(1)_T_$(2)_H_$(3))/$(4).o: \ $(S)src/rtstartup/$(4).rs \ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.core \ - $$(HSREQ$(1)_T_$(2)_H_$(3)) \ + $$(HSREQ$(1)_H_$(3)) \ | $$(TBIN$(1)_T_$(2)_H_$(3))/ @$$(call E, rustc: $$@) $$(STAGE$(1)_T_$(2)_H_$(3)) --emit=obj -o $$@ $$< -ifeq ($$(CFG_RUSTRT_HAS_STARTUP_OBJS_$(2)), 1) +ifdef CFG_RUSTRT_HAS_STARTUP_OBJS_$(2) # Add dependencies on Rust startup objects to all crates that depend on core. # This ensures that they are built after core (since they depend on it), # but before everything else (since they are needed for linking dylib crates). diff --git a/mk/tests.mk b/mk/tests.mk index 19587a28d55..bce5a52118b 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -36,6 +36,8 @@ TEST_CRATES = $(TEST_TARGET_CRATES) $(TEST_HOST_CRATES) # Environment configuration ###################################################################### +TESTARGS := + # The arguments to all test runners ifdef TESTNAME TESTARGS += $(TESTNAME) @@ -48,6 +50,8 @@ endif # Arguments to the cfail/rfail/rpass tests ifdef CFG_VALGRIND CTEST_RUNTOOL = --runtool "$(CFG_VALGRIND)" +else + CTEST_RUNTOOL = endif CTEST_TESTARGS := $(TESTARGS) @@ -143,10 +147,11 @@ else CFG_ADB_TEST_DIR= endif +DOC_NAMES := # $(1) - name of doc test # $(2) - file of the test define DOCTEST -DOC_NAMES := $$(DOC_NAMES) $(1) +DOC_NAMES += $(1) DOCFILE_$(1) := $(2) endef @@ -362,7 +367,7 @@ define TEST_RUNNER # If NO_REBUILD is set then break the dependencies on everything but # the source files so we can test crates without rebuilding any of the # parent crates. -ifeq ($(NO_REBUILD),) +ifndef NO_REBUILD TESTDEP_$(1)_$(2)_$(3)_$(4) = $$(SREQ$(1)_T_$(2)_H_$(3)) \ $$(foreach crate,$$(TARGET_CRATES), \ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$$(crate)) \ @@ -447,7 +452,7 @@ $(foreach host,$(CFG_HOST), \ $(if $(findstring $(target),$(CFG_BUILD)), \ $(eval $(call DEF_TEST_CRATE_RULES,$(stage),$(target),$(host),$(crate))), \ $(if $(findstring android, $(target)), \ - $(if $(findstring $(CFG_ADB_DEVICE_STATUS),"true"), \ + $(if $(findstring $(CFG_ADB_DEVICE_STATUS),true), \ $(eval $(call DEF_TEST_CRATE_RULES_android,$(stage),$(target),$(host),$(crate))), \ $(eval $(call DEF_TEST_CRATE_RULES_null,$(stage),$(target),$(host),$(crate))) \ ), \ @@ -700,14 +705,14 @@ check-stage$(1)-T-$(2)-H-$(3)-$(4)-exec: $$(call TEST_OK_FILE,$(1),$(2),$(3),$(4 # (Encoded as a separate variable because GNU make does not have a # good way to express OR on ifeq commands) -ifneq ($$(CTEST_DISABLE_$(4)),) +ifdef CTEST_DISABLE_$(4) # Test suite is disabled for all configured targets. CTEST_DONT_RUN_$(1)-T-$(2)-H-$(3)-$(4) := $$(CTEST_DISABLE_$(4)) else # else, check if non-self-hosted target (i.e. target not-in hosts) ... ifeq ($$(findstring $(2),$$(CFG_HOST)),) # ... if so, then check if this test suite is disabled for non-selfhosts. -ifneq ($$(CTEST_DISABLE_NONSELFHOST_$(4)),) +ifdef CTEST_DISABLE_NONSELFHOST_$(4) # Test suite is disabled for this target. CTEST_DONT_RUN_$(1)-T-$(2)-H-$(3)-$(4) := $$(CTEST_DISABLE_NONSELFHOST_$(4)) endif @@ -715,7 +720,7 @@ endif # Neither DISABLE nor DISABLE_NONSELFHOST is set ==> okay, run the test. endif -ifeq ($$(CTEST_DONT_RUN_$(1)-T-$(2)-H-$(3)-$(4)),) +ifndef CTEST_DONT_RUN_$(1)-T-$(2)-H-$(3)-$(4) $$(call TEST_OK_FILE,$(1),$(2),$(3),$(4)): \ $$(TEST_SREQ$(1)_T_$(2)_H_$(3)) \ $$(CTEST_DEPS_$(4)_$(1)-T-$(2)-H-$(3)) @@ -824,7 +829,7 @@ check-stage$(1)-T-$(2)-H-$(3)-doc-$(4)-exec: $$(call TEST_OK_FILE,$(1),$(2),$(3) # If NO_REBUILD is set then break the dependencies on everything but # the source files so we can test documentation without rebuilding # rustdoc etc. -ifeq ($(NO_REBUILD),) +ifndef NO_REBUILD DOCTESTDEP_$(1)_$(2)_$(3)_$(4) = \ $$(DOCFILE_$(4)) \ $$(TEST_SREQ$(1)_T_$(2)_H_$(3)) \ @@ -859,7 +864,7 @@ define DEF_CRATE_DOC_TEST # If NO_REBUILD is set then break the dependencies on everything but # the source files so we can test crate documentation without # rebuilding any of the parent crates. -ifeq ($(NO_REBUILD),) +ifndef NO_REBUILD CRATEDOCTESTDEP_$(1)_$(2)_$(3)_$(4) = \ $$(TEST_SREQ$(1)_T_$(2)_H_$(3)) \ $$(CRATE_FULLDEPS_$(1)_T_$(2)_H_$(3)_$(4)) \ @@ -922,8 +927,7 @@ TEST_GROUPS = \ pretty-rpass-full \ pretty-rfail-full \ pretty-rfail \ - pretty-pretty \ - $(NULL) + pretty-pretty define DEF_CHECK_FOR_STAGE_AND_TARGET_AND_HOST check-stage$(1)-T-$(2)-H-$(3): check-stage$(1)-T-$(2)-H-$(3)-exec diff --git a/src/doc/book/documentation.md b/src/doc/book/documentation.md index 4053e5776e3..ede3100194e 100644 --- a/src/doc/book/documentation.md +++ b/src/doc/book/documentation.md @@ -118,7 +118,7 @@ least. If your function has a non-trivial contract like this, that is detected/enforced by panics, documenting it is very important. ```rust -/// # Failures +/// # Errors # fn foo() {} ``` diff --git a/src/doc/book/error-handling.md b/src/doc/book/error-handling.md index 78527c21d10..73875704eca 100644 --- a/src/doc/book/error-handling.md +++ b/src/doc/book/error-handling.md @@ -356,11 +356,28 @@ fn file_name(file_path: &str) -> Option<&str> { ``` You might think that we could use the `map` combinator to reduce the case -analysis, but its type doesn't quite fit. Namely, `map` takes a function that -does something only with the inner value. The result of that function is then -*always* [rewrapped with `Some`](#code-option-map). Instead, we need something -like `map`, but which allows the caller to return another `Option`. Its generic -implementation is even simpler than `map`: +analysis, but its type doesn't quite fit... + +```rust,ignore +fn file_path_ext(file_path: &str) -> Option<&str> { + file_name(file_path).map(|x| extension(x)) //Compilation error +} +``` + +The `map` function here wraps the value returned by the `extension` function +inside an `Option<_>` and since the `extension` function itself returns an +`Option<&str>` the expression `file_name(file_path).map(|x| extension(x))` +actually returns an `Option<Option<&str>>`. + +But since `file_path_ext` just returns `Option<&str>` (and not +`Option<Option<&str>>`) we get a compilation error. + +The result of the function taken by map as input is *always* [rewrapped with +`Some`](#code-option-map). Instead, we need something like `map`, but which +allows the caller to return a `Option<_>` directly without wrapping it in +another `Option<_>`. + +Its generic implementation is even simpler than `map`: ```rust fn and_then<F, T, A>(option: Option<T>, f: F) -> Option<A> @@ -382,6 +399,10 @@ fn file_path_ext(file_path: &str) -> Option<&str> { } ``` +Side note: Since `and_then` essentially works like `map` but returns an +`Option<_>` instead of an `Option<Option<_>>` it is known as `flatmap` in some +other languages. + The `Option` type has many other combinators [defined in the standard library][5]. It is a good idea to skim this list and familiarize yourself with what's available—they can often reduce case analysis diff --git a/src/doc/book/getting-started.md b/src/doc/book/getting-started.md index d7b6e15794e..31ee385a928 100644 --- a/src/doc/book/getting-started.md +++ b/src/doc/book/getting-started.md @@ -39,6 +39,7 @@ Specifically they will each satisfy the following requirements: | Target | std |rustc|cargo| notes | |-------------------------------|-----|-----|-----|----------------------------| +| `i686-pc-windows-msvc` | ✓ | ✓ | ✓ | 32-bit MSVC (Windows 7+) | | `x86_64-pc-windows-msvc` | ✓ | ✓ | ✓ | 64-bit MSVC (Windows 7+) | | `i686-pc-windows-gnu` | ✓ | ✓ | ✓ | 32-bit MinGW (Windows 7+) | | `x86_64-pc-windows-gnu` | ✓ | ✓ | ✓ | 64-bit MinGW (Windows 7+) | @@ -62,7 +63,6 @@ these platforms are required to have each of the following: | Target | std |rustc|cargo| notes | |-------------------------------|-----|-----|-----|----------------------------| -| `i686-pc-windows-msvc` | ✓ | ✓ | ✓ | 32-bit MSVC (Windows 7+) | | `x86_64-unknown-linux-musl` | ✓ | | | 64-bit Linux with MUSL | | `arm-linux-androideabi` | ✓ | | | ARM Android | | `arm-unknown-linux-gnueabi` | ✓ | ✓ | | ARM Linux (2.6.18+) | @@ -85,6 +85,9 @@ unofficial locations. | `i686-linux-android` | ✓ | | | 32-bit x86 Android | | `aarch64-linux-android` | ✓ | | | ARM64 Android | | `powerpc-unknown-linux-gnu` | ✓ | | | PowerPC Linux (2.6.18+) | +| `powerpc64-unknown-linux-gnu` | ✓ | | | PPC64 Linux (2.6.18+) | +|`powerpc64le-unknown-linux-gnu`| ✓ | | | PPC64LE Linux (2.6.18+) | +|`armv7-unknown-linux-gnueabihf`| ✓ | | | ARMv7 Linux (2.6.18+) | | `i386-apple-ios` | ✓ | | | 32-bit x86 iOS | | `x86_64-apple-ios` | ✓ | | | 64-bit x86 iOS | | `armv7-apple-ios` | ✓ | | | ARM iOS | @@ -97,6 +100,7 @@ unofficial locations. | `x86_64-unknown-bitrig` | ✓ | ✓ | | 64-bit Bitrig | | `x86_64-unknown-dragonfly` | ✓ | ✓ | | 64-bit DragonFlyBSD | | `x86_64-rumprun-netbsd` | ✓ | | | 64-bit NetBSD Rump Kernel | +| `x86_64-sun-solaris` | ✓ | ✓ | | 64-bit Solaris/SunOS | | `i686-pc-windows-msvc` (XP) | ✓ | | | Windows XP support | | `x86_64-pc-windows-msvc` (XP) | ✓ | | | Windows XP support | @@ -569,7 +573,7 @@ executable application, as opposed to a library. Executables are often called *binaries* (as in `/usr/bin`, if you’re on a Unix system). Cargo has generated two files and one directory for us: a `Cargo.toml` and a -*src* directory with a *main.rs* file inside. These should look familliar, +*src* directory with a *main.rs* file inside. These should look familiar, they’re exactly what we created by hand, above. This output is all you need to get started. First, open `Cargo.toml`. It should diff --git a/src/doc/book/loops.md b/src/doc/book/loops.md index 5b08c2fb04d..b5dde9be17f 100644 --- a/src/doc/book/loops.md +++ b/src/doc/book/loops.md @@ -125,7 +125,8 @@ Don't forget to add the parentheses around the range. #### On iterators: ```rust -# let lines = "hello\nworld".lines(); +let lines = "hello\nworld".lines(); + for (linenumber, line) in lines.enumerate() { println!("{}: {}", linenumber, line); } @@ -134,10 +135,8 @@ for (linenumber, line) in lines.enumerate() { Outputs: ```text -0: Content of line one -1: Content of line two -2: Content of line three -3: Content of line four +0: hello +1: world ``` ## Ending iteration early diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index 094b7f1d034..20a7c651350 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -1511,6 +1511,13 @@ impl str { /// 'Whitespace' is defined according to the terms of the Unicode Derived /// Core Property `White_Space`. /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Left' in this context means the first + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _right_ side, not the left. + /// /// # Examples /// /// Basic usage: @@ -1520,6 +1527,16 @@ impl str { /// /// assert_eq!("Hello\tworld\t", s.trim_left()); /// ``` + /// + /// Directionality: + /// + /// ``` + /// let s = " English"; + /// assert!(Some('E') == s.trim_left().chars().next()); + /// + /// let s = " עברית"; + /// assert!(Some('×¢') == s.trim_left().chars().next()); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_left(&self) -> &str { UnicodeStr::trim_left(self) @@ -1530,6 +1547,13 @@ impl str { /// 'Whitespace' is defined according to the terms of the Unicode Derived /// Core Property `White_Space`. /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Right' in this context means the last + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _left_ side, not the right. + /// /// # Examples /// /// Basic usage: @@ -1539,6 +1563,16 @@ impl str { /// /// assert_eq!(" Hello\tworld", s.trim_right()); /// ``` + /// + /// Directionality: + /// + /// ``` + /// let s = "English "; + /// assert!(Some('h') == s.trim_right().chars().rev().next()); + /// + /// let s = "עברית "; + /// assert!(Some('ת') == s.trim_right().chars().rev().next()); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_right(&self) -> &str { UnicodeStr::trim_right(self) @@ -1584,6 +1618,13 @@ impl str { /// /// [`char`]: primitive.char.html /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Left' in this context means the first + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _right_ side, not the left. + /// /// # Examples /// /// Basic usage: @@ -1608,6 +1649,13 @@ impl str { /// /// [`char`]: primitive.char.html /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Right' in this context means the last + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _left_ side, not the right. + /// /// # Examples /// /// Simple patterns: @@ -1644,7 +1692,7 @@ impl str { /// /// [`FromStr`]: str/trait.FromStr.html /// - /// # Failure + /// # Errors /// /// Will return `Err` if it's not possible to parse this string slice into /// the desired type. diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 97c12043e76..b1242ba6d4d 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -433,7 +433,7 @@ impl String { /// /// [`str::from_utf8()`]: ../str/fn.from_utf8.html /// - /// # Failure + /// # Errors /// /// Returns `Err` if the slice is not UTF-8 with a description as to why the /// provided bytes are not UTF-8. The vector you moved in is also included. diff --git a/src/libcollectionstest/btree/map.rs b/src/libcollectionstest/btree/map.rs index 05d4aff108a..7f368f0205b 100644 --- a/src/libcollectionstest/btree/map.rs +++ b/src/libcollectionstest/btree/map.rs @@ -379,6 +379,7 @@ fn test_clone() { } #[test] +#[allow(dead_code)] fn test_variance() { use std::collections::btree_map::{Iter, IntoIter, Range, Keys, Values}; diff --git a/src/libcollectionstest/btree/set.rs b/src/libcollectionstest/btree/set.rs index fee18343328..3928804a8ed 100644 --- a/src/libcollectionstest/btree/set.rs +++ b/src/libcollectionstest/btree/set.rs @@ -256,6 +256,7 @@ fn test_recovery() { } #[test] +#[allow(dead_code)] fn test_variance() { use std::collections::btree_set::{IntoIter, Iter, Range}; diff --git a/src/libcollectionstest/lib.rs b/src/libcollectionstest/lib.rs index e57620dfb04..891ca22265e 100644 --- a/src/libcollectionstest/lib.rs +++ b/src/libcollectionstest/lib.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![deny(warnings)] + #![feature(ascii)] #![feature(binary_heap_extras)] #![feature(box_syntax)] @@ -16,27 +18,20 @@ #![feature(collections_bound)] #![feature(const_fn)] #![feature(fn_traits)] -#![feature(deque_extras)] -#![feature(drain)] #![feature(enumset)] -#![feature(into_cow)] #![feature(iter_arith)] #![feature(pattern)] #![feature(rand)] -#![feature(range_inclusive)] #![feature(rustc_private)] #![feature(set_recovery)] #![feature(slice_bytes)] -#![feature(slice_splits)] #![feature(step_by)] #![feature(str_char)] #![feature(str_escape)] -#![feature(str_match_indices)] #![feature(str_utf16)] #![feature(test)] #![feature(unboxed_closures)] #![feature(unicode)] -#![feature(vec_push_all)] #[macro_use] extern crate log; diff --git a/src/libcollectionstest/slice.rs b/src/libcollectionstest/slice.rs index e5e15025625..cde7fcaaf51 100644 --- a/src/libcollectionstest/slice.rs +++ b/src/libcollectionstest/slice.rs @@ -866,6 +866,7 @@ fn test_vec_default() { } #[test] +#[allow(deprecated)] fn test_bytes_set_memory() { use std::slice::bytes::MutableByteVector; diff --git a/src/libcollectionstest/string.rs b/src/libcollectionstest/string.rs index 89df77d074f..158145af2bb 100644 --- a/src/libcollectionstest/string.rs +++ b/src/libcollectionstest/string.rs @@ -8,11 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::borrow::{IntoCow, Cow}; +use std::borrow::Cow; use std::iter::repeat; use test::Bencher; +pub trait IntoCow<'a, B: ?Sized> where B: ToOwned { + fn into_cow(self) -> Cow<'a, B>; +} + +impl<'a> IntoCow<'a, str> for String { + fn into_cow(self) -> Cow<'a, str> { + Cow::Owned(self) + } +} + +impl<'a> IntoCow<'a, str> for &'a str { + fn into_cow(self) -> Cow<'a, str> { + Cow::Borrowed(self) + } +} + #[test] fn test_from_str() { let owned: Option<::std::string::String> = "string".parse().ok(); @@ -175,7 +191,7 @@ fn test_push_bytes() { let mut s = String::from("ABC"); unsafe { let mv = s.as_mut_vec(); - mv.push_all(&[b'D']); + mv.extend_from_slice(&[b'D']); } assert_eq!(s, "ABCD"); } diff --git a/src/libcollectionstest/vec.rs b/src/libcollectionstest/vec.rs index 554be72e426..b799be218e6 100644 --- a/src/libcollectionstest/vec.rs +++ b/src/libcollectionstest/vec.rs @@ -686,7 +686,7 @@ fn do_bench_push_all(b: &mut Bencher, dst_len: usize, src_len: usize) { b.iter(|| { let mut dst = dst.clone(); - dst.push_all(&src); + dst.extend_from_slice(&src); assert_eq!(dst.len(), dst_len + src_len); assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); }); diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index b02b2a06b75..c207ad16595 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -17,6 +17,24 @@ //! Like many traits, these are often used as bounds for generic functions, to //! support arguments of multiple types. //! +//! - Impl the `As*` traits for reference-to-reference conversions +//! - Impl the `Into` trait when you want to consume the value in the conversion +//! - The `From` trait is the most flexible, usefull for values _and_ references conversions +//! +//! As a library writer, you should prefer implementing `From<T>` rather than +//! `Into<U>`, as `From` provides greater flexibility and offer the equivalent `Into` +//! implementation for free, thanks to a blanket implementation in the standard library. +//! +//! **Note: these traits must not fail**. If the conversion can fail, you must use a dedicated +//! method which return an `Option<T>` or a `Result<T, E>`. +//! +//! # Generic impl +//! +//! - `AsRef` and `AsMut` auto-dereference if the inner type is a reference +//! - `From<U> for T` implies `Into<T> for U` +//! - `From` and `Into` are reflexive, which means that all types can `into()` +//! themselves and `from()` themselves +//! //! See each trait for usage examples. #![stable(feature = "rust1", since = "1.0.0")] @@ -30,6 +48,9 @@ use marker::Sized; /// /// [book]: ../../book/borrow-and-asref.html /// +/// **Note: this trait must not fail**. If the conversion can fail, use a dedicated method which +/// return an `Option<T>` or a `Result<T, E>`. +/// /// # Examples /// /// Both `String` and `&str` implement `AsRef<str>`: @@ -45,6 +66,12 @@ use marker::Sized; /// let s = "hello".to_string(); /// is_hello(s); /// ``` +/// +/// # Generic Impls +/// +/// - `AsRef` auto-dereference if the inner type is a reference or a mutable +/// reference (eg: `foo.as_ref()` will work the same if `foo` has type `&mut Foo` or `&&mut Foo`) +/// #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRef<T: ?Sized> { /// Performs the conversion. @@ -53,6 +80,15 @@ pub trait AsRef<T: ?Sized> { } /// A cheap, mutable reference-to-mutable reference conversion. +/// +/// **Note: this trait must not fail**. If the conversion can fail, use a dedicated method which +/// return an `Option<T>` or a `Result<T, E>`. +/// +/// # Generic Impls +/// +/// - `AsMut` auto-dereference if the inner type is a reference or a mutable +/// reference (eg: `foo.as_ref()` will work the same if `foo` has type `&mut Foo` or `&&mut Foo`) +/// #[stable(feature = "rust1", since = "1.0.0")] pub trait AsMut<T: ?Sized> { /// Performs the conversion. @@ -62,6 +98,13 @@ pub trait AsMut<T: ?Sized> { /// A conversion that consumes `self`, which may or may not be expensive. /// +/// **Note: this trait must not fail**. If the conversion can fail, use a dedicated method which +/// return an `Option<T>` or a `Result<T, E>`. +/// +/// Library writer should not implement directly this trait, but should prefer the implementation +/// of the `From` trait, which offer greater flexibility and provide the equivalent `Into` +/// implementation for free, thanks to a blanket implementation in the standard library. +/// /// # Examples /// /// `String` implements `Into<Vec<u8>>`: @@ -75,6 +118,12 @@ pub trait AsMut<T: ?Sized> { /// let s = "hello".to_string(); /// is_hello(s); /// ``` +/// +/// # Generic Impls +/// +/// - `From<T> for U` implies `Into<U> for T` +/// - `into()` is reflexive, which means that `Into<T> for T` is implemented +/// #[stable(feature = "rust1", since = "1.0.0")] pub trait Into<T>: Sized { /// Performs the conversion. @@ -84,6 +133,9 @@ pub trait Into<T>: Sized { /// Construct `Self` via a conversion. /// +/// **Note: this trait must not fail**. If the conversion can fail, use a dedicated method which +/// return an `Option<T>` or a `Result<T, E>`. +/// /// # Examples /// /// `String` implements `From<&str>`: @@ -94,6 +146,11 @@ pub trait Into<T>: Sized { /// /// assert_eq!(string, other_string); /// ``` +/// # Generic impls +/// +/// - `From<T> for U` implies `Into<U> for T` +/// - `from()` is reflexive, which means that `From<T> for T` is implemented +/// #[stable(feature = "rust1", since = "1.0.0")] pub trait From<T>: Sized { /// Performs the conversion. diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index b9ca99628e5..b2736609cf7 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -2756,20 +2756,11 @@ pub trait Extend<A> { /// /// let mut iter = numbers.iter(); /// -/// let n = iter.next(); -/// assert_eq!(Some(&1), n); -/// -/// let n = iter.next_back(); -/// assert_eq!(Some(&3), n); -/// -/// let n = iter.next_back(); -/// assert_eq!(Some(&2), n); -/// -/// let n = iter.next(); -/// assert_eq!(None, n); -/// -/// let n = iter.next_back(); -/// assert_eq!(None, n); +/// assert_eq!(Some(&1), iter.next()); +/// assert_eq!(Some(&3), iter.next_back()); +/// assert_eq!(Some(&2), iter.next_back()); +/// assert_eq!(None, iter.next()); +/// assert_eq!(None, iter.next_back()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait DoubleEndedIterator: Iterator { @@ -2789,20 +2780,11 @@ pub trait DoubleEndedIterator: Iterator { /// /// let mut iter = numbers.iter(); /// - /// let n = iter.next(); - /// assert_eq!(Some(&1), n); - /// - /// let n = iter.next_back(); - /// assert_eq!(Some(&3), n); - /// - /// let n = iter.next_back(); - /// assert_eq!(Some(&2), n); - /// - /// let n = iter.next(); - /// assert_eq!(None, n); - /// - /// let n = iter.next_back(); - /// assert_eq!(None, n); + /// assert_eq!(Some(&1), iter.next()); + /// assert_eq!(Some(&3), iter.next_back()); + /// assert_eq!(Some(&2), iter.next_back()); + /// assert_eq!(None, iter.next()); + /// assert_eq!(None, iter.next_back()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn next_back(&mut self) -> Option<Self::Item>; diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 3892455395f..f19970546d7 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -188,7 +188,7 @@ impl Utf8Error { /// it, this function is one way to have a stack-allocated string. There is /// an example of this in the examples section below. /// -/// # Failure +/// # Errors /// /// Returns `Err` if the slice is not UTF-8 with a description as to why the /// provided slice is not UTF-8. diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index 21b76c1f4be..700a577e20c 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -711,7 +711,7 @@ impl AtomicUsize { /// ``` /// use std::sync::atomic::{AtomicUsize, Ordering}; /// - /// let some_usize= AtomicUsize::new(5); + /// let some_usize = AtomicUsize::new(5); /// /// assert_eq!(some_usize.swap(10, Ordering::Relaxed), 5); /// assert_eq!(some_usize.load(Ordering::Relaxed), 10); diff --git a/src/libcoretest/fmt/num.rs b/src/libcoretest/fmt/num.rs index 247c3dcb9c7..2d3c05ea4ab 100644 --- a/src/libcoretest/fmt/num.rs +++ b/src/libcoretest/fmt/num.rs @@ -7,7 +7,6 @@ // <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. -use core::fmt::radix; #[test] fn test_format_int() { @@ -153,17 +152,22 @@ fn test_format_int_twos_complement() { } #[test] +#[allow(deprecated)] fn test_format_radix() { + use core::fmt::radix; assert!(format!("{:04}", radix(3, 2)) == "0011"); assert!(format!("{}", radix(55, 36)) == "1j"); } #[test] #[should_panic] +#[allow(deprecated)] fn test_radix_base_too_large() { + use core::fmt::radix; let _ = radix(55, 37); } +#[allow(deprecated)] mod u32 { use test::Bencher; use core::fmt::radix; @@ -207,6 +211,7 @@ mod u32 { } } +#[allow(deprecated)] mod i32 { use test::Bencher; use core::fmt::radix; diff --git a/src/libcoretest/iter.rs b/src/libcoretest/iter.rs index ba308314e9e..da9062b8a92 100644 --- a/src/libcoretest/iter.rs +++ b/src/libcoretest/iter.rs @@ -607,15 +607,15 @@ fn test_count() { } #[test] -fn test_max_by() { +fn test_max_by_key() { let xs: &[isize] = &[-3, 0, 1, 5, -10]; - assert_eq!(*xs.iter().max_by(|x| x.abs()).unwrap(), -10); + assert_eq!(*xs.iter().max_by_key(|x| x.abs()).unwrap(), -10); } #[test] -fn test_min_by() { +fn test_min_by_key() { let xs: &[isize] = &[-3, 0, 1, 5, -10]; - assert_eq!(*xs.iter().min_by(|x| x.abs()).unwrap(), 0); + assert_eq!(*xs.iter().min_by_key(|x| x.abs()).unwrap(), 0); } #[test] @@ -961,18 +961,18 @@ fn bench_multiple_take(b: &mut Bencher) { fn scatter(x: i32) -> i32 { (x * 31) % 127 } #[bench] -fn bench_max_by(b: &mut Bencher) { +fn bench_max_by_key(b: &mut Bencher) { b.iter(|| { let it = 0..100; - it.max_by(|&x| scatter(x)) + it.max_by_key(|&x| scatter(x)) }) } // http://www.reddit.com/r/rust/comments/31syce/using_iterators_to_find_the_index_of_the_min_or/ #[bench] -fn bench_max_by2(b: &mut Bencher) { +fn bench_max_by_key2(b: &mut Bencher) { fn max_index_iter(array: &[i32]) -> usize { - array.iter().enumerate().max_by(|&(_, item)| item).unwrap().0 + array.iter().enumerate().max_by_key(|&(_, item)| item).unwrap().0 } let mut data = vec![0i32; 1638]; diff --git a/src/libcoretest/lib.rs b/src/libcoretest/lib.rs index 88f1835d2cc..f23ddea5cc9 100644 --- a/src/libcoretest/lib.rs +++ b/src/libcoretest/lib.rs @@ -8,12 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![deny(warnings)] + #![feature(as_unsafe_cell)] #![feature(borrow_state)] #![feature(box_syntax)] #![feature(cell_extras)] #![feature(const_fn)] -#![feature(core)] #![feature(core_float)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] @@ -21,29 +22,22 @@ #![feature(decode_utf16)] #![feature(fixed_size_array)] #![feature(float_extras)] -#![feature(float_from_str_radix)] #![feature(flt2dec)] #![feature(fmt_radix)] #![feature(iter_arith)] #![feature(iter_arith)] -#![feature(iter_cmp)] -#![feature(iter_order)] #![feature(libc)] #![feature(nonzero)] -#![feature(num_bits_bytes)] #![feature(peekable_is_empty)] #![feature(ptr_as_ref)] #![feature(rand)] -#![feature(range_inclusive)] #![feature(raw)] -#![feature(slice_bytes)] #![feature(slice_patterns)] #![feature(step_by)] #![feature(test)] #![feature(unboxed_closures)] #![feature(unicode)] #![feature(unique)] -#![feature(clone_from_slice)] extern crate core; extern crate test; diff --git a/src/libcoretest/num/int_macros.rs b/src/libcoretest/num/int_macros.rs index b1c8aec3c35..afcf836ad10 100644 --- a/src/libcoretest/num/int_macros.rs +++ b/src/libcoretest/num/int_macros.rs @@ -14,6 +14,8 @@ mod tests { use core::$T_i::*; use core::isize; use core::ops::{Shl, Shr, Not, BitXor, BitAnd, BitOr}; + use core::mem; + use num; #[test] @@ -85,9 +87,10 @@ mod tests { #[test] fn test_count_zeros() { - assert!(A.count_zeros() == BITS as u32 - 3); - assert!(B.count_zeros() == BITS as u32 - 2); - assert!(C.count_zeros() == BITS as u32 - 5); + let bits = mem::size_of::<$T>() * 8; + assert!(A.count_zeros() == bits as u32 - 3); + assert!(B.count_zeros() == bits as u32 - 2); + assert!(C.count_zeros() == bits as u32 - 5); } #[test] diff --git a/src/libcoretest/num/uint_macros.rs b/src/libcoretest/num/uint_macros.rs index 25591db64d9..daa1cc3a7f4 100644 --- a/src/libcoretest/num/uint_macros.rs +++ b/src/libcoretest/num/uint_macros.rs @@ -15,6 +15,7 @@ mod tests { use num; use core::ops::{BitOr, BitAnd, BitXor, Shl, Shr, Not}; use std::str::FromStr; + use std::mem; #[test] fn test_overflows() { @@ -54,9 +55,10 @@ mod tests { #[test] fn test_count_zeros() { - assert!(A.count_zeros() == BITS as u32 - 3); - assert!(B.count_zeros() == BITS as u32 - 2); - assert!(C.count_zeros() == BITS as u32 - 5); + let bits = mem::size_of::<$T>() * 8; + assert!(A.count_zeros() == bits as u32 - 3); + assert!(B.count_zeros() == bits as u32 - 2); + assert!(C.count_zeros() == bits as u32 - 5); } #[test] diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index af9d0987ff0..48955bd9a15 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -93,9 +93,12 @@ type Scope<'a> = &'a ScopeChain<'a>; static ROOT_SCOPE: ScopeChain<'static> = RootScope; -pub fn krate(sess: &Session, krate: &hir::Crate, def_map: &DefMap) -> NamedRegionMap { +pub fn krate(sess: &Session, + krate: &hir::Crate, + def_map: &DefMap) + -> Result<NamedRegionMap, usize> { let mut named_region_map = NodeMap(); - sess.abort_if_new_errors(|| { + try!(sess.track_errors(|| { krate.visit_all_items(&mut LifetimeContext { sess: sess, named_region_map: &mut named_region_map, @@ -104,8 +107,8 @@ pub fn krate(sess: &Session, krate: &hir::Crate, def_map: &DefMap) -> NamedRegio trait_ref_hack: false, labels_in_fn: vec![], }); - }); - named_region_map + })); + Ok(named_region_map) } impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index 06ea945a09e..3a57474c303 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -1915,6 +1915,16 @@ impl<'tcx> ctxt<'tcx> { }) } + pub fn expr_ty_adjusted_opt(&self, expr: &hir::Expr) -> Option<Ty<'tcx>> { + self.expr_ty_opt(expr).map(|t| t.adjust(self, + expr.span, + expr.id, + self.tables.borrow().adjustments.get(&expr.id), + |method_call| { + self.tables.borrow().method_map.get(&method_call).map(|method| method.ty) + })) + } + pub fn expr_span(&self, id: NodeId) -> Span { match self.map.find(id) { Some(ast_map::NodeExpr(e)) => { diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index a55a6918b5b..f0c71825942 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -30,7 +30,7 @@ use rustc_back::target::Target; use std::path::{Path, PathBuf}; use std::cell::{Cell, RefCell}; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::env; use std::rc::Rc; @@ -77,6 +77,11 @@ pub struct Session { /// available in this crate pub available_macros: RefCell<HashSet<Name>>, + /// Map from imported macro spans (which consist of + /// the localized span for the macro body) to the + /// macro name and defintion span in the source crate. + pub imported_macro_spans: RefCell<HashMap<Span, (String, Span)>>, + next_node_id: Cell<ast::NodeId>, } @@ -179,24 +184,13 @@ impl Session { pub fn track_errors<F, T>(&self, f: F) -> Result<T, usize> where F: FnOnce() -> T { - let count = self.err_count(); + let old_count = self.err_count(); let result = f(); - let count = self.err_count() - count; - if count == 0 { + let errors = self.err_count() - old_count; + if errors == 0 { Ok(result) } else { - Err(count) - } - } - pub fn abort_if_new_errors<F, T>(&self, f: F) -> T - where F: FnOnce() -> T - { - match self.track_errors(f) { - Ok(result) => result, - Err(_) => { - self.abort_if_errors(); - unreachable!(); - } + Err(errors) } } pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { @@ -490,6 +484,7 @@ pub fn build_session_(sopts: config::Options, next_node_id: Cell::new(1), injected_allocator: Cell::new(None), available_macros: RefCell::new(HashSet::new()), + imported_macro_spans: RefCell::new(HashMap::new()), }; sess @@ -515,3 +510,15 @@ pub fn early_warn(output: config::ErrorOutputType, msg: &str) { }; emitter.emit(None, msg, None, errors::Level::Warning); } + +// Err(0) means compilation was stopped, but no errors were found. +// This would be better as a dedicated enum, but using try! is so convenient. +pub type CompileResult = Result<(), usize>; + +pub fn compile_result_from_err_count(err_count: usize) -> CompileResult { + if err_count == 0 { + Ok(()) + } else { + Err(err_count) + } +} diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs index 7ad4d3ca708..6cbea1abbb5 100644 --- a/src/librustc_borrowck/diagnostics.rs +++ b/src/librustc_borrowck/diagnostics.rs @@ -377,6 +377,33 @@ fn main() { } ``` +Moving out of a member of a mutably borrowed struct is fine if you put something +back. `mem::replace` can be used for that: + +``` +struct TheDarkKnight; + +impl TheDarkKnight { + fn nothing_is_true(self) {} +} + +struct Batcave { + knight: TheDarkKnight +} + +fn main() { + use std::mem; + + let mut cave = Batcave { + knight: TheDarkKnight + }; + let borrowed = &mut cave; + + borrowed.knight.nothing_is_true(); // E0507 + mem::replace(&mut borrowed.knight, TheDarkKnight).nothing_is_true(); // ok! +} +``` + You can find more information about borrowing in the rust-book: http://doc.rust-lang.org/stable/book/references-and-borrowing.html "##, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 6189416dd57..c189df18a82 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -12,14 +12,15 @@ use rustc::front; use rustc::front::map as hir_map; use rustc_mir as mir; use rustc_mir::mir_map::MirMap; -use rustc::session::Session; +use rustc::session::{Session, CompileResult, compile_result_from_err_count}; use rustc::session::config::{self, Input, OutputFilenames, OutputType}; use rustc::session::search_paths::PathKind; use rustc::lint; -use rustc::middle::{stability, ty, reachable}; -use rustc::middle::dependency_format; +use rustc::middle::{dependency_format, stability, ty, reachable}; +use rustc::middle::privacy::AccessLevels; use rustc::middle; use rustc::util::common::time; +use rustc::util::nodemap::NodeSet; use rustc_borrowck as borrowck; use rustc_resolve as resolve; use rustc_metadata::macro_import; @@ -35,7 +36,7 @@ use rustc_plugin as plugin; use rustc_front::hir; use rustc_front::lowering::{lower_crate, LoweringContext}; use rustc_passes::{no_asm, loops, consts, const_fn, rvalues, static_recursion}; -use super::{Compilation, CompileResult, compile_result_from_err_count}; +use super::Compilation; use serialize::json; @@ -57,15 +58,6 @@ use syntax::visit; use syntax; use syntax_ext; -macro_rules! throw_if_errors { - ($tsess: expr) => {{ - let err_count = $tsess.err_count(); - if err_count > 0 { - return Err(err_count); - } - }} -} - pub fn compile_input(sess: &Session, cstore: &CStore, cfg: ast::CrateConfig, @@ -74,14 +66,19 @@ pub fn compile_input(sess: &Session, output: &Option<PathBuf>, addl_plugins: Option<Vec<String>>, control: CompileController) -> CompileResult { - macro_rules! controller_entry_point{($point: ident, $tsess: expr, $make_state: expr) => ({ - let state = $make_state; - (control.$point.callback)(state); + macro_rules! controller_entry_point { + ($point: ident, $tsess: expr, $make_state: expr, $phase_result: expr) => {{ + let state = $make_state; + let phase_result: &CompileResult = &$phase_result; + if phase_result.is_ok() || control.$point.run_callback_on_error { + (control.$point.callback)(state); + } - if control.$point.stop == Compilation::Stop { - return compile_result_from_err_count($tsess.err_count()); - } - })} + if control.$point.stop == Compilation::Stop { + return compile_result_from_err_count($tsess.err_count()); + } + }} + } // We need nested scopes here, because the intermediate results can keep // large chunks of memory alive and we want to free them as soon as @@ -92,7 +89,8 @@ pub fn compile_input(sess: &Session, controller_entry_point!(after_parse, sess, - CompileState::state_after_parse(input, sess, outdir, &krate)); + CompileState::state_after_parse(input, sess, outdir, &krate), + Ok(())); let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess); let id = link::find_crate_name(Some(sess), &krate.attrs, input); @@ -111,7 +109,8 @@ pub fn compile_input(sess: &Session, sess, outdir, &expanded_crate, - &id[..])); + &id[..]), + Ok(())); let expanded_crate = assign_node_ids(sess, expanded_crate); // Lower ast -> hir. @@ -140,7 +139,8 @@ pub fn compile_input(sess: &Session, &expanded_crate, &hir_map.krate(), &id[..], - &lcx)); + &lcx), + Ok(())); time(sess.time_passes(), "attribute checking", || { front::check_attr::check_crate(sess, &expanded_crate); @@ -159,38 +159,38 @@ pub fn compile_input(sess: &Session, }; try!(try!(phase_3_run_analysis_passes(sess, - &cstore, - hir_map, - &arenas, - &id, - control.make_glob_map, - |tcx, mir_map, analysis| { + &cstore, + hir_map, + &arenas, + &id, + control.make_glob_map, + |tcx, mir_map, analysis, result| { { - let state = - CompileState::state_after_analysis(input, - &tcx.sess, - outdir, - opt_crate, - tcx.map.krate(), - &analysis, - &mir_map, - tcx, - &lcx, - &id); + let state = CompileState::state_after_analysis(input, + &tcx.sess, + outdir, + opt_crate, + tcx.map.krate(), + &analysis, + mir_map.as_ref(), + tcx, + &lcx, + &id); (control.after_analysis.callback)(state); - throw_if_errors!(tcx.sess); if control.after_analysis.stop == Compilation::Stop { return Err(0usize); } } + try!(result); + if log_enabled!(::log::INFO) { println!("Pre-trans"); tcx.print_debug_stats(); } let trans = phase_4_translate_to_llvm(tcx, - mir_map, + mir_map.unwrap(), analysis); if log_enabled!(::log::INFO) { @@ -205,17 +205,20 @@ pub fn compile_input(sess: &Session, }))) }; - try!(phase_5_run_llvm_passes(sess, &trans, &outputs)); + let phase5_result = phase_5_run_llvm_passes(sess, &trans, &outputs); controller_entry_point!(after_llvm, sess, - CompileState::state_after_llvm(input, sess, outdir, &trans)); + CompileState::state_after_llvm(input, sess, outdir, &trans), + phase5_result); + try!(phase5_result); phase_6_link_output(sess, &trans, &outputs); Ok(()) } + /// The name used for source code that doesn't originate in a file /// (e.g. source from stdin or a string) pub fn anon_src() -> String { @@ -269,6 +272,9 @@ impl<'a> CompileController<'a> { pub struct PhaseController<'a> { pub stop: Compilation, + // If true then the compiler will try to run the callback even if the phase + // ends with an error. Note that this is not always possible. + pub run_callback_on_error: bool, pub callback: Box<Fn(CompileState) -> () + 'a>, } @@ -276,6 +282,7 @@ impl<'a> PhaseController<'a> { pub fn basic() -> PhaseController<'a> { PhaseController { stop: Compilation::Continue, + run_callback_on_error: false, callback: box |_| {}, } } @@ -372,14 +379,14 @@ impl<'a, 'ast, 'tcx> CompileState<'a, 'ast, 'tcx> { krate: Option<&'a ast::Crate>, hir_crate: &'a hir::Crate, analysis: &'a ty::CrateAnalysis, - mir_map: &'a MirMap<'tcx>, + mir_map: Option<&'a MirMap<'tcx>>, tcx: &'a ty::ctxt<'tcx>, lcx: &'a LoweringContext<'a>, crate_name: &'a str) -> CompileState<'a, 'ast, 'tcx> { CompileState { analysis: Some(analysis), - mir_map: Some(mir_map), + mir_map: mir_map, tcx: Some(tcx), krate: krate, hir_crate: Some(hir_crate), @@ -659,9 +666,9 @@ pub fn phase_2_configure_and_expand(sess: &Session, }) })); - time(time_passes, - "const fn bodies and arguments", - || const_fn::check_crate(sess, &krate)); + try!(time(time_passes, + "const fn bodies and arguments", + || const_fn::check_crate(sess, &krate))); if sess.opts.debugging_opts.input_stats { println!("Post-expansion node count: {}", count_nodes(&krate)); @@ -713,8 +720,20 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, make_glob_map: resolve::MakeGlobMap, f: F) -> Result<R, usize> - where F: for<'a> FnOnce(&'a ty::ctxt<'tcx>, MirMap<'tcx>, ty::CrateAnalysis) -> R + where F: FnOnce(&ty::ctxt<'tcx>, Option<MirMap<'tcx>>, ty::CrateAnalysis, CompileResult) -> R { + macro_rules! try_with_f { + ($e: expr, ($t: expr, $m: expr, $a: expr)) => { + match $e { + Ok(x) => x, + Err(x) => { + f($t, $m, $a, Err(x)); + return Err(x); + } + } + } + } + let time_passes = sess.time_passes(); let krate = hir_map.krate(); @@ -739,9 +758,19 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, "resolution", || resolve::resolve_crate(sess, &hir_map, make_glob_map)); - let named_region_map = time(time_passes, - "lifetime resolution", - || middle::resolve_lifetime::krate(sess, krate, &def_map.borrow())); + let mut analysis = ty::CrateAnalysis { + export_map: export_map, + access_levels: AccessLevels::default(), + reachable: NodeSet(), + name: name, + glob_map: glob_map, + }; + + let named_region_map = try!(time(time_passes, + "lifetime resolution", + || middle::resolve_lifetime::krate(sess, + krate, + &def_map.borrow()))); time(time_passes, "looking for entry point", @@ -759,9 +788,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, "loop checking", || loops::check_crate(sess, krate)); - time(time_passes, - "static item recursion checking", - || static_recursion::check_crate(sess, krate, &def_map.borrow(), &hir_map)); + try!(time(time_passes, + "static item recursion checking", + || static_recursion::check_crate(sess, krate, &def_map.borrow(), &hir_map))); ty::ctxt::create_and_enter(sess, arenas, @@ -774,22 +803,22 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, stability::Index::new(krate), |tcx| { // passes are timed inside typeck - typeck::check_crate(tcx, trait_map); + try_with_f!(typeck::check_crate(tcx, trait_map), (tcx, None, analysis)); time(time_passes, "const checking", || consts::check_crate(tcx)); - let access_levels = + analysis.access_levels = time(time_passes, "privacy checking", || { rustc_privacy::check_crate(tcx, - &export_map, + &analysis.export_map, external_exports) }); // Do not move this check past lint time(time_passes, "stability index", || { - tcx.stability.borrow_mut().build(tcx, krate, &access_levels) + tcx.stability.borrow_mut().build(tcx, krate, &analysis.access_levels) }); time(time_passes, @@ -827,15 +856,17 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, // lot of annoying errors in the compile-fail tests (basically, // lint warnings and so on -- kindck used to do this abort, but // kindck is gone now). -nmatsakis - throw_if_errors!(tcx.sess); + if sess.err_count() > 0 { + return Ok(f(tcx, Some(mir_map), analysis, Err(sess.err_count()))); + } - let reachable_map = + analysis.reachable = time(time_passes, "reachability checking", - || reachable::find_reachable(tcx, &access_levels)); + || reachable::find_reachable(tcx, &analysis.access_levels)); time(time_passes, "death checking", || { - middle::dead::check_crate(tcx, &access_levels); + middle::dead::check_crate(tcx, &analysis.access_levels); }); let ref lib_features_used = @@ -850,20 +881,14 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, time(time_passes, "lint checking", - || lint::check_crate(tcx, &access_levels)); + || lint::check_crate(tcx, &analysis.access_levels)); // The above three passes generate errors w/o aborting - throw_if_errors!(tcx.sess); - - Ok(f(tcx, - mir_map, - ty::CrateAnalysis { - export_map: export_map, - access_levels: access_levels, - reachable: reachable_map, - name: name, - glob_map: glob_map, - })) + if sess.err_count() > 0 { + return Ok(f(tcx, Some(mir_map), analysis, Err(sess.err_count()))); + } + + Ok(f(tcx, Some(mir_map), analysis, Ok(()))) }) } @@ -913,8 +938,11 @@ pub fn phase_5_run_llvm_passes(sess: &Session, || write::run_passes(sess, trans, &sess.opts.output_types, outputs)); } - throw_if_errors!(sess); - Ok(()) + if sess.err_count() > 0 { + Err(sess.err_count()) + } else { + Ok(()) + } } /// Run the linker on any artifacts that resulted from the LLVM run. diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 97256a1a3fd..70bd938321a 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -63,7 +63,7 @@ use pretty::{PpMode, UserIdentifiedItem}; use rustc_resolve as resolve; use rustc_trans::back::link; use rustc_trans::save; -use rustc::session::{config, Session, build_session}; +use rustc::session::{config, Session, build_session, CompileResult}; use rustc::session::config::{Input, PrintRequest, OutputType, ErrorOutputType}; use rustc::middle::cstore::CrateStore; use rustc::lint::Lint; @@ -105,18 +105,6 @@ pub mod target_features; const BUG_REPORT_URL: &'static str = "https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.\ md#bug-reports"; -// Err(0) means compilation was stopped, but no errors were found. -// This would be better as a dedicated enum, but using try! is so convenient. -pub type CompileResult = Result<(), usize>; - -pub fn compile_result_from_err_count(err_count: usize) -> CompileResult { - if err_count == 0 { - Ok(()) - } else { - Err(err_count) - } -} - #[inline] fn abort_msg(err_count: usize) -> String { match err_count { @@ -146,7 +134,7 @@ pub fn run(args: Vec<String>) -> isize { let mut emitter = errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto); emitter.emit(None, &abort_msg(err_count), None, errors::Level::Fatal); - panic!(errors::FatalError); + exit_on_err(); } } } @@ -462,6 +450,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { state.out_dir) }); }; + control.after_analysis.run_callback_on_error = true; control.make_glob_map = resolve::MakeGlobMap::Yes; } @@ -947,15 +936,19 @@ pub fn monitor<F: FnOnce() + Send + 'static>(f: F) { println!("{}", str::from_utf8(&data.lock().unwrap()).unwrap()); } - // Panic so the process returns a failure code, but don't pollute the - // output with some unnecessary panic messages, we've already - // printed everything that we needed to. - io::set_panic(box io::sink()); - panic!(); + exit_on_err(); } } } +fn exit_on_err() -> ! { + // Panic so the process returns a failure code, but don't pollute the + // output with some unnecessary panic messages, we've already + // printed everything that we needed to. + io::set_panic(box io::sink()); + panic!(); +} + pub fn diagnostics_registry() -> diagnostics::registry::Registry { use syntax::diagnostics::registry::Registry; diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 1e99067bf32..91af78a5bd4 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -200,7 +200,7 @@ impl PpSourceMode { arenas, id, resolve::MakeGlobMap::No, - |tcx, _, _| { + |tcx, _, _, _| { let annotation = TypedAnnotation { tcx: tcx, }; @@ -824,7 +824,7 @@ pub fn pretty_print_input(sess: Session, &arenas, &id, resolve::MakeGlobMap::No, - |tcx, _, _| { + |tcx, _, _, _| { print_flowgraph(variants, tcx, code, diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index a662081ac21..3389992ebb8 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -132,7 +132,7 @@ fn test_env<F>(source_string: &str, ty::ctxt::create_and_enter(&sess, &arenas, def_map, - named_region_map, + named_region_map.unwrap(), ast_map, freevars, region_map, diff --git a/src/librustc_metadata/common.rs b/src/librustc_metadata/common.rs index 479ab759278..991cbe137ec 100644 --- a/src/librustc_metadata/common.rs +++ b/src/librustc_metadata/common.rs @@ -223,6 +223,8 @@ pub const tag_polarity: usize = 0x9d; pub const tag_macro_defs: usize = 0x10e; // top-level only pub const tag_macro_def: usize = 0x9e; pub const tag_macro_def_body: usize = 0x9f; +pub const tag_macro_def_span_lo: usize = 0xa8; +pub const tag_macro_def_span_hi: usize = 0xa9; pub const tag_paren_sugar: usize = 0xa0; diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 9c75007a8db..b569739c40c 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -494,7 +494,7 @@ impl<'a> CrateReader<'a> { let mut macros = vec![]; decoder::each_exported_macro(ekrate.metadata.as_slice(), &*self.cstore.intr, - |name, attrs, body| { + |name, attrs, span, body| { // NB: Don't use parse::parse_tts_from_source_str because it parses with // quote_depth > 0. let mut p = parse::new_parser_from_source_str(&self.sess.parse_sess, @@ -509,7 +509,7 @@ impl<'a> CrateReader<'a> { panic!(FatalError); } }; - let span = mk_sp(lo, p.last_span.hi); + let local_span = mk_sp(lo, p.last_span.hi); // Mark the attrs as used for attr in &attrs { @@ -520,7 +520,7 @@ impl<'a> CrateReader<'a> { ident: ast::Ident::with_empty_ctxt(name), attrs: attrs, id: ast::DUMMY_NODE_ID, - span: span, + span: local_span, imported_from: Some(item.ident), // overridden in plugin/load.rs export: false, @@ -529,6 +529,8 @@ impl<'a> CrateReader<'a> { body: body, }); + self.sess.imported_macro_spans.borrow_mut() + .insert(local_span, (name.as_str().to_string(), span)); true } ); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 326f68561b0..2cdce7ae784 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -52,7 +52,7 @@ use syntax::parse::token::{IdentInterner, special_idents}; use syntax::parse::token; use syntax::ast; use syntax::abi; -use syntax::codemap::{self, Span}; +use syntax::codemap::{self, Span, BytePos, NO_EXPANSION}; use syntax::print::pprust; use syntax::ptr::P; @@ -1471,19 +1471,28 @@ pub fn get_plugin_registrar_fn(data: &[u8]) -> Option<DefIndex> { } pub fn each_exported_macro<F>(data: &[u8], intr: &IdentInterner, mut f: F) where - F: FnMut(ast::Name, Vec<ast::Attribute>, String) -> bool, + F: FnMut(ast::Name, Vec<ast::Attribute>, Span, String) -> bool, { let macros = reader::get_doc(rbml::Doc::new(data), tag_macro_defs); for macro_doc in reader::tagged_docs(macros, tag_macro_def) { let name = item_name(intr, macro_doc); let attrs = get_attributes(macro_doc); + let span = get_macro_span(macro_doc); let body = reader::get_doc(macro_doc, tag_macro_def_body); - if !f(name, attrs, body.as_str().to_string()) { + if !f(name, attrs, span, body.as_str().to_string()) { break; } } } +pub fn get_macro_span(doc: rbml::Doc) -> Span { + let lo_doc = reader::get_doc(doc, tag_macro_def_span_lo); + let lo = BytePos(reader::doc_as_u32(lo_doc)); + let hi_doc = reader::get_doc(doc, tag_macro_def_span_hi); + let hi = BytePos(reader::doc_as_u32(hi_doc)); + return Span { lo: lo, hi: hi, expn_id: NO_EXPANSION }; +} + pub fn get_dylib_dependency_formats(cdata: Cmd) -> Vec<(ast::CrateNum, LinkagePreference)> { diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 45cbb22e6c9..1e50868f664 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -42,6 +42,7 @@ use std::rc::Rc; use std::u32; use syntax::abi; use syntax::ast::{self, NodeId, Name, CRATE_NODE_ID, CrateNum}; +use syntax::codemap::BytePos; use syntax::attr; use syntax::attr::AttrMetaMethods; use syntax::errors::Handler; @@ -1727,6 +1728,10 @@ fn encode_macro_defs(rbml_w: &mut Encoder, encode_name(rbml_w, def.name); encode_attributes(rbml_w, &def.attrs); + let &BytePos(lo) = &def.span.lo; + let &BytePos(hi) = &def.span.hi; + rbml_w.wr_tagged_u32(tag_macro_def_span_lo, lo); + rbml_w.wr_tagged_u32(tag_macro_def_span_hi, hi); rbml_w.wr_tagged_str(tag_macro_def_body, &::syntax::print::pprust::tts_to_string(&def.body)); diff --git a/src/librustc_passes/const_fn.rs b/src/librustc_passes/const_fn.rs index f422a47572b..f924210da9f 100644 --- a/src/librustc_passes/const_fn.rs +++ b/src/librustc_passes/const_fn.rs @@ -11,16 +11,16 @@ //! Verifies that const fn arguments are immutable by value bindings //! and the const fn body doesn't contain any statements -use rustc::session::Session; +use rustc::session::{Session, CompileResult}; use syntax::ast; use syntax::visit::{self, Visitor, FnKind}; use syntax::codemap::Span; -pub fn check_crate(sess: &Session, krate: &ast::Crate) { - sess.abort_if_new_errors(|| { +pub fn check_crate(sess: &Session, krate: &ast::Crate) -> CompileResult { + sess.track_errors(|| { visit::walk_crate(&mut CheckConstFn{ sess: sess }, krate); - }); + }) } struct CheckConstFn<'a> { diff --git a/src/librustc_passes/static_recursion.rs b/src/librustc_passes/static_recursion.rs index b49db16b4ce..2d81354495d 100644 --- a/src/librustc_passes/static_recursion.rs +++ b/src/librustc_passes/static_recursion.rs @@ -12,7 +12,7 @@ // recursively. use rustc::front::map as ast_map; -use rustc::session::Session; +use rustc::session::{Session, CompileResult}; use rustc::middle::def::{Def, DefMap}; use rustc::util::nodemap::NodeMap; @@ -92,16 +92,16 @@ impl<'a, 'ast: 'a> Visitor<'ast> for CheckCrateVisitor<'a, 'ast> { pub fn check_crate<'ast>(sess: &Session, krate: &'ast hir::Crate, def_map: &DefMap, - ast_map: &ast_map::Map<'ast>) { + ast_map: &ast_map::Map<'ast>) -> CompileResult { let mut visitor = CheckCrateVisitor { sess: sess, def_map: def_map, ast_map: ast_map, discriminant_map: RefCell::new(NodeMap()), }; - sess.abort_if_new_errors(|| { + sess.track_errors(|| { krate.visit_all_items(&mut visitor); - }); + }) } struct CheckItemRecursionVisitor<'a, 'ast: 'a> { diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index a1d866fc48b..8f4913f0420 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -107,20 +107,37 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { /// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined; /// otherwise, reports an error. fn define<T: ToNameBinding<'b>>(&self, parent: Module<'b>, name: Name, ns: Namespace, def: T) { - let name_binding = def.to_name_binding(); - let span = name_binding.span.unwrap_or(DUMMY_SP); - self.check_for_conflicts_between_external_crates_and_items(&parent, name, span); - if !parent.try_define_child(name, ns, name_binding) { + let binding = def.to_name_binding(); + let old_binding = match parent.try_define_child(name, ns, binding.clone()) { + Some(old_binding) => old_binding, + None => return, + }; + + let span = binding.span.unwrap_or(DUMMY_SP); + if !old_binding.is_extern_crate() && !binding.is_extern_crate() { // Record an error here by looking up the namespace that had the duplicate let ns_str = match ns { TypeNS => "type or module", ValueNS => "value" }; let resolution_error = ResolutionError::DuplicateDefinition(ns_str, name); let mut err = resolve_struct_error(self, span, resolution_error); - if let Some(sp) = parent.children.borrow().get(&(name, ns)).unwrap().span { + if let Some(sp) = old_binding.span { let note = format!("first definition of {} `{}` here", ns_str, name); err.span_note(sp, ¬e); } err.emit(); + } else if old_binding.is_extern_crate() && binding.is_extern_crate() { + span_err!(self.session, + span, + E0259, + "an external crate named `{}` has already been imported into this module", + name); + } else { + span_err!(self.session, + span, + E0260, + "the name `{}` conflicts with an external crate \ + that has been imported into this module", + name); } } @@ -289,14 +306,9 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { self.external_exports.insert(def_id); let parent_link = ModuleParentLink(parent, name); let def = Def::Mod(def_id); - let external_module = self.new_module(parent_link, Some(def), false, true); - - debug!("(build reduced graph for item) found extern `{}`", - module_to_string(&*external_module)); - self.check_for_conflicts_for_external_crate(parent, name, sp); - parent.external_module_children - .borrow_mut() - .insert(name, external_module); + let external_module = self.new_extern_crate_module(parent_link, def); + self.define(parent, name, TypeNS, (external_module, sp)); + self.build_reduced_graph_for_external_crate(&external_module); } parent diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 6c78f98c0cb..6f35d10c994 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -120,8 +120,6 @@ enum SuggestionType { } pub enum ResolutionError<'a> { - /// error E0260: name conflicts with an extern crate - NameConflictsWithExternCrate(Name), /// error E0401: can't use type parameters from outer function TypeParametersFromOuterFunction, /// error E0402: cannot use an outer type parameter in this context @@ -228,14 +226,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>, } match resolution_error { - ResolutionError::NameConflictsWithExternCrate(name) => { - struct_span_err!(resolver.session, - span, - E0260, - "the name `{}` conflicts with an external crate \ - that has been imported into this module", - name) - } ResolutionError::TypeParametersFromOuterFunction => { struct_span_err!(resolver.session, span, @@ -801,14 +791,11 @@ pub struct ModuleS<'a> { parent_link: ParentLink<'a>, def: Cell<Option<Def>>, is_public: bool, + is_extern_crate: bool, children: RefCell<HashMap<(Name, Namespace), NameBinding<'a>>>, imports: RefCell<Vec<ImportDirective>>, - // The external module children of this node that were declared with - // `extern crate`. - external_module_children: RefCell<HashMap<Name, Module<'a>>>, - // The anonymous children of this node. Anonymous children are pseudo- // modules that are implicitly created around items contained within // blocks. @@ -854,9 +841,9 @@ impl<'a> ModuleS<'a> { parent_link: parent_link, def: Cell::new(def), is_public: is_public, + is_extern_crate: false, children: RefCell::new(HashMap::new()), imports: RefCell::new(Vec::new()), - external_module_children: RefCell::new(HashMap::new()), anonymous_children: RefCell::new(NodeMap()), import_resolutions: RefCell::new(HashMap::new()), glob_count: Cell::new(0), @@ -871,10 +858,21 @@ impl<'a> ModuleS<'a> { self.children.borrow().get(&(name, ns)).cloned() } - fn try_define_child(&self, name: Name, ns: Namespace, binding: NameBinding<'a>) -> bool { + // If the name is not yet defined, define the name and return None. + // Otherwise, return the existing definition. + fn try_define_child(&self, name: Name, ns: Namespace, binding: NameBinding<'a>) + -> Option<NameBinding<'a>> { match self.children.borrow_mut().entry((name, ns)) { - hash_map::Entry::Vacant(entry) => { entry.insert(binding); true } - hash_map::Entry::Occupied(_) => false, + hash_map::Entry::Vacant(entry) => { entry.insert(binding); None } + hash_map::Entry::Occupied(entry) => { Some(entry.get().clone()) }, + } + } + + fn for_each_local_child<F: FnMut(Name, Namespace, &NameBinding<'a>)>(&self, mut f: F) { + for (&(name, ns), name_binding) in self.children.borrow().iter() { + if !name_binding.is_extern_crate() { + f(name, ns, name_binding) + } } } @@ -1005,6 +1003,10 @@ impl<'a> NameBinding<'a> { let def = self.def().unwrap(); (def, LastMod(if self.is_public() { AllPublic } else { DependsOn(def.def_id()) })) } + + fn is_extern_crate(&self) -> bool { + self.module().map(|module| module.is_extern_crate).unwrap_or(false) + } } /// Interns the names of the primitive types. @@ -1184,6 +1186,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.arenas.modules.alloc(ModuleS::new(parent_link, def, external, is_public)) } + fn new_extern_crate_module(&self, parent_link: ParentLink<'a>, def: Def) -> Module<'a> { + let mut module = ModuleS::new(parent_link, Some(def), false, true); + module.is_extern_crate = true; + self.arenas.modules.alloc(module) + } + fn get_ribs<'b>(&'b mut self, ns: Namespace) -> &'b mut Vec<Rib<'a>> { match ns { ValueNS => &mut self.value_ribs, TypeNS => &mut self.type_ribs } } @@ -1211,32 +1219,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - /// Check that an external crate doesn't collide with items or other external crates. - fn check_for_conflicts_for_external_crate(&self, module: Module<'a>, name: Name, span: Span) { - if module.external_module_children.borrow().contains_key(&name) { - span_err!(self.session, - span, - E0259, - "an external crate named `{}` has already been imported into this module", - name); - } - if let Some(name_binding) = module.get_child(name, TypeNS) { - resolve_error(self, - name_binding.span.unwrap_or(codemap::DUMMY_SP), - ResolutionError::NameConflictsWithExternCrate(name)); - } - } - - /// Checks that the names of items don't collide with external crates. - fn check_for_conflicts_between_external_crates_and_items(&self, - module: Module<'a>, - name: Name, - span: Span) { - if module.external_module_children.borrow().contains_key(&name) { - resolve_error(self, span, ResolutionError::NameConflictsWithExternCrate(name)); - } - } - /// Resolves the given module path from the given root `module_`. fn resolve_module_path_from_root(&mut self, module_: Module<'a>, @@ -1245,11 +1227,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { span: Span, lp: LastPrivate) -> ResolveResult<(Module<'a>, LastPrivate)> { - fn search_parent_externals<'a>(needle: Name, module: Module<'a>) - -> Option<Module<'a>> { - match module.external_module_children.borrow().get(&needle) { - Some(_) => Some(module), - None => match module.parent_link { + fn search_parent_externals<'a>(needle: Name, module: Module<'a>) -> Option<Module<'a>> { + match module.get_child(needle, TypeNS) { + Some(ref binding) if binding.is_extern_crate() => Some(module), + _ => match module.parent_link { ModuleParentLink(ref parent, _) => { search_parent_externals(needle, parent) } @@ -1480,17 +1461,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - // Search for external modules. - if namespace == TypeNS { - let children = module_.external_module_children.borrow(); - if let Some(module) = children.get(&name) { - let name_binding = NameBinding::create_from_module(module, None); - debug!("lower name bindings succeeded"); - return Success((Target::new(module_, name_binding, Shadowable::Never), - false)); - } - } - // Finally, proceed up the scope chain looking for parent modules. let mut search_module = module_; loop { @@ -1684,16 +1654,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some(..) | None => {} // Continue. } - // Finally, search through external children. - if namespace == TypeNS { - let children = module_.external_module_children.borrow(); - if let Some(module) = children.get(&name) { - let name_binding = NameBinding::create_from_module(module, None); - return Success((Target::new(module_, name_binding, Shadowable::Never), - false)); - } - } - // We're out of luck. debug!("(resolving name in module) failed to resolve `{}`", name); return Failed(None); @@ -1712,7 +1672,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Descend into children and anonymous children. build_reduced_graph::populate_module_if_necessary(self, &module_); - for (_, child_node) in module_.children.borrow().iter() { + module_.for_each_local_child(|_, _, child_node| { match child_node.module() { None => { // Continue. @@ -1721,7 +1681,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.report_unresolved_imports(child_module); } } - } + }); for (_, module_) in module_.anonymous_children.borrow().iter() { self.report_unresolved_imports(module_); diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 50537a6b936..47b91ccb9d6 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -213,7 +213,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { self.resolver.current_module = orig_module; build_reduced_graph::populate_module_if_necessary(self.resolver, &module_); - for (_, child_node) in module_.children.borrow().iter() { + module_.for_each_local_child(|_, _, child_node| { match child_node.module() { None => { // Nothing to do. @@ -222,7 +222,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { errors.extend(self.resolve_imports_for_module_subtree(child_module)); } } - } + }); for (_, child_module) in module_.anonymous_children.borrow().iter() { errors.extend(self.resolve_imports_for_module_subtree(child_module)); @@ -386,18 +386,13 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { -> (ResolveResult<(Module<'b>, NameBinding<'b>)>, bool) { build_reduced_graph::populate_module_if_necessary(self.resolver, module); if let Some(name_binding) = module.get_child(name, ns) { - return (Success((module, name_binding)), false); - } - - if ns == TypeNS { - if let Some(extern_crate) = module.external_module_children.borrow().get(&name) { + if name_binding.is_extern_crate() { // track the extern crate as used. - if let Some(DefId{ krate: kid, .. }) = extern_crate.def_id() { - self.resolver.used_crates.insert(kid); + if let Some(DefId { krate, .. }) = name_binding.module().unwrap().def_id() { + self.resolver.used_crates.insert(krate); } - let name_binding = NameBinding::create_from_module(extern_crate, None); - return (Success((module, name_binding)), false); } + return (Success((module, name_binding)), false) } // If there is an unresolved glob at this point in the containing module, bail out. @@ -725,13 +720,13 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { // Add all children from the containing module. build_reduced_graph::populate_module_if_necessary(self.resolver, &target_module); - for (&name, name_binding) in target_module.children.borrow().iter() { + target_module.for_each_local_child(|name, ns, name_binding| { self.merge_import_resolution(module_, target_module, import_directive, - name, + (name, ns), name_binding.clone()); - } + }); // Record the destination of this import if let Some(did) = target_module.def_id() { @@ -878,21 +873,6 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { import: &ImportResolution<'b>, import_span: Span, (name, ns): (Name, Namespace)) { - // First, check for conflicts between imports and `extern crate`s. - if ns == TypeNS { - if module.external_module_children.borrow().contains_key(&name) { - match import.target { - Some(ref target) if target.shadowable != Shadowable::Always => { - let msg = format!("import `{0}` conflicts with imported crate \ - in this module (maybe you meant `use {0}::*`?)", - name); - span_err!(self.resolver.session, import_span, E0254, "{}", &msg[..]); - } - Some(_) | None => {} - } - } - } - // Check for item conflicts. let name_binding = match module.get_child(name, ns) { None => { @@ -921,6 +901,14 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { } else { match import.target { Some(ref target) if target.shadowable != Shadowable::Always => { + if name_binding.is_extern_crate() { + let msg = format!("import `{0}` conflicts with imported crate \ + in this module (maybe you meant `use {0}::*`?)", + name); + span_err!(self.resolver.session, import_span, E0254, "{}", &msg[..]); + return; + } + let (what, note) = match name_binding.module() { Some(ref module) if module.is_normal() => ("existing submodule", "note conflicting module here"), diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs index 2951cf2ee1b..2b866eeaa9f 100644 --- a/src/librustc_trans/save/dump_csv.rs +++ b/src/librustc_trans/save/dump_csv.rs @@ -37,6 +37,8 @@ use middle::def_id::DefId; use middle::ty; use std::fs::File; +use std::hash::*; +use std::collections::HashSet; use syntax::ast::{self, NodeId}; use syntax::codemap::*; @@ -70,6 +72,14 @@ pub struct DumpCsvVisitor<'l, 'tcx: 'l> { fmt: FmtStrs<'l, 'tcx>, cur_scope: NodeId, + + // Set of macro definition (callee) spans, and the set + // of macro use (callsite) spans. We store these to ensure + // we only write one macro def per unique macro definition, and + // one macro use per unique callsite span. + mac_defs: HashSet<Span>, + mac_uses: HashSet<Span>, + } impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { @@ -92,6 +102,8 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { span_utils, tcx), cur_scope: 0, + mac_defs: HashSet::new(), + mac_uses: HashSet::new(), } } @@ -801,7 +813,7 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { "<mutable>".to_string() }; let types = self.tcx.node_types(); - let typ = types.get(&id).unwrap().to_string(); + let typ = types.get(&id).map(|t| t.to_string()).unwrap_or(String::new()); // Get the span only for the name of the variable (I hope the path // is only ever a variable name, but who knows?). let sub_span = self.span.span_for_last_ident(p.span); @@ -814,10 +826,41 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { &typ); } } + + /// Extract macro use and definition information from the AST node defined + /// by the given NodeId, using the expansion information from the node's + /// span. + /// + /// If the span is not macro-generated, do nothing, else use callee and + /// callsite spans to record macro definition and use data, using the + /// mac_uses and mac_defs sets to prevent multiples. + fn process_macro_use(&mut self, span: Span, id: NodeId) { + let data = match self.save_ctxt.get_macro_use_data(span, id) { + None => return, + Some(data) => data, + }; + let mut hasher = SipHasher::new(); + data.callee_span.hash(&mut hasher); + let hash = hasher.finish(); + let qualname = format!("{}::{}", data.name, hash); + // Don't write macro definition for imported macros + if !self.mac_defs.contains(&data.callee_span) + && !data.imported { + self.mac_defs.insert(data.callee_span); + self.fmt.macro_str(data.callee_span, data.callee_span, + data.name.clone(), qualname.clone()); + } + if !self.mac_uses.contains(&data.span) { + self.mac_uses.insert(data.span); + self.fmt.macro_use_str(data.span, data.span, data.name, + qualname, data.scope); + } + } } impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { fn visit_item(&mut self, item: &ast::Item) { + self.process_macro_use(item.span, item.id); match item.node { ast::ItemUse(ref use_item) => { match use_item.node { @@ -970,6 +1013,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) { + self.process_macro_use(trait_item.span, trait_item.id); match trait_item.node { ast::ConstTraitItem(ref ty, Some(ref expr)) => { self.process_const(trait_item.id, @@ -991,6 +1035,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) { + self.process_macro_use(impl_item.span, impl_item.id); match impl_item.node { ast::ImplItemKind::Const(ref ty, ref expr) => { self.process_const(impl_item.id, @@ -1012,6 +1057,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } fn visit_ty(&mut self, t: &ast::Ty) { + self.process_macro_use(t.span, t.id); match t.node { ast::TyPath(_, ref path) => { match self.lookup_type_ref(t.id) { @@ -1031,6 +1077,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } fn visit_expr(&mut self, ex: &ast::Expr) { + self.process_macro_use(ex.span, ex.id); match ex.node { ast::ExprCall(ref _f, ref _args) => { // Don't need to do anything for function calls, @@ -1117,11 +1164,13 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } } - fn visit_mac(&mut self, _: &ast::Mac) { - // Just stop, macros are poison to us. + fn visit_mac(&mut self, mac: &ast::Mac) { + // These shouldn't exist in the AST at this point, log a span bug. + self.sess.span_bug(mac.span, "macro invocation should have been expanded out of AST"); } fn visit_pat(&mut self, p: &ast::Pat) { + self.process_macro_use(p.span, p.id); self.process_pat(p); } @@ -1177,10 +1226,13 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } fn visit_stmt(&mut self, s: &ast::Stmt) { + let id = s.node.id(); + self.process_macro_use(s.span, id.unwrap()); visit::walk_stmt(self, s) } fn visit_local(&mut self, l: &ast::Local) { + self.process_macro_use(l.span, l.id); let value = self.span.snippet(l.span); self.process_var_decl(&l.pat, value); diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 37b23d6ee9c..11c82d30246 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -73,6 +73,8 @@ pub enum Data { FunctionCallData(FunctionCallData), /// Data about a method call. MethodCallData(MethodCallData), + /// Data about a macro use. + MacroUseData(MacroUseData), } /// Data for all kinds of functions and methods. @@ -174,6 +176,22 @@ pub struct MethodCallData { pub decl_id: Option<DefId>, } +/// Data about a macro use. +#[derive(Debug)] +pub struct MacroUseData { + pub span: Span, + pub name: String, + // Because macro expansion happens before ref-ids are determined, + // we use the callee span to reference the associated macro definition. + pub callee_span: Span, + pub scope: NodeId, + pub imported: bool, +} + +macro_rules! option_try( + ($e:expr) => (match $e { Some(e) => e, None => return None }) +); + impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { @@ -463,11 +481,15 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> { + let hir_node = lowering::lower_expr(self.lcx, expr); + let ty = self.tcx.expr_ty_adjusted_opt(&hir_node); + if ty.is_none() || ty.unwrap().sty == ty::TyError { + return None; + } match expr.node { ast::ExprField(ref sub_ex, ident) => { let hir_node = lowering::lower_expr(self.lcx, sub_ex); - let ty = &self.tcx.expr_ty_adjusted(&hir_node).sty; - match *ty { + match self.tcx.expr_ty_adjusted(&hir_node).sty { ty::TyStruct(def, _) => { let f = def.struct_variant().field_named(ident.node.name); let sub_span = self.span_utils.span_for_last_ident(expr.span); @@ -487,8 +509,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } ast::ExprStruct(ref path, _, _) => { let hir_node = lowering::lower_expr(self.lcx, expr); - let ty = &self.tcx.expr_ty_adjusted(&hir_node).sty; - match *ty { + match self.tcx.expr_ty_adjusted(&hir_node).sty { ty::TyStruct(def, _) => { let sub_span = self.span_utils.span_for_last_ident(path.span); filter!(self.span_utils, sub_span, path.span, None); @@ -652,6 +673,51 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { }) } + /// Attempt to return MacroUseData for any AST node. + /// + /// For a given piece of AST defined by the supplied Span and NodeId, + /// returns None if the node is not macro-generated or the span is malformed, + /// else uses the expansion callsite and callee to return some MacroUseData. + pub fn get_macro_use_data(&self, span: Span, id: NodeId) -> Option<MacroUseData> { + if !generated_code(span) { + return None; + } + // Note we take care to use the source callsite/callee, to handle + // nested expansions and ensure we only generate data for source-visible + // macro uses. + let callsite = self.tcx.sess.codemap().source_callsite(span); + let callee = self.tcx.sess.codemap().source_callee(span); + let callee = option_try!(callee); + let callee_span = option_try!(callee.span); + + // Ignore attribute macros, their spans are usually mangled + if let MacroAttribute(_) = callee.format { + return None; + } + + // If the callee is an imported macro from an external crate, need to get + // the source span and name from the session, as their spans are localized + // when read in, and no longer correspond to the source. + if let Some(mac) = self.tcx.sess.imported_macro_spans.borrow().get(&callee_span) { + let &(ref mac_name, mac_span) = mac; + return Some(MacroUseData { + span: callsite, + name: mac_name.clone(), + callee_span: mac_span, + scope: self.enclosing_scope(id), + imported: true, + }); + } + + Some(MacroUseData { + span: callsite, + name: callee.name().to_string(), + callee_span: callee_span, + scope: self.enclosing_scope(id), + imported: false, + }) + } + pub fn get_data_for_id(&self, _id: &NodeId) -> Data { // FIXME unimplemented!(); diff --git a/src/librustc_trans/save/recorder.rs b/src/librustc_trans/save/recorder.rs index eaba366c1d4..c0083bb9480 100644 --- a/src/librustc_trans/save/recorder.rs +++ b/src/librustc_trans/save/recorder.rs @@ -96,6 +96,8 @@ pub enum Row { VarRef, TypeRef, FnRef, + Macro, + MacroUse, } impl<'a, 'tcx: 'a> FmtStrs<'a, 'tcx> { @@ -219,6 +221,14 @@ impl<'a, 'tcx: 'a> FmtStrs<'a, 'tcx> { vec!("refid", "refidcrate", "qualname", "scopeid"), true, true), + Macro => ("macro", + vec!("name", "qualname"), + true, + true), + MacroUse => ("macro_use", + vec!("callee_name", "qualname", "scopeid"), + true, + true), } } @@ -686,4 +696,19 @@ impl<'a, 'tcx: 'a> FmtStrs<'a, 'tcx> { sub_span, svec!(id.index.as_usize(), id.krate, "", scope_id)); } + + pub fn macro_str(&mut self, span: Span, sub_span: Span, name: String, qualname: String) { + self.record_with_span(Macro, span, sub_span, svec!(name, qualname)); + } + + pub fn macro_use_str(&mut self, + span: Span, + sub_span: Span, + name: String, + qualname: String, + scope_id: NodeId) { + let scope_id = self.normalize_node_id(scope_id); + self.record_with_span(MacroUse, span, sub_span, + svec!(name, qualname, scope_id)); + } } diff --git a/src/librustc_trans/save/span_utils.rs b/src/librustc_trans/save/span_utils.rs index 344431032d6..95c1d7bd031 100644 --- a/src/librustc_trans/save/span_utils.rs +++ b/src/librustc_trans/save/span_utils.rs @@ -378,6 +378,25 @@ impl<'a> SpanUtils<'a> { } } + // Given a macro_rules definition span, return the span of the macro's name. + pub fn span_for_macro_name(&self, span: Span) -> Option<Span> { + let mut toks = self.retokenise_span(span); + loop { + let ts = toks.real_token(); + if ts.tok == token::Eof { + return None; + } + if ts.tok == token::Not { + let ts = toks.real_token(); + if ts.tok.is_ident() { + return self.make_sub_span(span, Some(ts.sp)); + } else { + return None; + } + } + } + } + /// Return true if the span is generated code, and /// it is not a subspan of the root callsite. /// @@ -395,10 +414,16 @@ impl<'a> SpanUtils<'a> { if sub_span.is_none() { return true; } - // A generated span is deemed invalid if it is not a sub-span of the root + + //If the span comes from a fake filemap, filter it. + if !self.sess.codemap().lookup_char_pos(parent.lo).file.is_real_file() { + return true; + } + + // Otherwise, a generated span is deemed invalid if it is not a sub-span of the root // callsite. This filters out macro internal variables and most malformed spans. let span = self.sess.codemap().source_callsite(parent); - !(parent.lo >= span.lo && parent.hi <= span.hi) + !(span.contains(parent)) } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f811eb872fc..dc93320285a 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -105,7 +105,7 @@ use middle::ty::fold::{TypeFolder, TypeFoldable}; use middle::ty::util::Representability; use require_c_abi_if_variadic; use rscope::{ElisionFailureInfo, RegionScope}; -use session::Session; +use session::{Session, CompileResult}; use {CrateCtxt, lookup_full_def}; use TypeAndSubsts; use lint; @@ -383,29 +383,29 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckItemBodiesVisitor<'a, 'tcx> { } } -pub fn check_wf_new(ccx: &CrateCtxt) { - ccx.tcx.sess.abort_if_new_errors(|| { +pub fn check_wf_new(ccx: &CrateCtxt) -> CompileResult { + ccx.tcx.sess.track_errors(|| { let mut visit = wfcheck::CheckTypeWellFormedVisitor::new(ccx); ccx.tcx.visit_all_items_in_krate(DepNode::WfCheck, &mut visit); - }); + }) } -pub fn check_item_types(ccx: &CrateCtxt) { - ccx.tcx.sess.abort_if_new_errors(|| { +pub fn check_item_types(ccx: &CrateCtxt) -> CompileResult { + ccx.tcx.sess.track_errors(|| { let mut visit = CheckItemTypesVisitor { ccx: ccx }; ccx.tcx.visit_all_items_in_krate(DepNode::TypeckItemType, &mut visit); - }); + }) } -pub fn check_item_bodies(ccx: &CrateCtxt) { - ccx.tcx.sess.abort_if_new_errors(|| { +pub fn check_item_bodies(ccx: &CrateCtxt) -> CompileResult { + ccx.tcx.sess.track_errors(|| { let mut visit = CheckItemBodiesVisitor { ccx: ccx }; ccx.tcx.visit_all_items_in_krate(DepNode::TypeckItemBody, &mut visit); - }); + }) } -pub fn check_drop_impls(ccx: &CrateCtxt) { - ccx.tcx.sess.abort_if_new_errors(|| { +pub fn check_drop_impls(ccx: &CrateCtxt) -> CompileResult { + ccx.tcx.sess.track_errors(|| { let _task = ccx.tcx.dep_graph.in_task(DepNode::Dropck); let drop_trait = match ccx.tcx.lang_items.drop_trait() { Some(id) => ccx.tcx.lookup_trait_def(id), None => { return } @@ -421,7 +421,7 @@ pub fn check_drop_impls(ccx: &CrateCtxt) { } } }); - }); + }) } fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 49de0efa61d..cb0b4d0902c 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -105,7 +105,7 @@ use middle::def::Def; use middle::infer::{self, TypeOrigin}; use middle::subst; use middle::ty::{self, Ty, TypeFoldable}; -use session::config; +use session::{config, CompileResult}; use util::common::time; use rustc_front::hir; @@ -323,7 +323,7 @@ fn check_for_entry_fn(ccx: &CrateCtxt) { } } -pub fn check_crate(tcx: &ty::ctxt, trait_map: ty::TraitMap) { +pub fn check_crate(tcx: &ty::ctxt, trait_map: ty::TraitMap) -> CompileResult { let time_passes = tcx.sess.time_passes(); let ccx = CrateCtxt { trait_map: trait_map, @@ -333,34 +333,40 @@ pub fn check_crate(tcx: &ty::ctxt, trait_map: ty::TraitMap) { // this ensures that later parts of type checking can assume that items // have valid types and not error - tcx.sess.abort_if_new_errors(|| { + try!(tcx.sess.track_errors(|| { time(time_passes, "type collecting", || collect::collect_item_types(tcx)); - }); + })); time(time_passes, "variance inference", || variance::infer_variance(tcx)); - tcx.sess.abort_if_new_errors(|| { + try!(tcx.sess.track_errors(|| { time(time_passes, "coherence checking", || coherence::check_coherence(&ccx)); - }); + })); - time(time_passes, "wf checking", || - check::check_wf_new(&ccx)); + try!(time(time_passes, "wf checking", || + check::check_wf_new(&ccx))); - time(time_passes, "item-types checking", || - check::check_item_types(&ccx)); + try!(time(time_passes, "item-types checking", || + check::check_item_types(&ccx))); - time(time_passes, "item-bodies checking", || - check::check_item_bodies(&ccx)); + try!(time(time_passes, "item-bodies checking", || + check::check_item_bodies(&ccx))); - time(time_passes, "drop-impl checking", || - check::check_drop_impls(&ccx)); + try!(time(time_passes, "drop-impl checking", || + check::check_drop_impls(&ccx))); check_for_entry_fn(&ccx); - tcx.sess.abort_if_errors(); + + let err_count = tcx.sess.err_count(); + if err_count == 0 { + Ok(()) + } else { + Err(err_count) + } } __build_diagnostic_array! { librustc_typeck, DIAGNOSTICS } diff --git a/src/librustc_unicode/char.rs b/src/librustc_unicode/char.rs index 46ecd3a80b5..9386453d660 100644 --- a/src/librustc_unicode/char.rs +++ b/src/librustc_unicode/char.rs @@ -194,7 +194,7 @@ impl char { /// * `a-z` /// * `A-Z` /// - /// # Failure + /// # Errors /// /// Returns `None` if the `char` does not refer to a digit in the given radix. /// diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 5eac358f1bf..3e8457069d2 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -153,7 +153,7 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs, &arenas, &name, resolve::MakeGlobMap::No, - |tcx, _, analysis| { + |tcx, _, analysis, _| { let _ignore = tcx.dep_graph.in_ignore(); let ty::CrateAnalysis { access_levels, .. } = analysis; diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 173214eda44..7ce4aa07b50 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -72,7 +72,10 @@ impl DefaultResizePolicy { // // This doesn't have to be checked for overflow since allocation size // in bytes will overflow earlier than multiplication by 10. - cap * 10 / 11 + // + // As per https://github.com/rust-lang/rust/pull/30991 this is updated + // to be: (cap * den + den - 1) / num + (cap * 10 + 10 - 1) / 11 } } @@ -2418,4 +2421,29 @@ mod test_map { assert_eq!(a[&2], "two"); assert_eq!(a[&3], "three"); } + + #[test] + fn test_capacity_not_less_than_len() { + let mut a = HashMap::new(); + let mut item = 0; + + for _ in 0..116 { + a.insert(item, 0); + item += 1; + } + + assert!(a.capacity() > a.len()); + + let free = a.capacity() - a.len(); + for _ in 0..free { + a.insert(item, 0); + item += 1; + } + + assert_eq!(a.len(), a.capacity()); + + // Insert at capacity should cause allocation. + a.insert(item, 0); + assert!(a.capacity() > a.len()); + } } diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index e40a3d06f77..d12cfa6183a 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -70,7 +70,7 @@ pub struct Metadata(fs_imp::FileAttr); /// information like the entry's path and possibly other metadata can be /// learned. /// -/// # Failure +/// # Errors /// /// This `io::Result` will be an `Err` if there's some sort of intermittent /// IO error during iteration. diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 0917346213f..b7afd12d8e5 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -365,6 +365,9 @@ pub mod builtin { /// stringification of all the tokens passed to the macro. No restrictions /// are placed on the syntax of the macro invocation itself. /// + /// Note that the expanded results of the input tokens may change in the + /// future. You should be careful if you rely on the output. + /// /// # Examples /// /// ``` diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 7197dfa8b2d..5e0a54392d2 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -47,6 +47,16 @@ use thread::{self, JoinHandle}; /// /// assert!(ecode.success()); /// ``` +/// +/// # Note +/// +/// Take note that there is no implementation of +/// [`Drop`](../../core/ops/trait.Drop.html) for child processes, so if you +/// do not ensure the `Child` has exited then it will continue to run, even +/// after the `Child` handle to the child process has gone out of scope. +/// +/// Calling `wait` (or other functions that wrap around it) will make the +/// parent process wait until the child has actually exited before continuing. #[stable(feature = "process", since = "1.0.0")] pub struct Child { handle: imp::Process, diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 1f7fe820bf8..9a786752365 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -129,7 +129,7 @@ impl Condvar { /// the predicate must always be checked each time this function returns to /// protect against spurious wakeups. /// - /// # Failure + /// # Errors /// /// This function will return an error if the mutex being waited on is /// poisoned when this thread re-acquires the lock. For more information, diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 6b20e51967d..fe9f0371abd 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -205,7 +205,7 @@ impl<T: ?Sized> Mutex<T> { /// held. An RAII guard is returned to allow scoped unlock of the lock. When /// the guard goes out of scope, the mutex will be unlocked. /// - /// # Failure + /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then /// this call will return an error once the mutex is acquired. @@ -223,7 +223,7 @@ impl<T: ?Sized> Mutex<T> { /// /// This function does not block. /// - /// # Failure + /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be @@ -250,7 +250,7 @@ impl<T: ?Sized> Mutex<T> { /// Consumes this mutex, returning the underlying data. /// - /// # Failure + /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then /// this call will return an error instead. @@ -280,7 +280,7 @@ impl<T: ?Sized> Mutex<T> { /// Since this call borrows the `Mutex` mutably, no actual locking needs to /// take place---the mutable borrow statically guarantees no locks exist. /// - /// # Failure + /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then /// this call will return an error instead. diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index 3dbef435481..63ef7732ad6 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -169,7 +169,7 @@ impl<T: ?Sized> RwLock<T> { /// Returns an RAII guard which will release this thread's shared access /// once it is dropped. /// - /// # Failure + /// # Errors /// /// This function will return an error if the RwLock is poisoned. An RwLock /// is poisoned whenever a writer panics while holding an exclusive lock. @@ -192,7 +192,7 @@ impl<T: ?Sized> RwLock<T> { /// This function does not provide any guarantees with respect to the ordering /// of whether contentious readers or writers will acquire the lock first. /// - /// # Failure + /// # Errors /// /// This function will return an error if the RwLock is poisoned. An RwLock /// is poisoned whenever a writer panics while holding an exclusive lock. An @@ -217,7 +217,7 @@ impl<T: ?Sized> RwLock<T> { /// Returns an RAII guard which will drop the write access of this rwlock /// when dropped. /// - /// # Failure + /// # Errors /// /// This function will return an error if the RwLock is poisoned. An RwLock /// is poisoned whenever a writer panics while holding an exclusive lock. @@ -240,7 +240,7 @@ impl<T: ?Sized> RwLock<T> { /// This function does not provide any guarantees with respect to the ordering /// of whether contentious readers or writers will acquire the lock first. /// - /// # Failure + /// # Errors /// /// This function will return an error if the RwLock is poisoned. An RwLock /// is poisoned whenever a writer panics while holding an exclusive lock. An @@ -269,7 +269,7 @@ impl<T: ?Sized> RwLock<T> { /// Consumes this `RwLock`, returning the underlying data. /// - /// # Failure + /// # Errors /// /// This function will return an error if the RwLock is poisoned. An RwLock /// is poisoned whenever a writer panics while holding an exclusive lock. An @@ -301,7 +301,7 @@ impl<T: ?Sized> RwLock<T> { /// Since this call borrows the `RwLock` mutably, no actual locking needs to /// take place---the mutable borrow statically guarantees no locks exist. /// - /// # Failure + /// # Errors /// /// This function will return an error if the RwLock is poisoned. An RwLock /// is poisoned whenever a writer panics while holding an exclusive lock. An diff --git a/src/libstd/sys/common/remutex.rs b/src/libstd/sys/common/remutex.rs index 31caa68c4b7..2e2be63c3cb 100644 --- a/src/libstd/sys/common/remutex.rs +++ b/src/libstd/sys/common/remutex.rs @@ -78,7 +78,7 @@ impl<T> ReentrantMutex<T> { /// calling this method already holds the lock, the call shall succeed without /// blocking. /// - /// # Failure + /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be @@ -95,7 +95,7 @@ impl<T> ReentrantMutex<T> { /// /// This function does not block. /// - /// # Failure + /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index e327adfaf89..088f911ed8c 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -70,9 +70,9 @@ 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. +/// 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); @@ -1174,6 +1174,20 @@ impl TokenTree { } (&TokenTree::Token(sp, token::DocComment(name)), _) => { let stripped = strip_doc_comment_decoration(&name.as_str()); + + // Searches for the occurrences of `"#*` and returns the minimum number of `#`s + // required to wrap the text. + let num_of_hashes = stripped.chars().scan(0, |cnt, x| { + *cnt = if x == '"' { + 1 + } else if *cnt != 0 && x == '#' { + *cnt + 1 + } else { + 0 + }; + Some(*cnt) + }).max().unwrap_or(0); + TokenTree::Delimited(sp, Rc::new(Delimited { delim: token::Bracket, open_span: sp, @@ -1181,7 +1195,7 @@ impl TokenTree { token::Plain)), TokenTree::Token(sp, token::Eq), TokenTree::Token(sp, token::Literal( - token::StrRaw(token::intern(&stripped), 0), None))], + token::StrRaw(token::intern(&stripped), num_of_hashes), None))], close_span: sp, })) } diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 9557310f318..9da5e1e3881 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -1064,6 +1064,27 @@ impl CodeMap { span } + /// Return the source callee. + /// + /// Returns None if the supplied span has no expansion trace, + /// else returns the NameAndSpan for the macro definition + /// corresponding to the source callsite. + pub fn source_callee(&self, sp: Span) -> Option<NameAndSpan> { + let mut span = sp; + while let Some(callsite) = self.with_expn_info(span.expn_id, + |ei| ei.map(|ei| ei.call_site.clone())) { + if let Some(_) = self.with_expn_info(callsite.expn_id, + |ei| ei.map(|ei| ei.call_site.clone())) { + span = callsite; + } + else { + return self.with_expn_info(span.expn_id, + |ei| ei.map(|ei| ei.callee.clone())); + } + } + None + } + pub fn span_to_filename(&self, sp: Span) -> FileName { self.lookup_char_pos(sp.lo).file.name.to_string() } diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs index 7e0e17423de..94bbd1bd128 100644 --- a/src/libsyntax/errors/emitter.rs +++ b/src/libsyntax/errors/emitter.rs @@ -516,7 +516,7 @@ impl EmitterWriter { }; let lo = self.cm.lookup_char_pos(sp.lo); let hi = self.cm.lookup_char_pos(sp.hi); - let elide_sp = (lo.line - hi.line) > MAX_SP_LINES; + let elide_sp = (hi.line - lo.line) >= MAX_SP_LINES; let line_num = line.line_index + 1; if !(lo.line <= line_num && hi.line >= line_num) { @@ -1024,7 +1024,7 @@ mod test { \x20 ^ ^\n"; let expect0_end = "dummy.txt: 5 ccccc\n\ - \x20 ...\n\ + dummy.txt: 6 xxxxx\n\ dummy.txt: 7 yyyyy\n\ \x20 ^\n\ \x20 ...\n\ diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b8e5642474c..bff5071b8ec 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2483,8 +2483,7 @@ impl<'a> Parser<'a> { float.trunc() as usize, format!(".{}", fstr.splitn(2, ".").last().unwrap()))); } - err.emit(); - self.abort_if_errors(); + return Err(err); } _ => { @@ -4117,9 +4116,7 @@ impl<'a> Parser<'a> { or did you mean the comma-separated arguments \ 'a, Type?"); err.span_note(mk_sp(span_lo, span_hi), &msg); - err.emit(); - - self.abort_if_errors() + return Err(err); } // First parse types. @@ -5189,8 +5186,7 @@ impl<'a> Parser<'a> { of possibly redeclaring it", paths.name)); } - err.emit(); - self.abort_if_errors(); + return Err(err); } match paths.result { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index e80297eb797..759b16c5738 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1781,11 +1781,8 @@ impl<'a> State<'a> { token::Paren => try!(self.popen()), token::Bracket => try!(word(&mut self.s, "[")), token::Brace => { - // head-ibox, will be closed by bopen() - try!(self.ibox(0)); - // Don't ask me why the regular bopen() does - // more then just opening a brace... - try!(self.bopen()) + try!(self.head("")); + try!(self.bopen()); } } try!(self.print_tts(&m.node.tts)); diff --git a/src/test/compile-fail/resolve-conflict-item-vs-extern-crate.rs b/src/test/compile-fail/resolve-conflict-item-vs-extern-crate.rs index e685353592f..07f80cf03d1 100644 --- a/src/test/compile-fail/resolve-conflict-item-vs-extern-crate.rs +++ b/src/test/compile-fail/resolve-conflict-item-vs-extern-crate.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn std() {} //~ ERROR the name `std` conflicts with an external crate +fn std() {} +mod std {} //~ ERROR the name `std` conflicts with an external crate fn main() { } diff --git a/src/test/pretty/issue-30731.rs b/src/test/pretty/issue-30731.rs new file mode 100644 index 00000000000..cd72ae98433 --- /dev/null +++ b/src/test/pretty/issue-30731.rs @@ -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. +// +// Test pretty printing of macro with braces but without terminating semicolon, +// this used to panic before fix. + +// pretty-compare-only +// pp-exact + +fn main() { b!{ } c } diff --git a/src/test/run-make/execution-engine/test.rs b/src/test/run-make/execution-engine/test.rs index c4d28223c1a..3f9c81eac0b 100644 --- a/src/test/run-make/execution-engine/test.rs +++ b/src/test/run-make/execution-engine/test.rs @@ -242,9 +242,9 @@ fn compile_program(input: &str, sysroot: PathBuf) abort_on_err(driver::phase_3_run_analysis_passes( &sess, &cstore, ast_map, &arenas, &id, - MakeGlobMap::No, |tcx, mir_map, analysis| { + MakeGlobMap::No, |tcx, mir_map, analysis, _| { - let trans = driver::phase_4_translate_to_llvm(tcx, mir_map, analysis); + let trans = driver::phase_4_translate_to_llvm(tcx, mir_map.unwrap(), analysis); let crates = tcx.sess.cstore.used_crates(LinkagePreference::RequireDynamic); diff --git a/src/test/run-pass/macro-doc-raw-str-hashes.rs b/src/test/run-pass/macro-doc-raw-str-hashes.rs new file mode 100644 index 00000000000..ffbe237b74e --- /dev/null +++ b/src/test/run-pass/macro-doc-raw-str-hashes.rs @@ -0,0 +1,39 @@ +// Copyright 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. + +// The number of `#`s used to wrap the documentation comment should differ regarding the content. +// +// Related issue: #27489 + +macro_rules! homura { + ($x:expr, #[$y:meta]) => (assert_eq!($x, stringify!($y))) +} + +fn main() { + homura! { + r#"doc = r" Madoka""#, + /// Madoka + }; + + homura! { + r##"doc = r#" One quote mark: ["]"#"##, + /// One quote mark: ["] + }; + + homura! { + r##"doc = r#" Two quote marks: [""]"#"##, + /// Two quote marks: [""] + }; + + homura! { + r#####"doc = r####" Raw string ending sequences: ["###]"####"#####, + /// Raw string ending sequences: ["###] + }; +} |
