diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /src/test/run-make | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/run-make')
327 files changed, 9261 insertions, 0 deletions
diff --git a/src/test/run-make/const_fn_mir/Makefile b/src/test/run-make/const_fn_mir/Makefile new file mode 100644 index 000000000..2aa0bc9d4 --- /dev/null +++ b/src/test/run-make/const_fn_mir/Makefile @@ -0,0 +1,10 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) main.rs --emit=mir -o "$(TMPDIR)"/dump.mir + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/dump.mir dump.mir +else + $(DIFF) dump.mir "$(TMPDIR)"/dump.mir +endif diff --git a/src/test/run-make/const_fn_mir/dump.mir b/src/test/run-make/const_fn_mir/dump.mir new file mode 100644 index 000000000..ab4084c95 --- /dev/null +++ b/src/test/run-make/const_fn_mir/dump.mir @@ -0,0 +1,42 @@ +// WARNING: This output format is intended for human consumers only +// and is subject to change without notice. Knock yourself out. +fn foo() -> i32 { + let mut _0: i32; // return place in scope 0 at main.rs:4:19: 4:22 + + bb0: { + _0 = const 11_i32; // scope 0 at main.rs:5:5: 5:10 + return; // scope 0 at main.rs:6:2: 6:2 + } +} + +// MIR FOR CTFE +fn foo() -> i32 { + let mut _0: i32; // return place in scope 0 at main.rs:4:19: 4:22 + let mut _1: (i32, bool); // in scope 0 at main.rs:5:5: 5:10 + + bb0: { + _1 = CheckedAdd(const 5_i32, const 6_i32); // scope 0 at main.rs:5:5: 5:10 + assert(!move (_1.1: bool), "attempt to compute `{} + {}`, which would overflow", const 5_i32, const 6_i32) -> bb1; // scope 0 at main.rs:5:5: 5:10 + } + + bb1: { + _0 = move (_1.0: i32); // scope 0 at main.rs:5:5: 5:10 + return; // scope 0 at main.rs:6:2: 6:2 + } +} + +fn main() -> () { + let mut _0: (); // return place in scope 0 at main.rs:8:11: 8:11 + let _1: i32; // in scope 0 at main.rs:9:5: 9:10 + + bb0: { + _1 = foo() -> bb1; // scope 0 at main.rs:9:5: 9:10 + // mir::Constant + // + span: main.rs:9:5: 9:8 + // + literal: Const { ty: fn() -> i32 {foo}, val: Value(<ZST>) } + } + + bb1: { + return; // scope 0 at main.rs:10:2: 10:2 + } +} diff --git a/src/test/run-make/const_fn_mir/main.rs b/src/test/run-make/const_fn_mir/main.rs new file mode 100644 index 000000000..e8552bd28 --- /dev/null +++ b/src/test/run-make/const_fn_mir/main.rs @@ -0,0 +1,10 @@ +// emit-mir +// check-pass + +const fn foo() -> i32 { + 5 + 6 +} + +fn main() { + foo(); +} diff --git a/src/test/run-make/coverage-llvmir/Makefile b/src/test/run-make/coverage-llvmir/Makefile new file mode 100644 index 000000000..7be655053 --- /dev/null +++ b/src/test/run-make/coverage-llvmir/Makefile @@ -0,0 +1,64 @@ +# needs-profiler-support + +# Rust coverage maps support LLVM Coverage Mapping Format versions 5 and 6, +# corresponding with LLVM versions 12 and 13, respectively. +# When upgrading LLVM versions, consider whether to enforce a minimum LLVM +# version during testing, with an additional directive at the top of this file +# that sets, for example: `min-llvm-version: 12.0` + +include ../coverage/coverage_tools.mk + +BASEDIR=../coverage-llvmir + +ifeq ($(UNAME),Darwin) + INSTR_PROF_DATA_SUFFIX=,regular,live_support + DATA_SECTION_PREFIX=__DATA, + LLVM_COV_SECTION_PREFIX=__LLVM_COV, + COMDAT_IF_SUPPORTED= +else + INSTR_PROF_DATA_SUFFIX= + DATA_SECTION_PREFIX= + LLVM_COV_SECTION_PREFIX= + COMDAT_IF_SUPPORTED=, comdat +endif + +DEFINE_INTERNAL=define internal + +ifdef IS_WINDOWS + LLVM_FILECHECK_OPTIONS=\ + -check-prefixes=CHECK,WINDOWS \ + -DDEFINE_INTERNAL='$(DEFINE_INTERNAL)' \ + -DCOMDAT_IF_SUPPORTED='$(COMDAT_IF_SUPPORTED)' \ + -DINSTR_PROF_DATA='.lprfd$$M' \ + -DINSTR_PROF_NAME='.lprfn$$M' \ + -DINSTR_PROF_CNTS='.lprfc$$M' \ + -DINSTR_PROF_VALS='.lprfv$$M' \ + -DINSTR_PROF_VNODES='.lprfnd$$M' \ + -DINSTR_PROF_COVMAP='.lcovmap$$M' \ + -DINSTR_PROF_COVFUN='.lcovfun$$M' \ + -DINSTR_PROF_ORDERFILE='.lorderfile$$M' +else + LLVM_FILECHECK_OPTIONS=\ + -check-prefixes=CHECK \ + -DDEFINE_INTERNAL='$(DEFINE_INTERNAL)' \ + -DCOMDAT_IF_SUPPORTED='$(COMDAT_IF_SUPPORTED)' \ + -DINSTR_PROF_DATA='$(DATA_SECTION_PREFIX)__llvm_prf_data$(INSTR_PROF_DATA_SUFFIX)' \ + -DINSTR_PROF_NAME='$(DATA_SECTION_PREFIX)__llvm_prf_names' \ + -DINSTR_PROF_CNTS='$(DATA_SECTION_PREFIX)__llvm_prf_cnts' \ + -DINSTR_PROF_VALS='$(DATA_SECTION_PREFIX)__llvm_prf_vals' \ + -DINSTR_PROF_VNODES='$(DATA_SECTION_PREFIX)__llvm_prf_vnds' \ + -DINSTR_PROF_COVMAP='$(LLVM_COV_SECTION_PREFIX)__llvm_covmap' \ + -DINSTR_PROF_COVFUN='$(LLVM_COV_SECTION_PREFIX)__llvm_covfun' \ + -DINSTR_PROF_ORDERFILE='$(DATA_SECTION_PREFIX)__llvm_orderfile' +endif + +all: test_llvm_ir + +test_llvm_ir: + # Compile the test program with non-experimental coverage instrumentation, and generate LLVM IR + $(RUSTC) $(BASEDIR)/testprog.rs \ + -Cinstrument-coverage \ + --emit=llvm-ir + + cat "$(TMPDIR)"/testprog.ll | \ + "$(LLVM_FILECHECK)" $(BASEDIR)/filecheck.testprog.txt $(LLVM_FILECHECK_OPTIONS) diff --git a/src/test/run-make/coverage-llvmir/filecheck.testprog.txt b/src/test/run-make/coverage-llvmir/filecheck.testprog.txt new file mode 100644 index 000000000..7a5f21922 --- /dev/null +++ b/src/test/run-make/coverage-llvmir/filecheck.testprog.txt @@ -0,0 +1,50 @@ +# Check for metadata, variables, declarations, and function definitions injected +# into LLVM IR when compiling with -Cinstrument-coverage. + +WINDOWS: $__llvm_profile_runtime_user = comdat any + +CHECK: @__covrec_{{[A-F0-9]+}}u = linkonce_odr hidden constant +CHECK-SAME: section "[[INSTR_PROF_COVFUN]]"[[COMDAT_IF_SUPPORTED]], align 8 + +CHECK: @__llvm_coverage_mapping = private constant +CHECK-SAME: section "[[INSTR_PROF_COVMAP]]", align 8 + +WINDOWS: @__llvm_profile_runtime = external global i32 + +CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global +CHECK-SAME: section "[[INSTR_PROF_CNTS]]"{{.*}}, align 8 + +CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global +CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called +CHECK-SAME: section "[[INSTR_PROF_DATA]]"{{.*}}, align 8 + +CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main = {{private|internal}} global +CHECK-SAME: section "[[INSTR_PROF_CNTS]]"{{.*}}, align 8 + +CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog4main = {{private|internal}} global +CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main +CHECK-SAME: section "[[INSTR_PROF_DATA]]"{{.*}}, align 8 + +CHECK: @__llvm_prf_nm = private constant +CHECK-SAME: section "[[INSTR_PROF_NAME]]", align 1 + +CHECK: @llvm.used = appending global +CHECK-SAME: @__llvm_coverage_mapping +CHECK-SAME: @__llvm_prf_nm +CHECK-SAME: section "llvm.metadata" + +CHECK: [[DEFINE_INTERNAL]] { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} { +CHECK-NEXT: start: +CHECK-NOT: [[DEFINE_INTERNAL]] +CHECK: %pgocount = load i64, {{i64\*|ptr}} +CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called, + +CHECK: declare void @llvm.instrprof.increment({{i8\*|ptr}}, i64, i32, i32) #[[LLVM_INSTRPROF_INCREMENT_ATTR:[0-9]+]] + +WINDOWS: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat { +WINDOWS-NEXT: %1 = load i32, {{i32\*|ptr}} @__llvm_profile_runtime +WINDOWS-NEXT: ret i32 %1 +WINDOWS-NEXT: } + +CHECK: attributes #[[LLVM_INSTRPROF_INCREMENT_ATTR]] = { nounwind } +WINDOWS: attributes #[[LLVM_PROFILE_RUNTIME_USER_ATTR]] = { noinline } diff --git a/src/test/run-make/coverage-llvmir/testprog.rs b/src/test/run-make/coverage-llvmir/testprog.rs new file mode 100644 index 000000000..358c25677 --- /dev/null +++ b/src/test/run-make/coverage-llvmir/testprog.rs @@ -0,0 +1,38 @@ +pub fn will_be_called() -> &'static str { + let val = "called"; + println!("{}", val); + val +} + +pub fn will_not_be_called() -> bool { + println!("should not have been called"); + false +} + +pub fn print<T>(left: &str, value: T, right: &str) +where + T: std::fmt::Display, +{ + println!("{}{}{}", left, value, right); +} + +pub fn wrap_with<F, T>(inner: T, should_wrap: bool, wrapper: F) +where + F: FnOnce(&T) +{ + if should_wrap { + wrapper(&inner) + } +} + +fn main() { + let less = 1; + let more = 100; + + if less < more { + wrap_with(will_be_called(), less < more, |inner| print(" ***", inner, "*** ")); + wrap_with(will_be_called(), more < less, |inner| print(" ***", inner, "*** ")); + } else { + wrap_with(will_not_be_called(), true, |inner| print("wrapped result is: ", inner, "")); + } +} diff --git a/src/test/run-make/coverage-reports/Makefile b/src/test/run-make/coverage-reports/Makefile new file mode 100644 index 000000000..4e75672f2 --- /dev/null +++ b/src/test/run-make/coverage-reports/Makefile @@ -0,0 +1,180 @@ +# needs-profiler-support +# ignore-windows-gnu + +# Rust coverage maps support LLVM Coverage Mapping Format versions 5 and 6, +# corresponding with LLVM versions 12 and 13, respectively. +# When upgrading LLVM versions, consider whether to enforce a minimum LLVM +# version during testing, with an additional directive at the top of this file +# that sets, for example: `min-llvm-version: 12.0` + +# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works +# properly. Since we only have GCC on the CI ignore the test for now. + +include ../coverage/coverage_tools.mk + +BASEDIR=../coverage-reports +SOURCEDIR=../coverage + +# The `llvm-cov show` flag `--debug`, used to generate the `counters` output files, is only +# enabled if LLVM assertions are enabled. This requires Rust config `llvm/optimize` and not +# `llvm/release_debuginfo`. Note that some CI builds disable debug assertions (by setting +# `NO_LLVM_ASSERTIONS=1`), so the tests must still pass even if the `--debug` flag is +# not supported. (Note that `counters` files are only produced in the `$(TMPDIR)` +# directory, for inspection and debugging support. They are *not* copied to `expected_*` +# files when `--bless`ed.) +LLVM_COV_DEBUG := $(shell \ + "$(LLVM_BIN_DIR)"/llvm-cov show --debug 2>&1 | \ + grep -q "Unknown command line argument '--debug'"; \ + echo $$?) +ifeq ($(LLVM_COV_DEBUG), 1) +DEBUG_FLAG=--debug +endif + +# FIXME(richkadel): I'm adding `--ignore-filename-regex=` line(s) for specific test(s) that produce +# `llvm-cov` results for multiple files (for example `uses_crate.rs` and `used_crate/mod.rs`) as a +# workaround for two problems causing tests to fail on Windows: +# +# 1. When multiple files appear in the `llvm-cov show` results, each file's coverage results can +# appear in different a different order. Whether this is random or, somehow, platform-specific, +# the Windows output flips the order of the files, compared to Linux. In the `uses_crate.rs` +# test, the only test-unique (interesting) results we care about are the results for only one +# of the two files, `mod/uses_crate.rs`, so the workaround is to ignore all but this one file. +# In the future, we may want a more sophisticated solution that splits apart `llvm-cov show` +# results into separate results files for each result (taking care not to create new file +# paths that might be too long for Windows MAX_PATH limits when creating these new sub-results, +# as well). +# 2. When multiple files appear in the `llvm-cov show` results, the results for each file are +# prefixed with their filename, including platform-specific path separators (`\` for Windows, +# and `/` everywhere else). This could be filtered or normalized of course, but by ignoring +# coverage results for all but one of the file, the filenames are no longer included anyway. +# If this changes (if/when we decide to support `llvm-cov show` results for multiple files), +# the file path separator differences may need to be addressed. +# +# Since this is only a workaround, I decided to implement the override by adding an option for +# each file to be ignored, using a `--ignore-filename-regex=` entry for each one, rather than +# implement some more sophisticated solution with a new custom test directive in the test file +# itself (similar to `expect-exit-status`) because that would add a lot of complexity and still +# be a workaround, with the same result, with no benefit. +# +# Yes these `--ignore-filename-regex=` options are included in all invocations of `llvm-cov show` +# for now, but it is effectively ignored for all tests that don't include this file anyway. +# +# (Note that it's also possible the `_counters.<test>.txt` and `<test>.json` files (if generated) +# may order results from multiple files inconsistently, which might also have to be accomodated +# if and when we allow `llvm-cov` to produce results for multiple files. Note, the path separators +# appear to be normalized to `/` in those files, thankfully.) +LLVM_COV_IGNORE_FILES=\ + --ignore-filename-regex='(uses_crate.rs|uses_inline_crate.rs|unused_mod.rs)' + +all: $(patsubst $(SOURCEDIR)/lib/%.rs,%,$(wildcard $(SOURCEDIR)/lib/*.rs)) $(patsubst $(SOURCEDIR)/%.rs,%,$(wildcard $(SOURCEDIR)/*.rs)) + +# Ensure there are no `expected` results for tests that may have been removed or renamed +.PHONY: clear_expected_if_blessed +clear_expected_if_blessed: +ifdef RUSTC_BLESS_TEST + rm -f expected_* +endif + +-include clear_expected_if_blessed + +%: $(SOURCEDIR)/lib/%.rs + # Compile the test library with coverage instrumentation + $(RUSTC) $(SOURCEDIR)/lib/$@.rs \ + $$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/lib/$@.rs ) \ + --crate-type rlib -Cinstrument-coverage + +%: $(SOURCEDIR)/%.rs + # Compile the test program with coverage instrumentation + $(RUSTC) $(SOURCEDIR)/$@.rs \ + $$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/$@.rs ) \ + -L "$(TMPDIR)" -Cinstrument-coverage + + # Run it in order to generate some profiling data, + # with `LLVM_PROFILE_FILE=<profdata_file>` environment variable set to + # output the coverage stats for this run. + LLVM_PROFILE_FILE="$(TMPDIR)"/$@.profraw \ + $(call RUN,$@) || \ + ( \ + status=$$?; \ + grep -q "^\/\/ expect-exit-status-$$status" $(SOURCEDIR)/$@.rs || \ + ( >&2 echo "program exited with an unexpected exit status: $$status"; \ + false \ + ) \ + ) + + # Run it through rustdoc as well to cover doctests. + # `%p` is the pid, and `%m` the binary signature. We suspect that the pid alone + # might result in overwritten files and failed tests, as rustdoc spawns each + # doctest as its own process, so make sure the filename is as unique as possible. + LLVM_PROFILE_FILE="$(TMPDIR)"/$@-%p-%m.profraw \ + $(RUSTDOC) --crate-name workaround_for_79771 --test $(SOURCEDIR)/$@.rs \ + $$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/$@.rs ) \ + -L "$(TMPDIR)" -Cinstrument-coverage \ + -Z unstable-options --persist-doctests=$(TMPDIR)/rustdoc-$@ + + # Postprocess the profiling data so it can be used by the llvm-cov tool + "$(LLVM_BIN_DIR)"/llvm-profdata merge --sparse \ + "$(TMPDIR)"/$@*.profraw \ + -o "$(TMPDIR)"/$@.profdata + + # Generate a coverage report using `llvm-cov show`. + "$(LLVM_BIN_DIR)"/llvm-cov show \ + $(DEBUG_FLAG) \ + $(LLVM_COV_IGNORE_FILES) \ + --compilation-dir=. \ + --Xdemangler="$(RUST_DEMANGLER)" \ + --show-line-counts-or-regions \ + --instr-profile="$(TMPDIR)"/$@.profdata \ + $(call BIN,"$(TMPDIR)"/$@) \ + $$( \ + for file in $(TMPDIR)/rustdoc-$@/*/rust_out; do \ + [ -x "$$file" ] && printf "%s %s " -object $$file; \ + done \ + ) \ + 2> "$(TMPDIR)"/show_coverage_stderr.$@.txt \ + | "$(PYTHON)" $(BASEDIR)/normalize_paths.py \ + > "$(TMPDIR)"/actual_show_coverage.$@.txt || \ + ( status=$$? ; \ + >&2 cat "$(TMPDIR)"/show_coverage_stderr.$@.txt ; \ + exit $$status \ + ) + +ifdef DEBUG_FLAG + # The first line (beginning with "Args:" contains hard-coded, build-specific + # file paths. Strip that line and keep the remaining lines with counter debug + # data. + tail -n +2 "$(TMPDIR)"/show_coverage_stderr.$@.txt \ + > "$(TMPDIR)"/actual_show_coverage_counters.$@.txt +endif + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/actual_show_coverage.$@.txt \ + expected_show_coverage.$@.txt +else + # Compare the show coverage output (`--bless` refreshes `typical` files). + # + # FIXME(richkadel): None of the Rust test source samples have the + # `// ignore-llvm-cov-show-diffs` anymore. This directive exists to work around a limitation + # with `llvm-cov show`. When reporting coverage for multiple instantiations of a generic function, + # with different type substitutions, `llvm-cov show` prints these in a non-deterministic order, + # breaking the `diff` comparision. + # + # A partial workaround is implemented below, with `diff --ignore-matching-lines=RE` + # to ignore each line prefixing each generic instantiation coverage code region. + # + # This workaround only works if the coverage counts are identical across all reported + # instantiations. If there is no way to ensure this, you may need to apply the + # `// ignore-llvm-cov-show-diffs` directive, and check for differences using the + # `.json` files to validate that results have not changed. (Until then, the JSON + # files are redundant, so there is no need to generate `expected_*.json` files or + # compare actual JSON results.) + + $(DIFF) --ignore-matching-lines='^ | .*::<.*>.*:$$' --ignore-matching-lines='^ | <.*>::.*:$$' \ + expected_show_coverage.$@.txt "$(TMPDIR)"/actual_show_coverage.$@.txt || \ + ( grep -q '^\/\/ ignore-llvm-cov-show-diffs' $(SOURCEDIR)/$@.rs && \ + >&2 echo 'diff failed, but suppressed with `// ignore-llvm-cov-show-diffs` in $(SOURCEDIR)/$@.rs' \ + ) || \ + ( >&2 echo 'diff failed, and not suppressed without `// ignore-llvm-cov-show-diffs` in $(SOURCEDIR)/$@.rs'; \ + false \ + ) +endif diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.abort.txt b/src/test/run-make/coverage-reports/expected_show_coverage.abort.txt new file mode 100644 index 000000000..00f46f42a --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.abort.txt @@ -0,0 +1,69 @@ + 1| |#![feature(c_unwind)] + 2| |#![allow(unused_assignments)] + 3| | + 4| 12|extern "C" fn might_abort(should_abort: bool) { + 5| 12| if should_abort { + 6| 0| println!("aborting..."); + 7| 0| panic!("panics and aborts"); + 8| 12| } else { + 9| 12| println!("Don't Panic"); + 10| 12| } + 11| 12|} + 12| | + 13| 1|fn main() -> Result<(), u8> { + 14| 1| let mut countdown = 10; + 15| 11| while countdown > 0 { + 16| 10| if countdown < 5 { + 17| 4| might_abort(false); + 18| 6| } + 19| | // See discussion (below the `Notes` section) on coverage results for the closing brace. + 20| 10| if countdown < 5 { might_abort(false); } // Counts for different regions on one line. + ^4 ^6 + 21| | // For the following example, the closing brace is the last character on the line. + 22| | // This shows the character after the closing brace is highlighted, even if that next + 23| | // character is a newline. + 24| 10| if countdown < 5 { might_abort(false); } + ^4 ^6 + 25| 10| countdown -= 1; + 26| | } + 27| 1| Ok(()) + 28| 1|} + 29| | + 30| |// Notes: + 31| |// 1. Compare this program and its coverage results to those of the similar tests + 32| |// `panic_unwind.rs` and `try_error_result.rs`. + 33| |// 2. This test confirms the coverage generated when a program includes `TerminatorKind::Abort`. + 34| |// 3. The test does not invoke the abort. By executing to a successful completion, the coverage + 35| |// results show where the program did and did not execute. + 36| |// 4. If the program actually aborted, the coverage counters would not be saved (which "works as + 37| |// intended"). Coverage results would show no executed coverage regions. + 38| |// 6. If `should_abort` is `true` and the program aborts, the program exits with a `132` status + 39| |// (on Linux at least). + 40| | + 41| |/* + 42| | + 43| |Expect the following coverage results: + 44| | + 45| |```text + 46| | 16| 11| while countdown > 0 { + 47| | 17| 10| if countdown < 5 { + 48| | 18| 4| might_abort(false); + 49| | 19| 6| } + 50| |``` + 51| | + 52| |This is actually correct. + 53| | + 54| |The condition `countdown < 5` executed 10 times (10 loop iterations). + 55| | + 56| |It evaluated to `true` 4 times, and executed the `might_abort()` call. + 57| | + 58| |It skipped the body of the `might_abort()` call 6 times. If an `if` does not include an explicit + 59| |`else`, the coverage implementation injects a counter, at the character immediately after the `if`s + 60| |closing brace, to count the "implicit" `else`. This is the only way to capture the coverage of the + 61| |non-true condition. + 62| | + 63| |As another example of why this is important, say the condition was `countdown < 50`, which is always + 64| |`true`. In that case, we wouldn't have a test for what happens if `might_abort()` is not called. + 65| |The closing brace would have a count of `0`, highlighting the missed coverage. + 66| |*/ + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.assert.txt b/src/test/run-make/coverage-reports/expected_show_coverage.assert.txt new file mode 100644 index 000000000..405688806 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.assert.txt @@ -0,0 +1,34 @@ + 1| |#![allow(unused_assignments)] + 2| |// expect-exit-status-101 + 3| | + 4| 4|fn might_fail_assert(one_plus_one: u32) { + 5| 4| println!("does 1 + 1 = {}?", one_plus_one); + 6| 4| assert_eq!(1 + 1, one_plus_one, "the argument was wrong"); + ^1 + 7| 3|} + 8| | + 9| 1|fn main() -> Result<(),u8> { + 10| 1| let mut countdown = 10; + 11| 11| while countdown > 0 { + 12| 11| if countdown == 1 { + 13| 1| might_fail_assert(3); + 14| 10| } else if countdown < 5 { + 15| 3| might_fail_assert(2); + 16| 6| } + 17| 10| countdown -= 1; + 18| | } + 19| 0| Ok(()) + 20| 0|} + 21| | + 22| |// Notes: + 23| |// 1. Compare this program and its coverage results to those of the very similar test + 24| |// `panic_unwind.rs`, and similar tests `abort.rs` and `try_error_result.rs`. + 25| |// 2. This test confirms the coverage generated when a program passes or fails an `assert!()` or + 26| |// related `assert_*!()` macro. + 27| |// 3. Notably, the `assert` macros *do not* generate `TerminatorKind::Assert`. The macros produce + 28| |// conditional expressions, `TerminatorKind::SwitchInt` branches, and a possible call to + 29| |// `begin_panic_fmt()` (that begins a panic unwind, if the assertion test fails). + 30| |// 4. `TerminatoKind::Assert` is, however, also present in the MIR generated for this test + 31| |// (and in many other coverage tests). The `Assert` terminator is typically generated by the + 32| |// Rust compiler to check for runtime failures, such as numeric overflows. + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.async.txt b/src/test/run-make/coverage-reports/expected_show_coverage.async.txt new file mode 100644 index 000000000..2f69adbd8 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.async.txt @@ -0,0 +1,139 @@ + 1| |#![allow(unused_assignments, dead_code)] + 2| | + 3| |// compile-flags: --edition=2018 -C opt-level=1 + 4| | + 5| 1|async fn c(x: u8) -> u8 { + 6| 1| if x == 8 { + 7| 1| 1 + 8| | } else { + 9| 0| 0 + 10| | } + 11| 1|} + 12| | + 13| 0|async fn d() -> u8 { 1 } + 14| | + 15| 0|async fn e() -> u8 { 1 } // unused function; executor does not block on `g()` + 16| | + 17| 1|async fn f() -> u8 { 1 } + 18| | + 19| 0|async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()` + 20| | + 21| 1|pub async fn g(x: u8) { + 22| 0| match x { + 23| 0| y if e().await == y => (), + 24| 0| y if f().await == y => (), + 25| 0| _ => (), + 26| | } + 27| 0|} + 28| | + 29| 1|async fn h(x: usize) { // The function signature is counted when called, but the body is not + 30| 0| // executed (not awaited) so the open brace has a `0` count (at least when + 31| 0| // displayed with `llvm-cov show` in color-mode). + 32| 0| match x { + 33| 0| y if foo().await[y] => (), + 34| 0| _ => (), + 35| | } + 36| 0|} + 37| | + 38| 1|async fn i(x: u8) { // line coverage is 1, but there are 2 regions: + 39| 1| // (a) the function signature, counted when the function is called; and + 40| 1| // (b) the open brace for the function body, counted once when the body is + 41| 1| // executed asynchronously. + 42| 1| match x { + 43| 1| y if c(x).await == y + 1 => { d().await; } + ^0 ^0 ^0 ^0 + 44| 1| y if f().await == y + 1 => (), + ^0 ^0 ^0 + 45| 1| _ => (), + 46| | } + 47| 1|} + 48| | + 49| 1|fn j(x: u8) { + 50| 1| // non-async versions of `c()`, `d()`, and `f()` to make it similar to async `i()`. + 51| 1| fn c(x: u8) -> u8 { + 52| 1| if x == 8 { + 53| 1| 1 // This line appears covered, but the 1-character expression span covering the `1` + ^0 + 54| 1| // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because + 55| 1| // `fn j()` executes the open brace for the funciton body, followed by the function's + 56| 1| // first executable statement, `match x`. Inner function declarations are not + 57| 1| // "visible" to the MIR for `j()`, so the code region counts all lines between the + 58| 1| // open brace and the first statement as executed, which is, in a sense, true. + 59| 1| // `llvm-cov show` overcomes this kind of situation by showing the actual counts + 60| 1| // of the enclosed coverages, (that is, the `1` expression was not executed, and + 61| 1| // accurately displays a `0`). + 62| 1| } else { + 63| 1| 0 + 64| 1| } + 65| 1| } + 66| 1| fn d() -> u8 { 1 } // inner function is defined in-line, but the function is not executed + ^0 + 67| 1| fn f() -> u8 { 1 } + 68| 1| match x { + 69| 1| y if c(x) == y + 1 => { d(); } + ^0 ^0 + 70| 1| y if f() == y + 1 => (), + ^0 ^0 + 71| 1| _ => (), + 72| | } + 73| 1|} + 74| | + 75| 0|fn k(x: u8) { // unused function + 76| 0| match x { + 77| 0| 1 => (), + 78| 0| 2 => (), + 79| 0| _ => (), + 80| | } + 81| 0|} + 82| | + 83| 1|fn l(x: u8) { + 84| 1| match x { + 85| 0| 1 => (), + 86| 0| 2 => (), + 87| 1| _ => (), + 88| | } + 89| 1|} + 90| | + 91| 1|async fn m(x: u8) -> u8 { x - 1 } + ^0 + 92| | + 93| 1|fn main() { + 94| 1| let _ = g(10); + 95| 1| let _ = h(9); + 96| 1| let mut future = Box::pin(i(8)); + 97| 1| j(7); + 98| 1| l(6); + 99| 1| let _ = m(5); + 100| 1| executor::block_on(future.as_mut()); + 101| 1|} + 102| | + 103| |mod executor { + 104| | use core::{ + 105| | future::Future, + 106| | pin::Pin, + 107| | task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + 108| | }; + 109| | + 110| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output { + 111| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) }; + 112| 1| use std::hint::unreachable_unchecked; + 113| 1| static VTABLE: RawWakerVTable = RawWakerVTable::new( + 114| 1| |_| unsafe { unreachable_unchecked() }, // clone + ^0 + 115| 1| |_| unsafe { unreachable_unchecked() }, // wake + ^0 + 116| 1| |_| unsafe { unreachable_unchecked() }, // wake_by_ref + ^0 + 117| 1| |_| (), + 118| 1| ); + 119| 1| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + 120| 1| let mut context = Context::from_waker(&waker); + 121| | + 122| | loop { + 123| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + 124| 1| break val; + 125| 0| } + 126| | } + 127| 1| } + 128| |} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.async2.txt b/src/test/run-make/coverage-reports/expected_show_coverage.async2.txt new file mode 100644 index 000000000..dc06a485a --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.async2.txt @@ -0,0 +1,116 @@ + 1| |// compile-flags: --edition=2018 + 2| | + 3| |use core::{ + 4| | future::Future, + 5| | marker::Send, + 6| | pin::Pin, + 7| |}; + 8| | + 9| 1|fn non_async_func() { + 10| 1| println!("non_async_func was covered"); + 11| 1| let b = true; + 12| 1| if b { + 13| 1| println!("non_async_func println in block"); + 14| 1| } + ^0 + 15| 1|} + 16| | + 17| | + 18| | + 19| | + 20| 1|async fn async_func() { + 21| 1| println!("async_func was covered"); + 22| 1| let b = true; + 23| 1| if b { + 24| 1| println!("async_func println in block"); + 25| 1| } + ^0 + 26| 1|} + 27| | + 28| | + 29| | + 30| | + 31| 1|async fn async_func_just_println() { + 32| 1| println!("async_func_just_println was covered"); + 33| 1|} + 34| | + 35| 1|fn main() { + 36| 1| println!("codecovsample::main"); + 37| 1| + 38| 1| non_async_func(); + 39| 1| + 40| 1| executor::block_on(async_func()); + 41| 1| executor::block_on(async_func_just_println()); + 42| 1|} + 43| | + 44| |mod executor { + 45| | use core::{ + 46| | future::Future, + 47| | pin::Pin, + 48| | task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + 49| | }; + 50| | + 51| 2| pub fn block_on<F: Future>(mut future: F) -> F::Output { + 52| 2| let mut future = unsafe { Pin::new_unchecked(&mut future) }; + 53| 2| use std::hint::unreachable_unchecked; + 54| 2| static VTABLE: RawWakerVTable = RawWakerVTable::new( + 55| 2| |_| unsafe { unreachable_unchecked() }, // clone + ^0 + 56| 2| |_| unsafe { unreachable_unchecked() }, // wake + ^0 + 57| 2| |_| unsafe { unreachable_unchecked() }, // wake_by_ref + ^0 + 58| 2| |_| (), + 59| 2| ); + 60| 2| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + 61| 2| let mut context = Context::from_waker(&waker); + 62| | + 63| | loop { + 64| 2| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + 65| 2| break val; + 66| 0| } + 67| | } + 68| 2| } + ------------------ + | async2::executor::block_on::<core::future::from_generator::GenFuture<async2::async_func::{closure#0}>>: + | 51| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output { + | 52| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) }; + | 53| 1| use std::hint::unreachable_unchecked; + | 54| 1| static VTABLE: RawWakerVTable = RawWakerVTable::new( + | 55| 1| |_| unsafe { unreachable_unchecked() }, // clone + | 56| 1| |_| unsafe { unreachable_unchecked() }, // wake + | 57| 1| |_| unsafe { unreachable_unchecked() }, // wake_by_ref + | 58| 1| |_| (), + | 59| 1| ); + | 60| 1| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + | 61| 1| let mut context = Context::from_waker(&waker); + | 62| | + | 63| | loop { + | 64| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + | 65| 1| break val; + | 66| 0| } + | 67| | } + | 68| 1| } + ------------------ + | async2::executor::block_on::<core::future::from_generator::GenFuture<async2::async_func_just_println::{closure#0}>>: + | 51| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output { + | 52| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) }; + | 53| 1| use std::hint::unreachable_unchecked; + | 54| 1| static VTABLE: RawWakerVTable = RawWakerVTable::new( + | 55| 1| |_| unsafe { unreachable_unchecked() }, // clone + | 56| 1| |_| unsafe { unreachable_unchecked() }, // wake + | 57| 1| |_| unsafe { unreachable_unchecked() }, // wake_by_ref + | 58| 1| |_| (), + | 59| 1| ); + | 60| 1| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + | 61| 1| let mut context = Context::from_waker(&waker); + | 62| | + | 63| | loop { + | 64| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + | 65| 1| break val; + | 66| 0| } + | 67| | } + | 68| 1| } + ------------------ + 69| |} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.closure.txt b/src/test/run-make/coverage-reports/expected_show_coverage.closure.txt new file mode 100644 index 000000000..09ad276aa --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.closure.txt @@ -0,0 +1,222 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| |// compile-flags: -C opt-level=2 # fix described in rustc_middle/mir/mono.rs + 3| 1|fn main() { + 4| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 5| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 6| 1| // dependent conditions. + 7| 1| let is_true = std::env::args().len() == 1; + 8| 1| let is_false = ! is_true; + 9| 1| + 10| 1| let mut some_string = Some(String::from("the string content")); + 11| 1| println!( + 12| 1| "The string or alt: {}" + 13| 1| , + 14| 1| some_string + 15| 1| . + 16| 1| unwrap_or_else + 17| 1| ( + 18| 1| || + 19| 0| { + 20| 0| let mut countdown = 0; + 21| 0| if is_false { + 22| 0| countdown = 10; + 23| 0| } + 24| 0| "alt string 1".to_owned() + 25| 1| } + 26| 1| ) + 27| 1| ); + 28| 1| + 29| 1| some_string = Some(String::from("the string content")); + 30| 1| let + 31| 1| a + 32| 1| = + 33| 1| || + 34| 0| { + 35| 0| let mut countdown = 0; + 36| 0| if is_false { + 37| 0| countdown = 10; + 38| 0| } + 39| 0| "alt string 2".to_owned() + 40| | }; + 41| 1| println!( + 42| 1| "The string or alt: {}" + 43| 1| , + 44| 1| some_string + 45| 1| . + 46| 1| unwrap_or_else + 47| 1| ( + 48| 1| a + 49| 1| ) + 50| 1| ); + 51| 1| + 52| 1| some_string = None; + 53| 1| println!( + 54| 1| "The string or alt: {}" + 55| 1| , + 56| 1| some_string + 57| 1| . + 58| 1| unwrap_or_else + 59| 1| ( + 60| 1| || + 61| 1| { + 62| 1| let mut countdown = 0; + 63| 1| if is_false { + 64| 0| countdown = 10; + 65| 1| } + 66| 1| "alt string 3".to_owned() + 67| 1| } + 68| 1| ) + 69| 1| ); + 70| 1| + 71| 1| some_string = None; + 72| 1| let + 73| 1| a + 74| 1| = + 75| 1| || + 76| 1| { + 77| 1| let mut countdown = 0; + 78| 1| if is_false { + 79| 0| countdown = 10; + 80| 1| } + 81| 1| "alt string 4".to_owned() + 82| | }; + 83| 1| println!( + 84| 1| "The string or alt: {}" + 85| 1| , + 86| 1| some_string + 87| 1| . + 88| 1| unwrap_or_else + 89| 1| ( + 90| 1| a + 91| 1| ) + 92| 1| ); + 93| 1| + 94| 1| let + 95| 1| quote_closure + 96| 1| = + 97| 1| |val| + 98| 5| { + 99| 5| let mut countdown = 0; + 100| 5| if is_false { + 101| 0| countdown = 10; + 102| 5| } + 103| 5| format!("'{}'", val) + 104| | }; + 105| 1| println!( + 106| 1| "Repeated, quoted string: {:?}" + 107| 1| , + 108| 1| std::iter::repeat("repeat me") + 109| 1| .take(5) + 110| 1| .map + 111| 1| ( + 112| 1| quote_closure + 113| 1| ) + 114| 1| .collect::<Vec<_>>() + 115| 1| ); + 116| 1| + 117| 1| let + 118| 1| _unused_closure + 119| | = + 120| | | + 121| | mut countdown + 122| | | + 123| 0| { + 124| 0| if is_false { + 125| 0| countdown = 10; + 126| 0| } + 127| 0| "closure should be unused".to_owned() + 128| | }; + 129| | + 130| 1| let mut countdown = 10; + 131| 1| let _short_unused_closure = | _unused_arg: u8 | countdown += 1; + ^0 + 132| | + 133| | + 134| 1| let short_used_covered_closure_macro = | used_arg: u8 | println!("called"); + 135| 1| let short_used_not_covered_closure_macro = | used_arg: u8 | println!("not called"); + ^0 + 136| 1| let _short_unused_closure_macro = | _unused_arg: u8 | println!("not called"); + ^0 + 137| | + 138| | + 139| | + 140| | + 141| 1| let _short_unused_closure_block = | _unused_arg: u8 | { println!("not called") }; + ^0 + 142| | + 143| 1| let _shortish_unused_closure = | _unused_arg: u8 | { + 144| 0| println!("not called") + 145| 0| }; + 146| | + 147| 1| let _as_short_unused_closure = | + 148| | _unused_arg: u8 + 149| 0| | { println!("not called") }; + 150| | + 151| 1| let _almost_as_short_unused_closure = | + 152| | _unused_arg: u8 + 153| 0| | { println!("not called") } + 154| | ; + 155| | + 156| | + 157| | + 158| | + 159| | + 160| 1| let _short_unused_closure_line_break_no_block = | _unused_arg: u8 | + 161| 0|println!("not called") + 162| | ; + 163| | + 164| 1| let _short_unused_closure_line_break_no_block2 = + 165| | | _unused_arg: u8 | + 166| 0| println!( + 167| 0| "not called" + 168| 0| ) + 169| | ; + 170| | + 171| 1| let short_used_not_covered_closure_line_break_no_block_embedded_branch = + 172| | | _unused_arg: u8 | + 173| 0| println!( + 174| 0| "not called: {}", + 175| 0| if is_true { "check" } else { "me" } + 176| | ) + 177| | ; + 178| | + 179| 1| let short_used_not_covered_closure_line_break_block_embedded_branch = + 180| 1| | _unused_arg: u8 | + 181| 0| { + 182| 0| println!( + 183| 0| "not called: {}", + 184| 0| if is_true { "check" } else { "me" } + 185| | ) + 186| | } + 187| | ; + 188| | + 189| 1| let short_used_covered_closure_line_break_no_block_embedded_branch = + 190| 1| | _unused_arg: u8 | + 191| 1| println!( + 192| 1| "not called: {}", + 193| 1| if is_true { "check" } else { "me" } + ^0 + 194| | ) + 195| | ; + 196| | + 197| 1| let short_used_covered_closure_line_break_block_embedded_branch = + 198| 1| | _unused_arg: u8 | + 199| 1| { + 200| 1| println!( + 201| 1| "not called: {}", + 202| 1| if is_true { "check" } else { "me" } + ^0 + 203| | ) + 204| | } + 205| | ; + 206| | + 207| 1| if is_false { + 208| 0| short_used_not_covered_closure_macro(0); + 209| 0| short_used_not_covered_closure_line_break_no_block_embedded_branch(0); + 210| 0| short_used_not_covered_closure_line_break_block_embedded_branch(0); + 211| 1| } + 212| 1| short_used_covered_closure_macro(0); + 213| 1| short_used_covered_closure_line_break_no_block_embedded_branch(0); + 214| 1| short_used_covered_closure_line_break_block_embedded_branch(0); + 215| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.closure_macro.txt b/src/test/run-make/coverage-reports/expected_show_coverage.closure_macro.txt new file mode 100644 index 000000000..87f701476 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.closure_macro.txt @@ -0,0 +1,42 @@ + 1| |// compile-flags: --edition=2018 + 2| |#![feature(no_coverage)] + 3| | + 4| |macro_rules! bail { + 5| | ($msg:literal $(,)?) => { + 6| | if $msg.len() > 0 { + 7| | println!("no msg"); + 8| | } else { + 9| | println!($msg); + 10| | } + 11| | return Err(String::from($msg)); + 12| | }; + 13| |} + 14| | + 15| |macro_rules! on_error { + 16| | ($value:expr, $error_message:expr) => { + 17| | $value.or_else(|e| { // FIXME(85000): no coverage in closure macros + 18| | let message = format!($error_message, e); + 19| | if message.len() > 0 { + 20| | println!("{}", message); + 21| | Ok(String::from("ok")) + 22| | } else { + 23| | bail!("error"); + 24| | } + 25| | }) + 26| | }; + 27| |} + 28| | + 29| 1|fn load_configuration_files() -> Result<String, String> { + 30| 1| Ok(String::from("config")) + 31| 1|} + 32| | + 33| 1|pub fn main() -> Result<(), String> { + 34| 1| println!("Starting service"); + 35| 1| let config = on_error!(load_configuration_files(), "Error loading configs: {}")?; + ^0 + 36| | + 37| 1| let startup_delay_duration = String::from("arg"); + 38| 1| let _ = (config, startup_delay_duration); + 39| 1| Ok(()) + 40| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.closure_macro_async.txt b/src/test/run-make/coverage-reports/expected_show_coverage.closure_macro_async.txt new file mode 100644 index 000000000..2b5418132 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.closure_macro_async.txt @@ -0,0 +1,83 @@ + 1| |// compile-flags: --edition=2018 + 2| |#![feature(no_coverage)] + 3| | + 4| |macro_rules! bail { + 5| | ($msg:literal $(,)?) => { + 6| | if $msg.len() > 0 { + 7| | println!("no msg"); + 8| | } else { + 9| | println!($msg); + 10| | } + 11| | return Err(String::from($msg)); + 12| | }; + 13| |} + 14| | + 15| |macro_rules! on_error { + 16| | ($value:expr, $error_message:expr) => { + 17| | $value.or_else(|e| { // FIXME(85000): no coverage in closure macros + 18| | let message = format!($error_message, e); + 19| | if message.len() > 0 { + 20| | println!("{}", message); + 21| | Ok(String::from("ok")) + 22| | } else { + 23| | bail!("error"); + 24| | } + 25| | }) + 26| | }; + 27| |} + 28| | + 29| 1|fn load_configuration_files() -> Result<String, String> { + 30| 1| Ok(String::from("config")) + 31| 1|} + 32| | + 33| 1|pub async fn test() -> Result<(), String> { + 34| 1| println!("Starting service"); + 35| 1| let config = on_error!(load_configuration_files(), "Error loading configs: {}")?; + ^0 + 36| | + 37| 1| let startup_delay_duration = String::from("arg"); + 38| 1| let _ = (config, startup_delay_duration); + 39| 1| Ok(()) + 40| 1|} + 41| | + 42| |#[no_coverage] + 43| |fn main() { + 44| | executor::block_on(test()); + 45| |} + 46| | + 47| |mod executor { + 48| | use core::{ + 49| | future::Future, + 50| | pin::Pin, + 51| | task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + 52| | }; + 53| | + 54| | #[no_coverage] + 55| | pub fn block_on<F: Future>(mut future: F) -> F::Output { + 56| | let mut future = unsafe { Pin::new_unchecked(&mut future) }; + 57| | use std::hint::unreachable_unchecked; + 58| | static VTABLE: RawWakerVTable = RawWakerVTable::new( + 59| | + 60| | #[no_coverage] + 61| | |_| unsafe { unreachable_unchecked() }, // clone + 62| | + 63| | #[no_coverage] + 64| | |_| unsafe { unreachable_unchecked() }, // wake + 65| | + 66| | #[no_coverage] + 67| | |_| unsafe { unreachable_unchecked() }, // wake_by_ref + 68| | + 69| | #[no_coverage] + 70| | |_| (), + 71| | ); + 72| | let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + 73| | let mut context = Context::from_waker(&waker); + 74| | + 75| | loop { + 76| | if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + 77| | break val; + 78| | } + 79| | } + 80| | } + 81| |} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.conditions.txt b/src/test/run-make/coverage-reports/expected_show_coverage.conditions.txt new file mode 100644 index 000000000..2d8a98a5d --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.conditions.txt @@ -0,0 +1,94 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 4| 1| let mut countdown = 0; + 5| 1| if true { + 6| 1| countdown = 10; + 7| 1| } + ^0 + 8| | + 9| | const B: u32 = 100; + 10| 1| let x = if countdown > 7 { + 11| 1| countdown -= 4; + 12| 1| B + 13| 0| } else if countdown > 2 { + 14| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 15| 0| countdown = 0; + 16| 0| } + 17| 0| countdown -= 5; + 18| 0| countdown + 19| | } else { + 20| 0| return; + 21| | }; + 22| | + 23| 1| let mut countdown = 0; + 24| 1| if true { + 25| 1| countdown = 10; + 26| 1| } + ^0 + 27| | + 28| 1| if countdown > 7 { + 29| 1| countdown -= 4; + 30| 1| } else if countdown > 2 { + ^0 + 31| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 32| 0| countdown = 0; + 33| 0| } + 34| 0| countdown -= 5; + 35| | } else { + 36| 0| return; + 37| | } + 38| | + 39| 1| if true { + 40| 1| let mut countdown = 0; + 41| 1| if true { + 42| 1| countdown = 10; + 43| 1| } + ^0 + 44| | + 45| 1| if countdown > 7 { + 46| 1| countdown -= 4; + 47| 1| } + 48| 0| else if countdown > 2 { + 49| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 50| 0| countdown = 0; + 51| 0| } + 52| 0| countdown -= 5; + 53| | } else { + 54| 0| return; + 55| | } + 56| 0| } + 57| | + 58| | + 59| 1| let mut countdown = 0; + 60| 1| if true { + 61| 1| countdown = 1; + 62| 1| } + ^0 + 63| | + 64| 1| let z = if countdown > 7 { + ^0 + 65| 0| countdown -= 4; + 66| 1| } else if countdown > 2 { + 67| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 68| 0| countdown = 0; + 69| 0| } + 70| 0| countdown -= 5; + 71| | } else { + 72| 1| let should_be_reachable = countdown; + 73| 1| println!("reached"); + 74| 1| return; + 75| | }; + 76| | + 77| 0| let w = if countdown > 7 { + 78| 0| countdown -= 4; + 79| 0| } else if countdown > 2 { + 80| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 81| 0| countdown = 0; + 82| 0| } + 83| 0| countdown -= 5; + 84| | } else { + 85| 0| return; + 86| | }; + 87| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.continue.txt b/src/test/run-make/coverage-reports/expected_show_coverage.continue.txt new file mode 100644 index 000000000..1c64ead9f --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.continue.txt @@ -0,0 +1,70 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 4| 1| let is_true = std::env::args().len() == 1; + 5| 1| + 6| 1| let mut x = 0; + 7| 11| for _ in 0..10 { + 8| 10| match is_true { + 9| | true => { + 10| 10| continue; + 11| | } + 12| 0| _ => { + 13| 0| x = 1; + 14| 0| } + 15| 0| } + 16| 0| x = 3; + 17| | } + 18| 11| for _ in 0..10 { + 19| 10| match is_true { + 20| 0| false => { + 21| 0| x = 1; + 22| 0| } + 23| | _ => { + 24| 10| continue; + 25| | } + 26| | } + 27| 0| x = 3; + 28| | } + 29| 11| for _ in 0..10 { + 30| 10| match is_true { + 31| 10| true => { + 32| 10| x = 1; + 33| 10| } + 34| | _ => { + 35| 0| continue; + 36| | } + 37| | } + 38| 10| x = 3; + 39| | } + 40| 11| for _ in 0..10 { + 41| 10| if is_true { + 42| 10| continue; + 43| 0| } + 44| 0| x = 3; + 45| | } + 46| 11| for _ in 0..10 { + 47| 10| match is_true { + 48| 0| false => { + 49| 0| x = 1; + 50| 0| } + 51| 10| _ => { + 52| 10| let _ = x; + 53| 10| } + 54| | } + 55| 10| x = 3; + 56| | } + 57| 1| for _ in 0..10 { + 58| 1| match is_true { + 59| 0| false => { + 60| 0| x = 1; + 61| 0| } + 62| | _ => { + 63| 1| break; + 64| | } + 65| | } + 66| 0| x = 3; + 67| | } + 68| | let _ = x; + 69| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.dead_code.txt b/src/test/run-make/coverage-reports/expected_show_coverage.dead_code.txt new file mode 100644 index 000000000..09ff14c6f --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.dead_code.txt @@ -0,0 +1,39 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 0|pub fn unused_pub_fn_not_in_library() { + 4| 0| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 5| 0| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 6| 0| // dependent conditions. + 7| 0| let is_true = std::env::args().len() == 1; + 8| 0| + 9| 0| let mut countdown = 0; + 10| 0| if is_true { + 11| 0| countdown = 10; + 12| 0| } + 13| 0|} + 14| | + 15| 0|fn unused_fn() { + 16| 0| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 17| 0| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 18| 0| // dependent conditions. + 19| 0| let is_true = std::env::args().len() == 1; + 20| 0| + 21| 0| let mut countdown = 0; + 22| 0| if is_true { + 23| 0| countdown = 10; + 24| 0| } + 25| 0|} + 26| | + 27| 1|fn main() { + 28| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 29| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 30| 1| // dependent conditions. + 31| 1| let is_true = std::env::args().len() == 1; + 32| 1| + 33| 1| let mut countdown = 0; + 34| 1| if is_true { + 35| 1| countdown = 10; + 36| 1| } + ^0 + 37| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.doctest.txt b/src/test/run-make/coverage-reports/expected_show_coverage.doctest.txt new file mode 100644 index 000000000..7ae0e9788 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.doctest.txt @@ -0,0 +1,121 @@ +../coverage/doctest.rs: + 1| |//! This test ensures that code from doctests is properly re-mapped. + 2| |//! See <https://github.com/rust-lang/rust/issues/79417> for more info. + 3| |//! + 4| |//! Just some random code: + 5| 1|//! ``` + 6| 1|//! if true { + 7| |//! // this is executed! + 8| 1|//! assert_eq!(1, 1); + 9| |//! } else { + 10| |//! // this is not! + 11| 0|//! assert_eq!(1, 2); + 12| |//! } + 13| 1|//! ``` + 14| |//! + 15| |//! doctest testing external code: + 16| |//! ``` + 17| 1|//! extern crate doctest_crate; + 18| 1|//! doctest_crate::fn_run_in_doctests(1); + 19| 1|//! ``` + 20| |//! + 21| |//! doctest returning a result: + 22| 1|//! ``` + 23| 2|//! #[derive(Debug, PartialEq)] + ^1 + ------------------ + | Unexecuted instantiation: <rust_out::main::_doctest_main____coverage_doctest_rs_22_0::SomeError as core::cmp::PartialEq>::ne + ------------------ + | <rust_out::main::_doctest_main____coverage_doctest_rs_22_0::SomeError as core::cmp::PartialEq>::eq: + | 23| 2|//! #[derive(Debug, PartialEq)] + ------------------ + 24| 1|//! struct SomeError { + 25| 1|//! msg: String, + 26| 1|//! } + 27| 1|//! let mut res = Err(SomeError { msg: String::from("a message") }); + 28| 1|//! if res.is_ok() { + 29| 0|//! res?; + 30| |//! } else { + 31| 1|//! if *res.as_ref().unwrap_err() == *res.as_ref().unwrap_err() { + 32| 1|//! println!("{:?}", res); + 33| 1|//! } + ^0 + 34| 1|//! if *res.as_ref().unwrap_err() == *res.as_ref().unwrap_err() { + 35| 1|//! res = Ok(1); + 36| 1|//! } + ^0 + 37| 1|//! res = Ok(0); + 38| |//! } + 39| |//! // need to be explicit because rustdoc cant infer the return type + 40| 1|//! Ok::<(), SomeError>(()) + 41| 1|//! ``` + 42| |//! + 43| |//! doctest with custom main: + 44| |//! ``` + 45| 1|//! fn some_func() { + 46| 1|//! println!("called some_func()"); + 47| 1|//! } + 48| |//! + 49| 0|//! #[derive(Debug)] + 50| |//! struct SomeError; + 51| |//! + 52| |//! extern crate doctest_crate; + 53| |//! + 54| 1|//! fn doctest_main() -> Result<(), SomeError> { + 55| 1|//! some_func(); + 56| 1|//! doctest_crate::fn_run_in_doctests(2); + 57| 1|//! Ok(()) + 58| 1|//! } + 59| |//! + 60| |//! // this `main` is not shown as covered, as it clashes with all the other + 61| |//! // `main` functions that were automatically generated for doctests + 62| |//! fn main() -> Result<(), SomeError> { + 63| |//! doctest_main() + 64| |//! } + 65| |//! ``` + 66| | + 67| |/// doctest attached to fn testing external code: + 68| |/// ``` + 69| 1|/// extern crate doctest_crate; + 70| 1|/// doctest_crate::fn_run_in_doctests(3); + 71| 1|/// ``` + 72| |/// + 73| 1|fn main() { + 74| 1| if true { + 75| 1| assert_eq!(1, 1); + 76| | } else { + 77| 0| assert_eq!(1, 2); + 78| | } + 79| 1|} + 80| | + 81| |// FIXME(Swatinem): Fix known issue that coverage code region columns need to be offset by the + 82| |// doc comment line prefix (`///` or `//!`) and any additional indent (before or after the doc + 83| |// comment characters). This test produces `llvm-cov show` results demonstrating the problem. + 84| |// + 85| |// One of the above tests now includes: `derive(Debug, PartialEq)`, producing an `llvm-cov show` + 86| |// result with a distinct count for `Debug`, denoted by `^1`, but the caret points to the wrong + 87| |// column. Similarly, the `if` blocks without `else` blocks show `^0`, which should point at, or + 88| |// one character past, the `if` block's closing brace. In both cases, these are most likely off + 89| |// by the number of characters stripped from the beginning of each doc comment line: indent + 90| |// whitespace, if any, doc comment prefix (`//!` in this case) and (I assume) one space character + 91| |// (?). Note, when viewing `llvm-cov show` results in `--color` mode, the column offset errors are + 92| |// more pronounced, and show up in more places, with background color used to show some distinct + 93| |// code regions with different coverage counts. + 94| |// + 95| |// NOTE: Since the doc comment line prefix may vary, one possible solution is to replace each + 96| |// character stripped from the beginning of doc comment lines with a space. This will give coverage + 97| |// results the correct column offsets, and I think it should compile correctly, but I don't know + 98| |// what affect it might have on diagnostic messages from the compiler, and whether anyone would care + 99| |// if the indentation changed. I don't know if there is a more viable solution. + +../coverage/lib/doctest_crate.rs: + 1| |/// A function run only from within doctests + 2| 3|pub fn fn_run_in_doctests(conditional: usize) { + 3| 3| match conditional { + 4| 1| 1 => assert_eq!(1, 1), // this is run, + 5| 1| 2 => assert_eq!(1, 1), // this, + 6| 1| 3 => assert_eq!(1, 1), // and this too + 7| 0| _ => assert_eq!(1, 2), // however this is not + 8| | } + 9| 3|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.drop_trait.txt b/src/test/run-make/coverage-reports/expected_show_coverage.drop_trait.txt new file mode 100644 index 000000000..fe6a9e93c --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.drop_trait.txt @@ -0,0 +1,34 @@ + 1| |#![allow(unused_assignments)] + 2| |// expect-exit-status-1 + 3| | + 4| |struct Firework { + 5| | strength: i32, + 6| |} + 7| | + 8| |impl Drop for Firework { + 9| 2| fn drop(&mut self) { + 10| 2| println!("BOOM times {}!!!", self.strength); + 11| 2| } + 12| |} + 13| | + 14| 1|fn main() -> Result<(),u8> { + 15| 1| let _firecracker = Firework { strength: 1 }; + 16| 1| + 17| 1| let _tnt = Firework { strength: 100 }; + 18| 1| + 19| 1| if true { + 20| 1| println!("Exiting with error..."); + 21| 1| return Err(1); + 22| 0| } + 23| 0| + 24| 0| let _ = Firework { strength: 1000 }; + 25| 0| + 26| 0| Ok(()) + 27| 1|} + 28| | + 29| |// Expected program output: + 30| |// Exiting with error... + 31| |// BOOM times 100!!! + 32| |// BOOM times 1!!! + 33| |// Error: 1 + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.generator.txt b/src/test/run-make/coverage-reports/expected_show_coverage.generator.txt new file mode 100644 index 000000000..d70e12e41 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.generator.txt @@ -0,0 +1,32 @@ + 1| |#![feature(generators, generator_trait)] + 2| | + 3| |use std::ops::{Generator, GeneratorState}; + 4| |use std::pin::Pin; + 5| | + 6| |// The following implementation of a function called from a `yield` statement + 7| |// (apparently requiring the Result and the `String` type or constructor) + 8| |// creates conditions where the `generator::StateTransform` MIR transform will + 9| |// drop all `Counter` `Coverage` statements from a MIR. `simplify.rs` has logic + 10| |// to handle this condition, and still report dead block coverage. + 11| 1|fn get_u32(val: bool) -> Result<u32, String> { + 12| 1| if val { Ok(1) } else { Err(String::from("some error")) } + ^0 + 13| 1|} + 14| | + 15| 1|fn main() { + 16| 1| let is_true = std::env::args().len() == 1; + 17| 1| let mut generator = || { + 18| 1| yield get_u32(is_true); + 19| 1| return "foo"; + 20| | }; + 21| | + 22| 1| match Pin::new(&mut generator).resume(()) { + 23| 1| GeneratorState::Yielded(Ok(1)) => {} + 24| 0| _ => panic!("unexpected return from resume"), + 25| | } + 26| 1| match Pin::new(&mut generator).resume(()) { + 27| 1| GeneratorState::Complete("foo") => {} + 28| 0| _ => panic!("unexpected return from resume"), + 29| | } + 30| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.generics.txt b/src/test/run-make/coverage-reports/expected_show_coverage.generics.txt new file mode 100644 index 000000000..48983ba43 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.generics.txt @@ -0,0 +1,71 @@ + 1| |#![allow(unused_assignments)] + 2| |// expect-exit-status-1 + 3| | + 4| |struct Firework<T> where T: Copy + std::fmt::Display { + 5| | strength: T, + 6| |} + 7| | + 8| |impl<T> Firework<T> where T: Copy + std::fmt::Display { + 9| | #[inline(always)] + 10| 3| fn set_strength(&mut self, new_strength: T) { + 11| 3| self.strength = new_strength; + 12| 3| } + ------------------ + | <generics::Firework<i32>>::set_strength: + | 10| 1| fn set_strength(&mut self, new_strength: T) { + | 11| 1| self.strength = new_strength; + | 12| 1| } + ------------------ + | <generics::Firework<f64>>::set_strength: + | 10| 2| fn set_strength(&mut self, new_strength: T) { + | 11| 2| self.strength = new_strength; + | 12| 2| } + ------------------ + 13| |} + 14| | + 15| |impl<T> Drop for Firework<T> where T: Copy + std::fmt::Display { + 16| | #[inline(always)] + 17| 2| fn drop(&mut self) { + 18| 2| println!("BOOM times {}!!!", self.strength); + 19| 2| } + ------------------ + | <generics::Firework<f64> as core::ops::drop::Drop>::drop: + | 17| 1| fn drop(&mut self) { + | 18| 1| println!("BOOM times {}!!!", self.strength); + | 19| 1| } + ------------------ + | <generics::Firework<i32> as core::ops::drop::Drop>::drop: + | 17| 1| fn drop(&mut self) { + | 18| 1| println!("BOOM times {}!!!", self.strength); + | 19| 1| } + ------------------ + 20| |} + 21| | + 22| 1|fn main() -> Result<(),u8> { + 23| 1| let mut firecracker = Firework { strength: 1 }; + 24| 1| firecracker.set_strength(2); + 25| 1| + 26| 1| let mut tnt = Firework { strength: 100.1 }; + 27| 1| tnt.set_strength(200.1); + 28| 1| tnt.set_strength(300.3); + 29| 1| + 30| 1| if true { + 31| 1| println!("Exiting with error..."); + 32| 1| return Err(1); + 33| 0| } + 34| 0| + 35| 0| + 36| 0| + 37| 0| + 38| 0| + 39| 0| let _ = Firework { strength: 1000 }; + 40| 0| + 41| 0| Ok(()) + 42| 1|} + 43| | + 44| |// Expected program output: + 45| |// Exiting with error... + 46| |// BOOM times 100!!! + 47| |// BOOM times 1!!! + 48| |// Error: 1 + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.if.txt b/src/test/run-make/coverage-reports/expected_show_coverage.if.txt new file mode 100644 index 000000000..0c9eff227 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.if.txt @@ -0,0 +1,30 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 4| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 5| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 6| 1| // dependent conditions. + 7| 1| let + 8| 1| is_true + 9| 1| = + 10| 1| std::env::args().len() + 11| 1| == + 12| 1| 1 + 13| 1| ; + 14| 1| let + 15| 1| mut + 16| 1| countdown + 17| 1| = + 18| 1| 0 + 19| 1| ; + 20| 1| if + 21| 1| is_true + 22| 1| { + 23| 1| countdown + 24| 1| = + 25| 1| 10 + 26| 1| ; + 27| 1| } + ^0 + 28| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.if_else.txt b/src/test/run-make/coverage-reports/expected_show_coverage.if_else.txt new file mode 100644 index 000000000..4285d3186 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.if_else.txt @@ -0,0 +1,41 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 4| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 5| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 6| 1| // dependent conditions. + 7| 1| let is_true = std::env::args().len() == 1; + 8| 1| + 9| 1| let mut countdown = 0; + 10| 1| if + 11| 1| is_true + 12| 1| { + 13| 1| countdown + 14| 1| = + 15| 1| 10 + 16| 1| ; + 17| 1| } + 18| | else // Note coverage region difference without semicolon + 19| | { + 20| 0| countdown + 21| 0| = + 22| 0| 100 + 23| | } + 24| | + 25| | if + 26| 1| is_true + 27| 1| { + 28| 1| countdown + 29| 1| = + 30| 1| 10 + 31| 1| ; + 32| 1| } + 33| | else + 34| 0| { + 35| 0| countdown + 36| 0| = + 37| 0| 100 + 38| 0| ; + 39| 0| } + 40| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.inline-dead.txt b/src/test/run-make/coverage-reports/expected_show_coverage.inline-dead.txt new file mode 100644 index 000000000..effdef80e --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.inline-dead.txt @@ -0,0 +1,28 @@ + 1| |// Regression test for issue #98833. + 2| |// compile-flags: -Zinline-mir -Cdebug-assertions=off + 3| | + 4| 1|fn main() { + 5| 1| println!("{}", live::<false>()); + 6| 1| + 7| 1| let f = |x: bool| { + 8| | debug_assert!( + 9| | x + 10| | ); + 11| 1| }; + 12| 1| f(false); + 13| 1|} + 14| | + 15| |#[inline] + 16| 1|fn live<const B: bool>() -> u32 { + 17| 1| if B { + 18| 0| dead() + 19| | } else { + 20| 1| 0 + 21| | } + 22| 1|} + 23| | + 24| |#[inline] + 25| 0|fn dead() -> u32 { + 26| 0| 42 + 27| 0|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.inline.txt b/src/test/run-make/coverage-reports/expected_show_coverage.inline.txt new file mode 100644 index 000000000..6f5d1544f --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.inline.txt @@ -0,0 +1,54 @@ + 1| |// compile-flags: -Zinline-mir + 2| | + 3| |use std::fmt::Display; + 4| | + 5| 1|fn main() { + 6| 1| permutations(&['a', 'b', 'c']); + 7| 1|} + 8| | + 9| |#[inline(always)] + 10| 1|fn permutations<T: Copy + Display>(xs: &[T]) { + 11| 1| let mut ys = xs.to_owned(); + 12| 1| permutate(&mut ys, 0); + 13| 1|} + 14| | + 15| 16|fn permutate<T: Copy + Display>(xs: &mut [T], k: usize) { + 16| 16| let n = length(xs); + 17| 16| if k == n { + 18| 6| display(xs); + 19| 10| } else if k < n { + 20| 15| for i in k..n { + ^10 + 21| 15| swap(xs, i, k); + 22| 15| permutate(xs, k + 1); + 23| 15| swap(xs, i, k); + 24| 15| } + 25| 0| } else { + 26| 0| error(); + 27| 0| } + 28| 16|} + 29| | + 30| 16|fn length<T>(xs: &[T]) -> usize { + 31| 16| xs.len() + 32| 16|} + 33| | + 34| |#[inline] + 35| 30|fn swap<T: Copy>(xs: &mut [T], i: usize, j: usize) { + 36| 30| let t = xs[i]; + 37| 30| xs[i] = xs[j]; + 38| 30| xs[j] = t; + 39| 30|} + 40| | + 41| 6|fn display<T: Display>(xs: &[T]) { + 42| 24| for x in xs { + ^18 + 43| 18| print!("{}", x); + 44| 18| } + 45| 6| println!(); + 46| 6|} + 47| | + 48| |#[inline(always)] + 49| 0|fn error() { + 50| 0| panic!("error"); + 51| 0|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.inner_items.txt b/src/test/run-make/coverage-reports/expected_show_coverage.inner_items.txt new file mode 100644 index 000000000..883254a09 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.inner_items.txt @@ -0,0 +1,60 @@ + 1| |#![allow(unused_assignments, unused_variables, dead_code)] + 2| | + 3| 1|fn main() { + 4| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 5| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 6| 1| // dependent conditions. + 7| 1| let is_true = std::env::args().len() == 1; + 8| 1| + 9| 1| let mut countdown = 0; + 10| 1| if is_true { + 11| 1| countdown = 10; + 12| 1| } + ^0 + 13| | + 14| | mod in_mod { + 15| | const IN_MOD_CONST: u32 = 1000; + 16| | } + 17| | + 18| 3| fn in_func(a: u32) { + 19| 3| let b = 1; + 20| 3| let c = a + b; + 21| 3| println!("c = {}", c) + 22| 3| } + 23| | + 24| | struct InStruct { + 25| | in_struct_field: u32, + 26| | } + 27| | + 28| | const IN_CONST: u32 = 1234; + 29| | + 30| | trait InTrait { + 31| | fn trait_func(&mut self, incr: u32); + 32| | + 33| 1| fn default_trait_func(&mut self) { + 34| 1| in_func(IN_CONST); + 35| 1| self.trait_func(IN_CONST); + 36| 1| } + 37| | } + 38| | + 39| | impl InTrait for InStruct { + 40| 1| fn trait_func(&mut self, incr: u32) { + 41| 1| self.in_struct_field += incr; + 42| 1| in_func(self.in_struct_field); + 43| 1| } + 44| | } + 45| | + 46| | type InType = String; + 47| | + 48| 1| if is_true { + 49| 1| in_func(countdown); + 50| 1| } + ^0 + 51| | + 52| 1| let mut val = InStruct { + 53| 1| in_struct_field: 101, + 54| 1| }; + 55| 1| + 56| 1| val.default_trait_func(); + 57| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.issue-83601.txt b/src/test/run-make/coverage-reports/expected_show_coverage.issue-83601.txt new file mode 100644 index 000000000..de32c88b7 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.issue-83601.txt @@ -0,0 +1,22 @@ + 1| |// Shows that rust-lang/rust/83601 is resolved + 2| | + 3| 3|#[derive(Debug, PartialEq, Eq)] + ^2 + ------------------ + | <issue_83601::Foo as core::cmp::PartialEq>::eq: + | 3| 2|#[derive(Debug, PartialEq, Eq)] + ------------------ + | Unexecuted instantiation: <issue_83601::Foo as core::cmp::PartialEq>::ne + ------------------ + 4| |struct Foo(u32); + 5| | + 6| 1|fn main() { + 7| 1| let bar = Foo(1); + 8| 1| assert_eq!(bar, Foo(1)); + 9| 1| let baz = Foo(0); + 10| 1| assert_ne!(baz, Foo(1)); + 11| 1| println!("{:?}", Foo(1)); + 12| 1| println!("{:?}", bar); + 13| 1| println!("{:?}", baz); + 14| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.issue-84561.txt b/src/test/run-make/coverage-reports/expected_show_coverage.issue-84561.txt new file mode 100644 index 000000000..f24f7c694 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.issue-84561.txt @@ -0,0 +1,195 @@ + 1| |// This demonstrated Issue #84561: function-like macros produce unintuitive coverage results. + 2| | + 3| |// expect-exit-status-101 + 4| 21|#[derive(PartialEq, Eq)] + ------------------ + | <issue_84561::Foo as core::cmp::PartialEq>::eq: + | 4| 21|#[derive(PartialEq, Eq)] + ------------------ + | Unexecuted instantiation: <issue_84561::Foo as core::cmp::PartialEq>::ne + ------------------ + 5| |struct Foo(u32); + 6| 1|fn test3() { + 7| 1| let is_true = std::env::args().len() == 1; + 8| 1| let bar = Foo(1); + 9| 1| assert_eq!(bar, Foo(1)); + 10| 1| let baz = Foo(0); + 11| 1| assert_ne!(baz, Foo(1)); + 12| 1| println!("{:?}", Foo(1)); + 13| 1| println!("{:?}", bar); + 14| 1| println!("{:?}", baz); + 15| 1| + 16| 1| assert_eq!(Foo(1), Foo(1)); + 17| 1| assert_ne!(Foo(0), Foo(1)); + 18| 1| assert_eq!(Foo(2), Foo(2)); + 19| 1| let bar = Foo(0); + 20| 1| assert_ne!(bar, Foo(3)); + 21| 1| assert_ne!(Foo(0), Foo(4)); + 22| 1| assert_eq!(Foo(3), Foo(3), "with a message"); + ^0 + 23| 1| println!("{:?}", bar); + 24| 1| println!("{:?}", Foo(1)); + 25| 1| + 26| 1| assert_ne!(Foo(0), Foo(5), "{}", if is_true { "true message" } else { "false message" }); + ^0 ^0 ^0 + 27| 1| assert_ne!( + 28| | Foo(0) + 29| | , + 30| | Foo(5) + 31| | , + 32| 0| "{}" + 33| 0| , + 34| 0| if + 35| 0| is_true + 36| | { + 37| 0| "true message" + 38| | } else { + 39| 0| "false message" + 40| | } + 41| | ); + 42| | + 43| 1| let is_true = std::env::args().len() == 1; + 44| 1| + 45| 1| assert_eq!( + 46| 1| Foo(1), + 47| 1| Foo(1) + 48| 1| ); + 49| 1| assert_ne!( + 50| 1| Foo(0), + 51| 1| Foo(1) + 52| 1| ); + 53| 1| assert_eq!( + 54| 1| Foo(2), + 55| 1| Foo(2) + 56| 1| ); + 57| 1| let bar = Foo(1); + 58| 1| assert_ne!( + 59| 1| bar, + 60| 1| Foo(3) + 61| 1| ); + 62| 1| if is_true { + 63| 1| assert_ne!( + 64| 1| Foo(0), + 65| 1| Foo(4) + 66| 1| ); + 67| | } else { + 68| 0| assert_eq!( + 69| 0| Foo(3), + 70| 0| Foo(3) + 71| 0| ); + 72| | } + 73| 1| if is_true { + 74| 1| assert_ne!( + 75| | Foo(0), + 76| | Foo(4), + 77| 0| "with a message" + 78| | ); + 79| | } else { + 80| 0| assert_eq!( + 81| | Foo(3), + 82| | Foo(3), + 83| 0| "with a message" + 84| | ); + 85| | } + 86| 1| assert_ne!( + 87| 1| if is_true { + 88| 1| Foo(0) + 89| | } else { + 90| 0| Foo(1) + 91| | }, + 92| | Foo(5) + 93| | ); + 94| 1| assert_ne!( + 95| 1| Foo(5), + 96| 1| if is_true { + 97| 1| Foo(0) + 98| | } else { + 99| 0| Foo(1) + 100| | } + 101| | ); + 102| 1| assert_ne!( + 103| 1| if is_true { + 104| 1| assert_eq!( + 105| 1| Foo(3), + 106| 1| Foo(3) + 107| 1| ); + 108| 1| Foo(0) + 109| | } else { + 110| 0| assert_ne!( + 111| 0| if is_true { + 112| 0| Foo(0) + 113| | } else { + 114| 0| Foo(1) + 115| | }, + 116| | Foo(5) + 117| | ); + 118| 0| Foo(1) + 119| | }, + 120| | Foo(5), + 121| 0| "with a message" + 122| | ); + 123| 1| assert_eq!( + 124| | Foo(1), + 125| | Foo(3), + 126| 1| "this assert should fail" + 127| | ); + 128| 0| assert_eq!( + 129| | Foo(3), + 130| | Foo(3), + 131| 0| "this assert should not be reached" + 132| | ); + 133| 0|} + 134| | + 135| |impl std::fmt::Debug for Foo { + 136| | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + 137| 7| write!(f, "try and succeed")?; + ^0 + 138| 7| Ok(()) + 139| 7| } + 140| |} + 141| | + 142| |static mut DEBUG_LEVEL_ENABLED: bool = false; + 143| | + 144| |macro_rules! debug { + 145| | ($($arg:tt)+) => ( + 146| | if unsafe { DEBUG_LEVEL_ENABLED } { + 147| | println!($($arg)+); + 148| | } + 149| | ); + 150| |} + 151| | + 152| 1|fn test1() { + 153| 1| debug!("debug is enabled"); + ^0 + 154| 1| debug!("debug is enabled"); + ^0 + 155| 1| let _ = 0; + 156| 1| debug!("debug is enabled"); + ^0 + 157| 1| unsafe { + 158| 1| DEBUG_LEVEL_ENABLED = true; + 159| 1| } + 160| 1| debug!("debug is enabled"); + 161| 1|} + 162| | + 163| |macro_rules! call_debug { + 164| | ($($arg:tt)+) => ( + 165| 1| fn call_print(s: &str) { + 166| 1| print!("{}", s); + 167| 1| } + 168| | + 169| | call_print("called from call_debug: "); + 170| | debug!($($arg)+); + 171| | ); + 172| |} + 173| | + 174| 1|fn test2() { + 175| 1| call_debug!("debug is enabled"); + 176| 1|} + 177| | + 178| 1|fn main() { + 179| 1| test1(); + 180| 1| test2(); + 181| 1| test3(); + 182| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.issue-85461.txt b/src/test/run-make/coverage-reports/expected_show_coverage.issue-85461.txt new file mode 100644 index 000000000..1aa4a22c3 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.issue-85461.txt @@ -0,0 +1,36 @@ +../coverage/issue-85461.rs: + 1| |// Regression test for #85461: MSVC sometimes fail to link with dead code and #[inline(always)] + 2| | + 3| |extern crate inline_always_with_dead_code; + 4| | + 5| |use inline_always_with_dead_code::{bar, baz}; + 6| | + 7| 1|fn main() { + 8| 1| bar::call_me(); + 9| 1| baz::call_me(); + 10| 1|} + +../coverage/lib/inline_always_with_dead_code.rs: + 1| |// compile-flags: -Cinstrument-coverage -Ccodegen-units=4 -Copt-level=0 + 2| | + 3| |#![allow(dead_code)] + 4| | + 5| |mod foo { + 6| | #[inline(always)] + 7| 2| pub fn called() { } + 8| | + 9| 0| fn uncalled() { } + 10| |} + 11| | + 12| |pub mod bar { + 13| 1| pub fn call_me() { + 14| 1| super::foo::called(); + 15| 1| } + 16| |} + 17| | + 18| |pub mod baz { + 19| 1| pub fn call_me() { + 20| 1| super::foo::called(); + 21| 1| } + 22| |} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.issue-93054.txt b/src/test/run-make/coverage-reports/expected_show_coverage.issue-93054.txt new file mode 100644 index 000000000..a1655aded --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.issue-93054.txt @@ -0,0 +1,29 @@ + 1| |// Regression test for #93054: Functions using uninhabited types often only have a single, + 2| |// unreachable basic block which doesn't get instrumented. This should not cause llvm-cov to fail. + 3| |// Since these kinds functions can't be invoked anyway, it's ok to not have coverage data for them. + 4| | + 5| |// compile-flags: --edition=2021 + 6| | + 7| |enum Never { } + 8| | + 9| |impl Never { + 10| | fn foo(self) { + 11| | match self { } + 12| | make().map(|never| match never { }); + 13| | } + 14| | + 15| | fn bar(&self) { + 16| | match *self { } + 17| | } + 18| |} + 19| | + 20| 0|async fn foo2(never: Never) { + 21| | match never { } + 22| |} + 23| | + 24| 0|fn make() -> Option<Never> { + 25| 0| None + 26| 0|} + 27| | + 28| 1|fn main() { } + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.lazy_boolean.txt b/src/test/run-make/coverage-reports/expected_show_coverage.lazy_boolean.txt new file mode 100644 index 000000000..bd349df2f --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.lazy_boolean.txt @@ -0,0 +1,64 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 4| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 5| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 6| 1| // dependent conditions. + 7| 1| let is_true = std::env::args().len() == 1; + 8| 1| + 9| 1| let (mut a, mut b, mut c) = (0, 0, 0); + 10| 1| if is_true { + 11| 1| a = 1; + 12| 1| b = 10; + 13| 1| c = 100; + 14| 1| } + ^0 + 15| | let + 16| 1| somebool + 17| | = + 18| 1| a < b + 19| | || + 20| 0| b < c + 21| | ; + 22| | let + 23| 1| somebool + 24| | = + 25| 1| b < a + 26| | || + 27| 1| b < c + 28| | ; + 29| 1| let somebool = a < b && b < c; + 30| 1| let somebool = b < a && b < c; + ^0 + 31| | + 32| | if + 33| 1| ! + 34| 1| is_true + 35| 0| { + 36| 0| a = 2 + 37| 0| ; + 38| 1| } + 39| | + 40| | if + 41| 1| is_true + 42| 1| { + 43| 1| b = 30 + 44| 1| ; + 45| 1| } + 46| | else + 47| 0| { + 48| 0| c = 400 + 49| 0| ; + 50| 0| } + 51| | + 52| 1| if !is_true { + 53| 0| a = 2; + 54| 1| } + 55| | + 56| 1| if is_true { + 57| 1| b = 30; + 58| 1| } else { + 59| 0| c = 400; + 60| 0| } + 61| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.loop_break_value.txt b/src/test/run-make/coverage-reports/expected_show_coverage.loop_break_value.txt new file mode 100644 index 000000000..022fe4c59 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.loop_break_value.txt @@ -0,0 +1,14 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 4| 1| let result + 5| 1| = + 6| 1| loop + 7| 1| { + 8| 1| break + 9| 1| 10 + 10| 1| ; + 11| 1| } + 12| 1| ; + 13| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.loops_branches.txt b/src/test/run-make/coverage-reports/expected_show_coverage.loops_branches.txt new file mode 100644 index 000000000..b7ad79a24 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.loops_branches.txt @@ -0,0 +1,68 @@ + 1| |#![allow(unused_assignments, unused_variables, while_true)] + 2| | + 3| |// This test confirms that (1) unexecuted infinite loops are handled correctly by the + 4| |// InstrumentCoverage MIR pass; and (2) Counter Expressions that subtract from zero can be dropped. + 5| | + 6| |struct DebugTest; + 7| | + 8| |impl std::fmt::Debug for DebugTest { + 9| 1| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + 10| 1| if true { + 11| 1| if false { + 12| 0| while true { + 13| 0| } + 14| 1| } + 15| 1| write!(f, "cool")?; + ^0 + 16| 0| } else { + 17| 0| } + 18| | + 19| 11| for i in 0..10 { + ^10 + 20| 10| if true { + 21| 10| if false { + 22| 0| while true {} + 23| 10| } + 24| 10| write!(f, "cool")?; + ^0 + 25| 0| } else { + 26| 0| } + 27| | } + 28| 1| Ok(()) + 29| 1| } + 30| |} + 31| | + 32| |struct DisplayTest; + 33| | + 34| |impl std::fmt::Display for DisplayTest { + 35| 1| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + 36| 1| if false { + 37| 0| } else { + 38| 1| if false { + 39| 0| while true {} + 40| 1| } + 41| 1| write!(f, "cool")?; + ^0 + 42| | } + 43| 11| for i in 0..10 { + ^10 + 44| 10| if false { + 45| 0| } else { + 46| 10| if false { + 47| 0| while true {} + 48| 10| } + 49| 10| write!(f, "cool")?; + ^0 + 50| | } + 51| | } + 52| 1| Ok(()) + 53| 1| } + 54| |} + 55| | + 56| 1|fn main() { + 57| 1| let debug_test = DebugTest; + 58| 1| println!("{:?}", debug_test); + 59| 1| let display_test = DisplayTest; + 60| 1| println!("{}", display_test); + 61| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.match_or_pattern.txt b/src/test/run-make/coverage-reports/expected_show_coverage.match_or_pattern.txt new file mode 100644 index 000000000..a0fccb24f --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.match_or_pattern.txt @@ -0,0 +1,50 @@ + 1| |#![feature(or_patterns)] + 2| | + 3| 1|fn main() { + 4| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 5| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 6| 1| // dependent conditions. + 7| 1| let is_true = std::env::args().len() == 1; + 8| 1| + 9| 1| let mut a: u8 = 0; + 10| 1| let mut b: u8 = 0; + 11| 1| if is_true { + 12| 1| a = 2; + 13| 1| b = 0; + 14| 1| } + ^0 + 15| 1| match (a, b) { + 16| | // Or patterns generate MIR `SwitchInt` with multiple targets to the same `BasicBlock`. + 17| | // This test confirms a fix for Issue #79569. + 18| 0| (0 | 1, 2 | 3) => {} + 19| 1| _ => {} + 20| | } + 21| 1| if is_true { + 22| 1| a = 0; + 23| 1| b = 0; + 24| 1| } + ^0 + 25| 1| match (a, b) { + 26| 0| (0 | 1, 2 | 3) => {} + 27| 1| _ => {} + 28| | } + 29| 1| if is_true { + 30| 1| a = 2; + 31| 1| b = 2; + 32| 1| } + ^0 + 33| 1| match (a, b) { + 34| 0| (0 | 1, 2 | 3) => {} + 35| 1| _ => {} + 36| | } + 37| 1| if is_true { + 38| 1| a = 0; + 39| 1| b = 2; + 40| 1| } + ^0 + 41| 1| match (a, b) { + 42| 1| (0 | 1, 2 | 3) => {} + 43| 0| _ => {} + 44| | } + 45| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.nested_loops.txt b/src/test/run-make/coverage-reports/expected_show_coverage.nested_loops.txt new file mode 100644 index 000000000..0dbd6bcf3 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.nested_loops.txt @@ -0,0 +1,26 @@ + 1| 1|fn main() { + 2| 1| let is_true = std::env::args().len() == 1; + 3| 1| let mut countdown = 10; + 4| | + 5| 1| 'outer: while countdown > 0 { + 6| 1| let mut a = 100; + 7| 1| let mut b = 100; + 8| 3| for _ in 0..50 { + 9| 3| if a < 30 { + 10| 0| break; + 11| 3| } + 12| 3| a -= 5; + 13| 3| b -= 5; + 14| 3| if b < 90 { + 15| 1| a -= 10; + 16| 1| if is_true { + 17| 1| break 'outer; + 18| 0| } else { + 19| 0| a -= 2; + 20| 0| } + 21| 2| } + 22| | } + 23| 0| countdown -= 1; + 24| | } + 25| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.no_cov_crate.txt b/src/test/run-make/coverage-reports/expected_show_coverage.no_cov_crate.txt new file mode 100644 index 000000000..83a920413 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.no_cov_crate.txt @@ -0,0 +1,87 @@ + 1| |// Enables `no_coverage` on the entire crate + 2| |#![feature(no_coverage)] + 3| | + 4| |#[no_coverage] + 5| |fn do_not_add_coverage_1() { + 6| | println!("called but not covered"); + 7| |} + 8| | + 9| |fn do_not_add_coverage_2() { + 10| | #![no_coverage] + 11| | println!("called but not covered"); + 12| |} + 13| | + 14| |#[no_coverage] + 15| |fn do_not_add_coverage_not_called() { + 16| | println!("not called and not covered"); + 17| |} + 18| | + 19| 1|fn add_coverage_1() { + 20| 1| println!("called and covered"); + 21| 1|} + 22| | + 23| 1|fn add_coverage_2() { + 24| 1| println!("called and covered"); + 25| 1|} + 26| | + 27| 0|fn add_coverage_not_called() { + 28| 0| println!("not called but covered"); + 29| 0|} + 30| | + 31| |// FIXME: These test-cases illustrate confusing results of nested functions. + 32| |// See https://github.com/rust-lang/rust/issues/93319 + 33| |mod nested_fns { + 34| | #[no_coverage] + 35| | pub fn outer_not_covered(is_true: bool) { + 36| 1| fn inner(is_true: bool) { + 37| 1| if is_true { + 38| 1| println!("called and covered"); + 39| 1| } else { + 40| 0| println!("absolutely not covered"); + 41| 0| } + 42| 1| } + 43| | println!("called but not covered"); + 44| | inner(is_true); + 45| | } + 46| | + 47| 1| pub fn outer(is_true: bool) { + 48| 1| println!("called and covered"); + 49| 1| inner_not_covered(is_true); + 50| 1| + 51| 1| #[no_coverage] + 52| 1| fn inner_not_covered(is_true: bool) { + 53| 1| if is_true { + 54| 1| println!("called but not covered"); + 55| 1| } else { + 56| 1| println!("absolutely not covered"); + 57| 1| } + 58| 1| } + 59| 1| } + 60| | + 61| 1| pub fn outer_both_covered(is_true: bool) { + 62| 1| println!("called and covered"); + 63| 1| inner(is_true); + 64| 1| + 65| 1| fn inner(is_true: bool) { + 66| 1| if is_true { + 67| 1| println!("called and covered"); + 68| 1| } else { + 69| 0| println!("absolutely not covered"); + 70| 0| } + 71| 1| } + 72| 1| } + 73| |} + 74| | + 75| 1|fn main() { + 76| 1| let is_true = std::env::args().len() == 1; + 77| 1| + 78| 1| do_not_add_coverage_1(); + 79| 1| do_not_add_coverage_2(); + 80| 1| add_coverage_1(); + 81| 1| add_coverage_2(); + 82| 1| + 83| 1| nested_fns::outer_not_covered(is_true); + 84| 1| nested_fns::outer(is_true); + 85| 1| nested_fns::outer_both_covered(is_true); + 86| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.overflow.txt b/src/test/run-make/coverage-reports/expected_show_coverage.overflow.txt new file mode 100644 index 000000000..25e822bff --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.overflow.txt @@ -0,0 +1,64 @@ + 1| |#![allow(unused_assignments)] + 2| |// expect-exit-status-101 + 3| | + 4| 4|fn might_overflow(to_add: u32) -> u32 { + 5| 4| if to_add > 5 { + 6| 1| println!("this will probably overflow"); + 7| 3| } + 8| 4| let add_to = u32::MAX - 5; + 9| 4| println!("does {} + {} overflow?", add_to, to_add); + 10| 4| let result = to_add + add_to; + 11| 4| println!("continuing after overflow check"); + 12| 4| result + 13| 4|} + 14| | + 15| 1|fn main() -> Result<(),u8> { + 16| 1| let mut countdown = 10; + 17| 11| while countdown > 0 { + 18| 11| if countdown == 1 { + 19| 1| let result = might_overflow(10); + 20| 1| println!("Result: {}", result); + 21| 10| } else if countdown < 5 { + 22| 3| let result = might_overflow(1); + 23| 3| println!("Result: {}", result); + 24| 6| } + 25| 10| countdown -= 1; + 26| | } + 27| 0| Ok(()) + 28| 0|} + 29| | + 30| |// Notes: + 31| |// 1. Compare this program and its coverage results to those of the very similar test `assert.rs`, + 32| |// and similar tests `panic_unwind.rs`, abort.rs` and `try_error_result.rs`. + 33| |// 2. This test confirms the coverage generated when a program passes or fails a + 34| |// compiler-generated `TerminatorKind::Assert` (based on an overflow check, in this case). + 35| |// 3. Similar to how the coverage instrumentation handles `TerminatorKind::Call`, + 36| |// compiler-generated assertion failures are assumed to be a symptom of a program bug, not + 37| |// expected behavior. To simplify the coverage graphs and keep instrumented programs as + 38| |// small and fast as possible, `Assert` terminators are assumed to always succeed, and + 39| |// therefore are considered "non-branching" terminators. So, an `Assert` terminator does not + 40| |// get its own coverage counter. + 41| |// 4. After an unhandled panic or failed Assert, coverage results may not always be intuitive. + 42| |// In this test, the final count for the statements after the `if` block in `might_overflow()` + 43| |// is 4, even though the lines after `to_add + add_to` were executed only 3 times. Depending + 44| |// on the MIR graph and the structure of the code, this count could have been 3 (which might + 45| |// have been valid for the overflowed add `+`, but should have been 4 for the lines before + 46| |// the overflow. The reason for this potential uncertainty is, a `CounterKind` is incremented + 47| |// via StatementKind::Counter at the end of the block, but (as in the case in this test), + 48| |// a CounterKind::Expression is always evaluated. In this case, the expression was based on + 49| |// a `Counter` incremented as part of the evaluation of the `if` expression, which was + 50| |// executed, and counted, 4 times, before reaching the overflow add. + 51| | + 52| |// If the program did not overflow, the coverage for `might_overflow()` would look like this: + 53| |// + 54| |// 4| |fn might_overflow(to_add: u32) -> u32 { + 55| |// 5| 4| if to_add > 5 { + 56| |// 6| 0| println!("this will probably overflow"); + 57| |// 7| 4| } + 58| |// 8| 4| let add_to = u32::MAX - 5; + 59| |// 9| 4| println!("does {} + {} overflow?", add_to, to_add); + 60| |// 10| 4| let result = to_add + add_to; + 61| |// 11| 4| println!("continuing after overflow check"); + 62| |// 12| 4| result + 63| |// 13| 4|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.panic_unwind.txt b/src/test/run-make/coverage-reports/expected_show_coverage.panic_unwind.txt new file mode 100644 index 000000000..114507dc9 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.panic_unwind.txt @@ -0,0 +1,32 @@ + 1| |#![allow(unused_assignments)] + 2| |// expect-exit-status-101 + 3| | + 4| 4|fn might_panic(should_panic: bool) { + 5| 4| if should_panic { + 6| 1| println!("panicking..."); + 7| 1| panic!("panics"); + 8| 3| } else { + 9| 3| println!("Don't Panic"); + 10| 3| } + 11| 3|} + 12| | + 13| 1|fn main() -> Result<(), u8> { + 14| 1| let mut countdown = 10; + 15| 11| while countdown > 0 { + 16| 11| if countdown == 1 { + 17| 1| might_panic(true); + 18| 10| } else if countdown < 5 { + 19| 3| might_panic(false); + 20| 6| } + 21| 10| countdown -= 1; + 22| | } + 23| 0| Ok(()) + 24| 0|} + 25| | + 26| |// Notes: + 27| |// 1. Compare this program and its coverage results to those of the similar tests `abort.rs` and + 28| |// `try_error_result.rs`. + 29| |// 2. Since the `panic_unwind.rs` test is allowed to unwind, it is also allowed to execute the + 30| |// normal program exit cleanup, including writing out the current values of the coverage + 31| |// counters. + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.partial_eq.txt b/src/test/run-make/coverage-reports/expected_show_coverage.partial_eq.txt new file mode 100644 index 000000000..fc2666533 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.partial_eq.txt @@ -0,0 +1,53 @@ + 1| |// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the + 2| |// structure of this test. + 3| | + 4| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + ^0 ^0 ^0 ^1 ^1 ^0^0 + ------------------ + | Unexecuted instantiation: <partial_eq::Version as core::cmp::PartialEq>::ne + ------------------ + | Unexecuted instantiation: <partial_eq::Version as core::cmp::PartialEq>::eq + ------------------ + 5| |pub struct Version { + 6| | major: usize, + 7| | minor: usize, + 8| | patch: usize, + 9| |} + 10| | + 11| |impl Version { + 12| 2| pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 13| 2| Self { + 14| 2| major, + 15| 2| minor, + 16| 2| patch, + 17| 2| } + 18| 2| } + 19| |} + 20| | + 21| 1|fn main() { + 22| 1| let version_3_2_1 = Version::new(3, 2, 1); + 23| 1| let version_3_3_0 = Version::new(3, 3, 0); + 24| 1| + 25| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); + 26| 1|} + 27| | + 28| |/* + 29| | + 30| |This test verifies a bug was fixed that otherwise generated this error: + 31| | + 32| |thread 'rustc' panicked at 'No counters provided the source_hash for function: + 33| | Instance { + 34| | def: Item(WithOptConstParam { + 35| | did: DefId(0:101 ~ autocfg[c44a]::version::{impl#2}::partial_cmp), + 36| | const_param_did: None + 37| | }), + 38| | substs: [] + 39| | }' + 40| |The `PartialOrd` derived by `Version` happened to generate a MIR that generated coverage + 41| |without a code region associated with any `Counter`. Code regions were associated with at least + 42| |one expression, which is allowed, but the `function_source_hash` was only passed to the codegen + 43| |(coverage mapgen) phase from a `Counter`s code region. A new method was added to pass the + 44| |`function_source_hash` without a code region, if necessary. + 45| | + 46| |*/ + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.simple_loop.txt b/src/test/run-make/coverage-reports/expected_show_coverage.simple_loop.txt new file mode 100644 index 000000000..feb83bad6 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.simple_loop.txt @@ -0,0 +1,37 @@ + 1| |#![allow(unused_assignments)] + 2| | + 3| 1|fn main() { + 4| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 5| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 6| 1| // dependent conditions. + 7| 1| let is_true = std::env::args().len() == 1; + 8| 1| + 9| 1| let mut countdown = 0; + 10| 1| + 11| 1| if + 12| 1| is_true + 13| 1| { + 14| 1| countdown + 15| 1| = + 16| 1| 10 + 17| 1| ; + 18| 1| } + ^0 + 19| | + 20| | loop + 21| | { + 22| | if + 23| 11| countdown + 24| 11| == + 25| 11| 0 + 26| | { + 27| 1| break + 28| | ; + 29| 10| } + 30| 10| countdown + 31| 10| -= + 32| 10| 1 + 33| | ; + 34| | } + 35| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.simple_match.txt b/src/test/run-make/coverage-reports/expected_show_coverage.simple_match.txt new file mode 100644 index 000000000..b92982131 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.simple_match.txt @@ -0,0 +1,45 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 4| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 5| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 6| 1| // dependent conditions. + 7| 1| let is_true = std::env::args().len() == 1; + 8| 1| + 9| 1| let mut countdown = 1; + 10| 1| if is_true { + 11| 1| countdown = 0; + 12| 1| } + ^0 + 13| | + 14| | for + 15| | _ + 16| | in + 17| 3| 0..2 + 18| | { + 19| | let z + 20| | ; + 21| | match + 22| 2| countdown + 23| | { + 24| 1| x + 25| | if + 26| 2| x + 27| 2| < + 28| 2| 1 + 29| | => + 30| 1| { + 31| 1| z = countdown + 32| 1| ; + 33| 1| let y = countdown + 34| 1| ; + 35| 1| countdown = 10 + 36| 1| ; + 37| 1| } + 38| | _ + 39| | => + 40| 1| {} + 41| | } + 42| | } + 43| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.tight_inf_loop.txt b/src/test/run-make/coverage-reports/expected_show_coverage.tight_inf_loop.txt new file mode 100644 index 000000000..2d4c57f45 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.tight_inf_loop.txt @@ -0,0 +1,6 @@ + 1| 1|fn main() { + 2| 1| if false { + 3| 0| loop {} + 4| 1| } + 5| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.try_error_result.txt b/src/test/run-make/coverage-reports/expected_show_coverage.try_error_result.txt new file mode 100644 index 000000000..0ad0180b7 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.try_error_result.txt @@ -0,0 +1,125 @@ + 1| |#![allow(unused_assignments)] + 2| |// expect-exit-status-1 + 3| | + 4| 6|fn call(return_error: bool) -> Result<(),()> { + 5| 6| if return_error { + 6| 1| Err(()) + 7| | } else { + 8| 5| Ok(()) + 9| | } + 10| 6|} + 11| | + 12| 1|fn test1() -> Result<(),()> { + 13| 1| let mut + 14| 1| countdown = 10 + 15| | ; + 16| | for + 17| | _ + 18| | in + 19| 6| 0..10 + 20| | { + 21| 6| countdown + 22| 6| -= 1 + 23| 6| ; + 24| 6| if + 25| 6| countdown < 5 + 26| | { + 27| 1| call(/*return_error=*/ true)?; + 28| 0| call(/*return_error=*/ false)?; + 29| | } + 30| | else + 31| | { + 32| 5| call(/*return_error=*/ false)?; + ^0 + 33| | } + 34| | } + 35| 0| Ok(()) + 36| 1|} + 37| | + 38| |struct Thing1; + 39| |impl Thing1 { + 40| 18| fn get_thing_2(&self, return_error: bool) -> Result<Thing2,()> { + 41| 18| if return_error { + 42| 1| Err(()) + 43| | } else { + 44| 17| Ok(Thing2{}) + 45| | } + 46| 18| } + 47| |} + 48| | + 49| |struct Thing2; + 50| |impl Thing2 { + 51| 17| fn call(&self, return_error: bool) -> Result<u32,()> { + 52| 17| if return_error { + 53| 2| Err(()) + 54| | } else { + 55| 15| Ok(57) + 56| | } + 57| 17| } + 58| |} + 59| | + 60| 1|fn test2() -> Result<(),()> { + 61| 1| let thing1 = Thing1{}; + 62| 1| let mut + 63| 1| countdown = 10 + 64| | ; + 65| | for + 66| | _ + 67| | in + 68| 6| 0..10 + 69| | { + 70| 6| countdown + 71| 6| -= 1 + 72| 6| ; + 73| 6| if + 74| 6| countdown < 5 + 75| | { + 76| 1| thing1.get_thing_2(/*err=*/ false)?.call(/*err=*/ true).expect_err("call should fail"); + ^0 + 77| 1| thing1 + 78| 1| . + 79| 1| get_thing_2(/*return_error=*/ false) + 80| 0| ? + 81| | . + 82| 1| call(/*return_error=*/ true) + 83| 1| . + 84| 1| expect_err( + 85| 1| "call should fail" + 86| 1| ); + 87| 1| let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ true)?; + ^0 ^0 ^0 + 88| 0| assert_eq!(val, 57); + 89| 0| let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ false)?; + 90| 0| assert_eq!(val, 57); + 91| | } + 92| | else + 93| | { + 94| 5| let val = thing1.get_thing_2(/*return_error=*/ false)?.call(/*return_error=*/ false)?; + ^0 ^0 + 95| 5| assert_eq!(val, 57); + 96| 5| let val = thing1 + 97| 5| .get_thing_2(/*return_error=*/ false)? + ^0 + 98| 5| .call(/*return_error=*/ false)?; + ^0 + 99| 5| assert_eq!(val, 57); + 100| 5| let val = thing1 + 101| 5| .get_thing_2(/*return_error=*/ false) + 102| 0| ? + 103| 5| .call(/*return_error=*/ false) + 104| 0| ? + 105| | ; + 106| 5| assert_eq!(val, 57); + 107| | } + 108| | } + 109| 0| Ok(()) + 110| 1|} + 111| | + 112| 1|fn main() -> Result<(),()> { + 113| 1| test1().expect_err("test1 should fail"); + 114| 1| test2() + 115| 1| ? + 116| | ; + 117| 0| Ok(()) + 118| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.unused.txt b/src/test/run-make/coverage-reports/expected_show_coverage.unused.txt new file mode 100644 index 000000000..15fcf21c0 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.unused.txt @@ -0,0 +1,62 @@ + 1| 2|fn foo<T>(x: T) { + 2| 2| let mut i = 0; + 3| 22| while i < 10 { + 4| 20| i != 0 || i != 0; + ^2 + 5| 20| i += 1; + 6| | } + 7| 2|} + ------------------ + | unused::foo::<f32>: + | 1| 1|fn foo<T>(x: T) { + | 2| 1| let mut i = 0; + | 3| 11| while i < 10 { + | 4| 10| i != 0 || i != 0; + | ^1 + | 5| 10| i += 1; + | 6| | } + | 7| 1|} + ------------------ + | unused::foo::<u32>: + | 1| 1|fn foo<T>(x: T) { + | 2| 1| let mut i = 0; + | 3| 11| while i < 10 { + | 4| 10| i != 0 || i != 0; + | ^1 + | 5| 10| i += 1; + | 6| | } + | 7| 1|} + ------------------ + 8| | + 9| 0|fn unused_template_func<T>(x: T) { + 10| 0| let mut i = 0; + 11| 0| while i < 10 { + 12| 0| i != 0 || i != 0; + 13| 0| i += 1; + 14| | } + 15| 0|} + 16| | + 17| 0|fn unused_func(mut a: u32) { + 18| 0| if a != 0 { + 19| 0| a += 1; + 20| 0| } + 21| 0|} + 22| | + 23| 0|fn unused_func2(mut a: u32) { + 24| 0| if a != 0 { + 25| 0| a += 1; + 26| 0| } + 27| 0|} + 28| | + 29| 0|fn unused_func3(mut a: u32) { + 30| 0| if a != 0 { + 31| 0| a += 1; + 32| 0| } + 33| 0|} + 34| | + 35| 1|fn main() -> Result<(), u8> { + 36| 1| foo::<u32>(0); + 37| 1| foo::<f32>(0.0); + 38| 1| Ok(()) + 39| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.unused_mod.txt b/src/test/run-make/coverage-reports/expected_show_coverage.unused_mod.txt new file mode 100644 index 000000000..82d6fccc2 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.unused_mod.txt @@ -0,0 +1,4 @@ + 1| 0|pub fn never_called_function() { + 2| 0| println!("I am never called"); + 3| 0|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.uses_crate.txt b/src/test/run-make/coverage-reports/expected_show_coverage.uses_crate.txt new file mode 100644 index 000000000..65eb1008d --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.uses_crate.txt @@ -0,0 +1,148 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| |// compile-flags: -C opt-level=3 # validates coverage now works with optimizations + 3| |use std::fmt::Debug; + 4| | + 5| 1|pub fn used_function() { + 6| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 7| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 8| 1| // dependent conditions. + 9| 1| let is_true = std::env::args().len() == 1; + 10| 1| let mut countdown = 0; + 11| 1| if is_true { + 12| 1| countdown = 10; + 13| 1| } + ^0 + 14| 1| use_this_lib_crate(); + 15| 1|} + 16| | + 17| 2|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + 18| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + 19| 2|} + ------------------ + | used_crate::used_only_from_bin_crate_generic_function::<&str>: + | 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + | 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | 19| 1|} + ------------------ + | used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>: + | 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + | 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | 19| 1|} + ------------------ + | Unexecuted instantiation: used_crate::used_only_from_bin_crate_generic_function::<_> + ------------------ + 20| |// Expect for above function: `Unexecuted instantiation` (see below) + 21| 2|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + 22| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + 23| 2|} + ------------------ + | used_crate::used_only_from_this_lib_crate_generic_function::<alloc::vec::Vec<i32>>: + | 21| 1|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + | 22| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | 23| 1|} + ------------------ + | used_crate::used_only_from_this_lib_crate_generic_function::<&str>: + | 21| 1|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + | 22| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | 23| 1|} + ------------------ + 24| | + 25| 2|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + 26| 2| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + 27| 2|} + ------------------ + | used_crate::used_from_bin_crate_and_lib_crate_generic_function::<alloc::vec::Vec<i32>>: + | 25| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 26| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 27| 1|} + ------------------ + | used_crate::used_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 25| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 26| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 27| 1|} + ------------------ + 28| | + 29| 2|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + 30| 2| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + 31| 2|} + ------------------ + | used_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 29| 1|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 30| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 31| 1|} + ------------------ + | used_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 29| 1|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 30| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 31| 1|} + ------------------ + 32| | + 33| 0|pub fn unused_generic_function<T: Debug>(arg: T) { + 34| 0| println!("unused_generic_function with {:?}", arg); + 35| 0|} + 36| | + 37| 0|pub fn unused_function() { + 38| 0| let is_true = std::env::args().len() == 1; + 39| 0| let mut countdown = 2; + 40| 0| if !is_true { + 41| 0| countdown = 20; + 42| 0| } + 43| 0|} + 44| | + 45| 0|fn unused_private_function() { + 46| 0| let is_true = std::env::args().len() == 1; + 47| 0| let mut countdown = 2; + 48| 0| if !is_true { + 49| 0| countdown = 20; + 50| 0| } + 51| 0|} + 52| | + 53| 1|fn use_this_lib_crate() { + 54| 1| used_from_bin_crate_and_lib_crate_generic_function("used from library used_crate.rs"); + 55| 1| used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + 56| 1| "used from library used_crate.rs", + 57| 1| ); + 58| 1| let some_vec = vec![5, 6, 7, 8]; + 59| 1| used_only_from_this_lib_crate_generic_function(some_vec); + 60| 1| used_only_from_this_lib_crate_generic_function("used ONLY from library used_crate.rs"); + 61| 1|} + 62| | + 63| |// FIXME(#79651): "Unexecuted instantiation" errors appear in coverage results, + 64| |// for example: + 65| |// + 66| |// | Unexecuted instantiation: used_crate::used_only_from_bin_crate_generic_function::<_> + 67| |// + 68| |// These notices appear when `llvm-cov` shows instantiations. This may be a + 69| |// default option, but it can be suppressed with: + 70| |// + 71| |// ```shell + 72| |// $ `llvm-cov show --show-instantiations=0 ...` + 73| |// ``` + 74| |// + 75| |// The notice is triggered because the function is unused by the library itself, + 76| |// and when the library is compiled, a synthetic function is generated, so + 77| |// unused function coverage can be reported. Coverage can be skipped for unused + 78| |// generic functions with: + 79| |// + 80| |// ```shell + 81| |// $ `rustc -Zunstable-options -C instrument-coverage=except-unused-generics ...` + 82| |// ``` + 83| |// + 84| |// Even though this function is used by `uses_crate.rs` (and + 85| |// counted), with substitutions for `T`, those instantiations are only generated + 86| |// when the generic function is actually used (from the binary, not from this + 87| |// library crate). So the test result shows coverage for all instantiated + 88| |// versions and their generic type substitutions, plus the `Unexecuted + 89| |// instantiation` message for the non-substituted version. This is valid, but + 90| |// unfortunately a little confusing. + 91| |// + 92| |// The library crate has its own coverage map, and the only way to show unused + 93| |// coverage of a generic function is to include the generic function in the + 94| |// coverage map, marked as an "unused function". If the library were used by + 95| |// another binary that never used this generic function, then it would be valid + 96| |// to show the unused generic, with unknown substitution (`_`). + 97| |// + 98| |// The alternative is to exclude all generics from being included in the "unused + 99| |// functions" list, which would then omit coverage results for + 100| |// `unused_generic_function<T>()`, below. + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.uses_inline_crate.txt b/src/test/run-make/coverage-reports/expected_show_coverage.uses_inline_crate.txt new file mode 100644 index 000000000..dab31cbf4 --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.uses_inline_crate.txt @@ -0,0 +1,139 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| |// compile-flags: -C opt-level=3 # validates coverage now works with optimizations + 4| | + 5| |use std::fmt::Debug; + 6| | + 7| 1|pub fn used_function() { + 8| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 9| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 10| 1| // dependent conditions. + 11| 1| let is_true = std::env::args().len() == 1; + 12| 1| let mut countdown = 0; + 13| 1| if is_true { + 14| 1| countdown = 10; + 15| 1| } + ^0 + 16| 1| use_this_lib_crate(); + 17| 1|} + 18| | + 19| |#[inline(always)] + 20| 1|pub fn used_inline_function() { + 21| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 22| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 23| 1| // dependent conditions. + 24| 1| let is_true = std::env::args().len() == 1; + 25| 1| let mut countdown = 0; + 26| 1| if is_true { + 27| 1| countdown = 10; + 28| 1| } + ^0 + 29| 1| use_this_lib_crate(); + 30| 1|} + 31| | + 32| | + 33| | + 34| | + 35| | + 36| | + 37| | + 38| |#[inline(always)] + 39| 2|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + 40| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + 41| 2|} + ------------------ + | used_inline_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>: + | 39| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + | 40| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | 41| 1|} + ------------------ + | used_inline_crate::used_only_from_bin_crate_generic_function::<&str>: + | 39| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + | 40| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | 41| 1|} + ------------------ + | Unexecuted instantiation: used_inline_crate::used_only_from_bin_crate_generic_function::<_> + ------------------ + 42| |// Expect for above function: `Unexecuted instantiation` (see notes in `used_crate.rs`) + 43| | + 44| |#[inline(always)] + 45| 4|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + 46| 4| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + 47| 4|} + ------------------ + | used_inline_crate::used_only_from_this_lib_crate_generic_function::<alloc::vec::Vec<i32>>: + | 45| 2|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + | 46| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | 47| 2|} + ------------------ + | used_inline_crate::used_only_from_this_lib_crate_generic_function::<&str>: + | 45| 2|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + | 46| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | 47| 2|} + ------------------ + 48| | + 49| |#[inline(always)] + 50| 3|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + 51| 3| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + 52| 3|} + ------------------ + | used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function::<alloc::vec::Vec<i32>>: + | 50| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 51| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 52| 1|} + ------------------ + | used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 50| 2|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 51| 2| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 52| 2|} + ------------------ + 53| | + 54| |#[inline(always)] + 55| 3|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + 56| 3| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + 57| 3|} + ------------------ + | used_inline_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 55| 1|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 56| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 57| 1|} + ------------------ + | used_inline_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 55| 2|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 56| 2| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 57| 2|} + ------------------ + 58| | + 59| |#[inline(always)] + 60| 0|pub fn unused_generic_function<T: Debug>(arg: T) { + 61| 0| println!("unused_generic_function with {:?}", arg); + 62| 0|} + 63| | + 64| |#[inline(always)] + 65| 0|pub fn unused_function() { + 66| 0| let is_true = std::env::args().len() == 1; + 67| 0| let mut countdown = 2; + 68| 0| if !is_true { + 69| 0| countdown = 20; + 70| 0| } + 71| 0|} + 72| | + 73| |#[inline(always)] + 74| 0|fn unused_private_function() { + 75| 0| let is_true = std::env::args().len() == 1; + 76| 0| let mut countdown = 2; + 77| 0| if !is_true { + 78| 0| countdown = 20; + 79| 0| } + 80| 0|} + 81| | + 82| 2|fn use_this_lib_crate() { + 83| 2| used_from_bin_crate_and_lib_crate_generic_function("used from library used_crate.rs"); + 84| 2| used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + 85| 2| "used from library used_crate.rs", + 86| 2| ); + 87| 2| let some_vec = vec![5, 6, 7, 8]; + 88| 2| used_only_from_this_lib_crate_generic_function(some_vec); + 89| 2| used_only_from_this_lib_crate_generic_function("used ONLY from library used_crate.rs"); + 90| 2|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.while.txt b/src/test/run-make/coverage-reports/expected_show_coverage.while.txt new file mode 100644 index 000000000..efa7d083f --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.while.txt @@ -0,0 +1,6 @@ + 1| 1|fn main() { + 2| 1| let num = 9; + 3| 1| while num >= 10 { + 4| 0| } + 5| 1|} + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.while_early_ret.txt b/src/test/run-make/coverage-reports/expected_show_coverage.while_early_ret.txt new file mode 100644 index 000000000..d19afc0de --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.while_early_ret.txt @@ -0,0 +1,43 @@ + 1| |#![allow(unused_assignments)] + 2| |// expect-exit-status-1 + 3| | + 4| 1|fn main() -> Result<(),u8> { + 5| 1| let mut countdown = 10; + 6| | while + 7| 7| countdown + 8| 7| > + 9| 7| 0 + 10| | { + 11| | if + 12| 7| countdown + 13| 7| < + 14| 7| 5 + 15| | { + 16| | return + 17| | if + 18| 1| countdown + 19| 1| > + 20| 1| 8 + 21| | { + 22| 0| Ok(()) + 23| | } + 24| | else + 25| | { + 26| 1| Err(1) + 27| | } + 28| | ; + 29| 6| } + 30| 6| countdown + 31| 6| -= + 32| 6| 1 + 33| | ; + 34| | } + 35| 0| Ok(()) + 36| 1|} + 37| | + 38| |// ISSUE(77553): Originally, this test had `Err(1)` on line 22 (instead of `Ok(())`) and + 39| |// `std::process::exit(2)` on line 26 (instead of `Err(1)`); and this worked as expected on Linux + 40| |// and MacOS. But on Windows (MSVC, at least), the call to `std::process::exit()` exits the program + 41| |// without saving the InstrProf coverage counters. The use of `std::process:exit()` is not critical + 42| |// to the coverage test for early returns, but this is a limitation that should be fixed. + diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.yield.txt b/src/test/run-make/coverage-reports/expected_show_coverage.yield.txt new file mode 100644 index 000000000..60a8d943f --- /dev/null +++ b/src/test/run-make/coverage-reports/expected_show_coverage.yield.txt @@ -0,0 +1,38 @@ + 1| |#![feature(generators, generator_trait)] + 2| |#![allow(unused_assignments)] + 3| | + 4| |use std::ops::{Generator, GeneratorState}; + 5| |use std::pin::Pin; + 6| | + 7| 1|fn main() { + 8| 1| let mut generator = || { + 9| 1| yield 1; + 10| 1| return "foo" + 11| | }; + 12| | + 13| 1| match Pin::new(&mut generator).resume(()) { + 14| 1| GeneratorState::Yielded(1) => {} + 15| 0| _ => panic!("unexpected value from resume"), + 16| | } + 17| 1| match Pin::new(&mut generator).resume(()) { + 18| 1| GeneratorState::Complete("foo") => {} + 19| 0| _ => panic!("unexpected value from resume"), + 20| | } + 21| | + 22| 1| let mut generator = || { + 23| 1| yield 1; + 24| 1| yield 2; + 25| 0| yield 3; + 26| 0| return "foo" + 27| | }; + 28| | + 29| 1| match Pin::new(&mut generator).resume(()) { + 30| 1| GeneratorState::Yielded(1) => {} + 31| 0| _ => panic!("unexpected value from resume"), + 32| | } + 33| 1| match Pin::new(&mut generator).resume(()) { + 34| 1| GeneratorState::Yielded(2) => {} + 35| 0| _ => panic!("unexpected value from resume"), + 36| | } + 37| 1|} + diff --git a/src/test/run-make/coverage-reports/normalize_paths.py b/src/test/run-make/coverage-reports/normalize_paths.py new file mode 100755 index 000000000..e5777ad25 --- /dev/null +++ b/src/test/run-make/coverage-reports/normalize_paths.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +from __future__ import print_function + +import sys + +# Normalize file paths in output +for line in sys.stdin: + if line.startswith("..") and line.rstrip().endswith(".rs:"): + print(line.replace("\\", "/"), end='') + else: + print(line, end='') diff --git a/src/test/run-make/coverage/WARNING_KEEP_NAMES_SHORT.txt b/src/test/run-make/coverage/WARNING_KEEP_NAMES_SHORT.txt new file mode 100644 index 000000000..6a1403b8a --- /dev/null +++ b/src/test/run-make/coverage/WARNING_KEEP_NAMES_SHORT.txt @@ -0,0 +1,10 @@ +IMPORTANT: The Rust test programs in this directory generate various output +files in the `../coverage*` directories (`expected` and `actual` files). + +Microsoft Windows has a relatively short limit on file paths (not individual +path components, but the entire path). The files generated by these +`../coverage*` tests typically have file paths that include the program +source file name plus function and type names (depending on the program). + +Keep the test file names short, and keep function names and other symbols +short as well, to avoid hitting the Windows limits. diff --git a/src/test/run-make/coverage/abort.rs b/src/test/run-make/coverage/abort.rs new file mode 100644 index 000000000..3dac43df8 --- /dev/null +++ b/src/test/run-make/coverage/abort.rs @@ -0,0 +1,66 @@ +#![feature(c_unwind)] +#![allow(unused_assignments)] + +extern "C" fn might_abort(should_abort: bool) { + if should_abort { + println!("aborting..."); + panic!("panics and aborts"); + } else { + println!("Don't Panic"); + } +} + +fn main() -> Result<(), u8> { + let mut countdown = 10; + while countdown > 0 { + if countdown < 5 { + might_abort(false); + } + // See discussion (below the `Notes` section) on coverage results for the closing brace. + if countdown < 5 { might_abort(false); } // Counts for different regions on one line. + // For the following example, the closing brace is the last character on the line. + // This shows the character after the closing brace is highlighted, even if that next + // character is a newline. + if countdown < 5 { might_abort(false); } + countdown -= 1; + } + Ok(()) +} + +// Notes: +// 1. Compare this program and its coverage results to those of the similar tests +// `panic_unwind.rs` and `try_error_result.rs`. +// 2. This test confirms the coverage generated when a program includes `TerminatorKind::Abort`. +// 3. The test does not invoke the abort. By executing to a successful completion, the coverage +// results show where the program did and did not execute. +// 4. If the program actually aborted, the coverage counters would not be saved (which "works as +// intended"). Coverage results would show no executed coverage regions. +// 6. If `should_abort` is `true` and the program aborts, the program exits with a `132` status +// (on Linux at least). + +/* + +Expect the following coverage results: + +```text + 16| 11| while countdown > 0 { + 17| 10| if countdown < 5 { + 18| 4| might_abort(false); + 19| 6| } +``` + +This is actually correct. + +The condition `countdown < 5` executed 10 times (10 loop iterations). + +It evaluated to `true` 4 times, and executed the `might_abort()` call. + +It skipped the body of the `might_abort()` call 6 times. If an `if` does not include an explicit +`else`, the coverage implementation injects a counter, at the character immediately after the `if`s +closing brace, to count the "implicit" `else`. This is the only way to capture the coverage of the +non-true condition. + +As another example of why this is important, say the condition was `countdown < 50`, which is always +`true`. In that case, we wouldn't have a test for what happens if `might_abort()` is not called. +The closing brace would have a count of `0`, highlighting the missed coverage. +*/ diff --git a/src/test/run-make/coverage/assert.rs b/src/test/run-make/coverage/assert.rs new file mode 100644 index 000000000..c85f2748e --- /dev/null +++ b/src/test/run-make/coverage/assert.rs @@ -0,0 +1,32 @@ +#![allow(unused_assignments)] +// expect-exit-status-101 + +fn might_fail_assert(one_plus_one: u32) { + println!("does 1 + 1 = {}?", one_plus_one); + assert_eq!(1 + 1, one_plus_one, "the argument was wrong"); +} + +fn main() -> Result<(),u8> { + let mut countdown = 10; + while countdown > 0 { + if countdown == 1 { + might_fail_assert(3); + } else if countdown < 5 { + might_fail_assert(2); + } + countdown -= 1; + } + Ok(()) +} + +// Notes: +// 1. Compare this program and its coverage results to those of the very similar test +// `panic_unwind.rs`, and similar tests `abort.rs` and `try_error_result.rs`. +// 2. This test confirms the coverage generated when a program passes or fails an `assert!()` or +// related `assert_*!()` macro. +// 3. Notably, the `assert` macros *do not* generate `TerminatorKind::Assert`. The macros produce +// conditional expressions, `TerminatorKind::SwitchInt` branches, and a possible call to +// `begin_panic_fmt()` (that begins a panic unwind, if the assertion test fails). +// 4. `TerminatoKind::Assert` is, however, also present in the MIR generated for this test +// (and in many other coverage tests). The `Assert` terminator is typically generated by the +// Rust compiler to check for runtime failures, such as numeric overflows. diff --git a/src/test/run-make/coverage/async.rs b/src/test/run-make/coverage/async.rs new file mode 100644 index 000000000..a6e387747 --- /dev/null +++ b/src/test/run-make/coverage/async.rs @@ -0,0 +1,128 @@ +#![allow(unused_assignments, dead_code)] + +// compile-flags: --edition=2018 -C opt-level=1 + +async fn c(x: u8) -> u8 { + if x == 8 { + 1 + } else { + 0 + } +} + +async fn d() -> u8 { 1 } + +async fn e() -> u8 { 1 } // unused function; executor does not block on `g()` + +async fn f() -> u8 { 1 } + +async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()` + +pub async fn g(x: u8) { + match x { + y if e().await == y => (), + y if f().await == y => (), + _ => (), + } +} + +async fn h(x: usize) { // The function signature is counted when called, but the body is not + // executed (not awaited) so the open brace has a `0` count (at least when + // displayed with `llvm-cov show` in color-mode). + match x { + y if foo().await[y] => (), + _ => (), + } +} + +async fn i(x: u8) { // line coverage is 1, but there are 2 regions: + // (a) the function signature, counted when the function is called; and + // (b) the open brace for the function body, counted once when the body is + // executed asynchronously. + match x { + y if c(x).await == y + 1 => { d().await; } + y if f().await == y + 1 => (), + _ => (), + } +} + +fn j(x: u8) { + // non-async versions of `c()`, `d()`, and `f()` to make it similar to async `i()`. + fn c(x: u8) -> u8 { + if x == 8 { + 1 // This line appears covered, but the 1-character expression span covering the `1` + // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because + // `fn j()` executes the open brace for the funciton body, followed by the function's + // first executable statement, `match x`. Inner function declarations are not + // "visible" to the MIR for `j()`, so the code region counts all lines between the + // open brace and the first statement as executed, which is, in a sense, true. + // `llvm-cov show` overcomes this kind of situation by showing the actual counts + // of the enclosed coverages, (that is, the `1` expression was not executed, and + // accurately displays a `0`). + } else { + 0 + } + } + fn d() -> u8 { 1 } // inner function is defined in-line, but the function is not executed + fn f() -> u8 { 1 } + match x { + y if c(x) == y + 1 => { d(); } + y if f() == y + 1 => (), + _ => (), + } +} + +fn k(x: u8) { // unused function + match x { + 1 => (), + 2 => (), + _ => (), + } +} + +fn l(x: u8) { + match x { + 1 => (), + 2 => (), + _ => (), + } +} + +async fn m(x: u8) -> u8 { x - 1 } + +fn main() { + let _ = g(10); + let _ = h(9); + let mut future = Box::pin(i(8)); + j(7); + l(6); + let _ = m(5); + executor::block_on(future.as_mut()); +} + +mod executor { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + pub fn block_on<F: Future>(mut future: F) -> F::Output { + let mut future = unsafe { Pin::new_unchecked(&mut future) }; + use std::hint::unreachable_unchecked; + static VTABLE: RawWakerVTable = RawWakerVTable::new( + |_| unsafe { unreachable_unchecked() }, // clone + |_| unsafe { unreachable_unchecked() }, // wake + |_| unsafe { unreachable_unchecked() }, // wake_by_ref + |_| (), + ); + let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + let mut context = Context::from_waker(&waker); + + loop { + if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + break val; + } + } + } +} diff --git a/src/test/run-make/coverage/async2.rs b/src/test/run-make/coverage/async2.rs new file mode 100644 index 000000000..959d48ce9 --- /dev/null +++ b/src/test/run-make/coverage/async2.rs @@ -0,0 +1,69 @@ +// compile-flags: --edition=2018 + +use core::{ + future::Future, + marker::Send, + pin::Pin, +}; + +fn non_async_func() { + println!("non_async_func was covered"); + let b = true; + if b { + println!("non_async_func println in block"); + } +} + + + + +async fn async_func() { + println!("async_func was covered"); + let b = true; + if b { + println!("async_func println in block"); + } +} + + + + +async fn async_func_just_println() { + println!("async_func_just_println was covered"); +} + +fn main() { + println!("codecovsample::main"); + + non_async_func(); + + executor::block_on(async_func()); + executor::block_on(async_func_just_println()); +} + +mod executor { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + pub fn block_on<F: Future>(mut future: F) -> F::Output { + let mut future = unsafe { Pin::new_unchecked(&mut future) }; + use std::hint::unreachable_unchecked; + static VTABLE: RawWakerVTable = RawWakerVTable::new( + |_| unsafe { unreachable_unchecked() }, // clone + |_| unsafe { unreachable_unchecked() }, // wake + |_| unsafe { unreachable_unchecked() }, // wake_by_ref + |_| (), + ); + let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + let mut context = Context::from_waker(&waker); + + loop { + if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + break val; + } + } + } +} diff --git a/src/test/run-make/coverage/closure.rs b/src/test/run-make/coverage/closure.rs new file mode 100644 index 000000000..32ec0bcdf --- /dev/null +++ b/src/test/run-make/coverage/closure.rs @@ -0,0 +1,215 @@ +#![allow(unused_assignments, unused_variables)] +// compile-flags: -C opt-level=2 # fix described in rustc_middle/mir/mono.rs +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let is_false = ! is_true; + + let mut some_string = Some(String::from("the string content")); + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 1".to_owned() + } + ) + ); + + some_string = Some(String::from("the string content")); + let + a + = + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 2".to_owned() + }; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + a + ) + ); + + some_string = None; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 3".to_owned() + } + ) + ); + + some_string = None; + let + a + = + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 4".to_owned() + }; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + a + ) + ); + + let + quote_closure + = + |val| + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + format!("'{}'", val) + }; + println!( + "Repeated, quoted string: {:?}" + , + std::iter::repeat("repeat me") + .take(5) + .map + ( + quote_closure + ) + .collect::<Vec<_>>() + ); + + let + _unused_closure + = + | + mut countdown + | + { + if is_false { + countdown = 10; + } + "closure should be unused".to_owned() + }; + + let mut countdown = 10; + let _short_unused_closure = | _unused_arg: u8 | countdown += 1; + + + let short_used_covered_closure_macro = | used_arg: u8 | println!("called"); + let short_used_not_covered_closure_macro = | used_arg: u8 | println!("not called"); + let _short_unused_closure_macro = | _unused_arg: u8 | println!("not called"); + + + + + let _short_unused_closure_block = | _unused_arg: u8 | { println!("not called") }; + + let _shortish_unused_closure = | _unused_arg: u8 | { + println!("not called") + }; + + let _as_short_unused_closure = | + _unused_arg: u8 + | { println!("not called") }; + + let _almost_as_short_unused_closure = | + _unused_arg: u8 + | { println!("not called") } + ; + + + + + + let _short_unused_closure_line_break_no_block = | _unused_arg: u8 | +println!("not called") + ; + + let _short_unused_closure_line_break_no_block2 = + | _unused_arg: u8 | + println!( + "not called" + ) + ; + + let short_used_not_covered_closure_line_break_no_block_embedded_branch = + | _unused_arg: u8 | + println!( + "not called: {}", + if is_true { "check" } else { "me" } + ) + ; + + let short_used_not_covered_closure_line_break_block_embedded_branch = + | _unused_arg: u8 | + { + println!( + "not called: {}", + if is_true { "check" } else { "me" } + ) + } + ; + + let short_used_covered_closure_line_break_no_block_embedded_branch = + | _unused_arg: u8 | + println!( + "not called: {}", + if is_true { "check" } else { "me" } + ) + ; + + let short_used_covered_closure_line_break_block_embedded_branch = + | _unused_arg: u8 | + { + println!( + "not called: {}", + if is_true { "check" } else { "me" } + ) + } + ; + + if is_false { + short_used_not_covered_closure_macro(0); + short_used_not_covered_closure_line_break_no_block_embedded_branch(0); + short_used_not_covered_closure_line_break_block_embedded_branch(0); + } + short_used_covered_closure_macro(0); + short_used_covered_closure_line_break_no_block_embedded_branch(0); + short_used_covered_closure_line_break_block_embedded_branch(0); +} diff --git a/src/test/run-make/coverage/closure_macro.rs b/src/test/run-make/coverage/closure_macro.rs new file mode 100644 index 000000000..5e3b00d1e --- /dev/null +++ b/src/test/run-make/coverage/closure_macro.rs @@ -0,0 +1,40 @@ +// compile-flags: --edition=2018 +#![feature(no_coverage)] + +macro_rules! bail { + ($msg:literal $(,)?) => { + if $msg.len() > 0 { + println!("no msg"); + } else { + println!($msg); + } + return Err(String::from($msg)); + }; +} + +macro_rules! on_error { + ($value:expr, $error_message:expr) => { + $value.or_else(|e| { // FIXME(85000): no coverage in closure macros + let message = format!($error_message, e); + if message.len() > 0 { + println!("{}", message); + Ok(String::from("ok")) + } else { + bail!("error"); + } + }) + }; +} + +fn load_configuration_files() -> Result<String, String> { + Ok(String::from("config")) +} + +pub fn main() -> Result<(), String> { + println!("Starting service"); + let config = on_error!(load_configuration_files(), "Error loading configs: {}")?; + + let startup_delay_duration = String::from("arg"); + let _ = (config, startup_delay_duration); + Ok(()) +} diff --git a/src/test/run-make/coverage/closure_macro_async.rs b/src/test/run-make/coverage/closure_macro_async.rs new file mode 100644 index 000000000..e3e89e9c8 --- /dev/null +++ b/src/test/run-make/coverage/closure_macro_async.rs @@ -0,0 +1,81 @@ +// compile-flags: --edition=2018 +#![feature(no_coverage)] + +macro_rules! bail { + ($msg:literal $(,)?) => { + if $msg.len() > 0 { + println!("no msg"); + } else { + println!($msg); + } + return Err(String::from($msg)); + }; +} + +macro_rules! on_error { + ($value:expr, $error_message:expr) => { + $value.or_else(|e| { // FIXME(85000): no coverage in closure macros + let message = format!($error_message, e); + if message.len() > 0 { + println!("{}", message); + Ok(String::from("ok")) + } else { + bail!("error"); + } + }) + }; +} + +fn load_configuration_files() -> Result<String, String> { + Ok(String::from("config")) +} + +pub async fn test() -> Result<(), String> { + println!("Starting service"); + let config = on_error!(load_configuration_files(), "Error loading configs: {}")?; + + let startup_delay_duration = String::from("arg"); + let _ = (config, startup_delay_duration); + Ok(()) +} + +#[no_coverage] +fn main() { + executor::block_on(test()); +} + +mod executor { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + #[no_coverage] + pub fn block_on<F: Future>(mut future: F) -> F::Output { + let mut future = unsafe { Pin::new_unchecked(&mut future) }; + use std::hint::unreachable_unchecked; + static VTABLE: RawWakerVTable = RawWakerVTable::new( + + #[no_coverage] + |_| unsafe { unreachable_unchecked() }, // clone + + #[no_coverage] + |_| unsafe { unreachable_unchecked() }, // wake + + #[no_coverage] + |_| unsafe { unreachable_unchecked() }, // wake_by_ref + + #[no_coverage] + |_| (), + ); + let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + let mut context = Context::from_waker(&waker); + + loop { + if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + break val; + } + } + } +} diff --git a/src/test/run-make/coverage/compiletest-ignore-dir b/src/test/run-make/coverage/compiletest-ignore-dir new file mode 100644 index 000000000..b533b272d --- /dev/null +++ b/src/test/run-make/coverage/compiletest-ignore-dir @@ -0,0 +1,3 @@ +# Directory "coverage" supports the tests at prefix ../coverage-* + +# Use ./x.py [options] test src/test/run-make/coverage to run all related tests. diff --git a/src/test/run-make/coverage/conditions.rs b/src/test/run-make/coverage/conditions.rs new file mode 100644 index 000000000..057599d1b --- /dev/null +++ b/src/test/run-make/coverage/conditions.rs @@ -0,0 +1,87 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + let mut countdown = 0; + if true { + countdown = 10; + } + + const B: u32 = 100; + let x = if countdown > 7 { + countdown -= 4; + B + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + countdown + } else { + return; + }; + + let mut countdown = 0; + if true { + countdown = 10; + } + + if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + } + + if true { + let mut countdown = 0; + if true { + countdown = 10; + } + + if countdown > 7 { + countdown -= 4; + } + else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + } + } + + + let mut countdown = 0; + if true { + countdown = 1; + } + + let z = if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + let should_be_reachable = countdown; + println!("reached"); + return; + }; + + let w = if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + }; +} diff --git a/src/test/run-make/coverage/continue.rs b/src/test/run-make/coverage/continue.rs new file mode 100644 index 000000000..624aa9834 --- /dev/null +++ b/src/test/run-make/coverage/continue.rs @@ -0,0 +1,69 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + let is_true = std::env::args().len() == 1; + + let mut x = 0; + for _ in 0..10 { + match is_true { + true => { + continue; + } + _ => { + x = 1; + } + } + x = 3; + } + for _ in 0..10 { + match is_true { + false => { + x = 1; + } + _ => { + continue; + } + } + x = 3; + } + for _ in 0..10 { + match is_true { + true => { + x = 1; + } + _ => { + continue; + } + } + x = 3; + } + for _ in 0..10 { + if is_true { + continue; + } + x = 3; + } + for _ in 0..10 { + match is_true { + false => { + x = 1; + } + _ => { + let _ = x; + } + } + x = 3; + } + for _ in 0..10 { + match is_true { + false => { + x = 1; + } + _ => { + break; + } + } + x = 3; + } + let _ = x; +} diff --git a/src/test/run-make/coverage/coverage_tools.mk b/src/test/run-make/coverage/coverage_tools.mk new file mode 100644 index 000000000..0b6bbc331 --- /dev/null +++ b/src/test/run-make/coverage/coverage_tools.mk @@ -0,0 +1,6 @@ +# Common Makefile include for Rust `run-make/coverage-* tests. Include this +# file with the line: +# +# include ../coverage/coverage_tools.mk + +include ../../run-make-fulldeps/tools.mk diff --git a/src/test/run-make/coverage/dead_code.rs b/src/test/run-make/coverage/dead_code.rs new file mode 100644 index 000000000..a1285df0e --- /dev/null +++ b/src/test/run-make/coverage/dead_code.rs @@ -0,0 +1,37 @@ +#![allow(unused_assignments, unused_variables)] + +pub fn unused_pub_fn_not_in_library() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if is_true { + countdown = 10; + } +} + +fn unused_fn() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if is_true { + countdown = 10; + } +} + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if is_true { + countdown = 10; + } +} diff --git a/src/test/run-make/coverage/doctest.rs b/src/test/run-make/coverage/doctest.rs new file mode 100644 index 000000000..ec04ea570 --- /dev/null +++ b/src/test/run-make/coverage/doctest.rs @@ -0,0 +1,99 @@ +//! This test ensures that code from doctests is properly re-mapped. +//! See <https://github.com/rust-lang/rust/issues/79417> for more info. +//! +//! Just some random code: +//! ``` +//! if true { +//! // this is executed! +//! assert_eq!(1, 1); +//! } else { +//! // this is not! +//! assert_eq!(1, 2); +//! } +//! ``` +//! +//! doctest testing external code: +//! ``` +//! extern crate doctest_crate; +//! doctest_crate::fn_run_in_doctests(1); +//! ``` +//! +//! doctest returning a result: +//! ``` +//! #[derive(Debug, PartialEq)] +//! struct SomeError { +//! msg: String, +//! } +//! let mut res = Err(SomeError { msg: String::from("a message") }); +//! if res.is_ok() { +//! res?; +//! } else { +//! if *res.as_ref().unwrap_err() == *res.as_ref().unwrap_err() { +//! println!("{:?}", res); +//! } +//! if *res.as_ref().unwrap_err() == *res.as_ref().unwrap_err() { +//! res = Ok(1); +//! } +//! res = Ok(0); +//! } +//! // need to be explicit because rustdoc cant infer the return type +//! Ok::<(), SomeError>(()) +//! ``` +//! +//! doctest with custom main: +//! ``` +//! fn some_func() { +//! println!("called some_func()"); +//! } +//! +//! #[derive(Debug)] +//! struct SomeError; +//! +//! extern crate doctest_crate; +//! +//! fn doctest_main() -> Result<(), SomeError> { +//! some_func(); +//! doctest_crate::fn_run_in_doctests(2); +//! Ok(()) +//! } +//! +//! // this `main` is not shown as covered, as it clashes with all the other +//! // `main` functions that were automatically generated for doctests +//! fn main() -> Result<(), SomeError> { +//! doctest_main() +//! } +//! ``` + +/// doctest attached to fn testing external code: +/// ``` +/// extern crate doctest_crate; +/// doctest_crate::fn_run_in_doctests(3); +/// ``` +/// +fn main() { + if true { + assert_eq!(1, 1); + } else { + assert_eq!(1, 2); + } +} + +// FIXME(Swatinem): Fix known issue that coverage code region columns need to be offset by the +// doc comment line prefix (`///` or `//!`) and any additional indent (before or after the doc +// comment characters). This test produces `llvm-cov show` results demonstrating the problem. +// +// One of the above tests now includes: `derive(Debug, PartialEq)`, producing an `llvm-cov show` +// result with a distinct count for `Debug`, denoted by `^1`, but the caret points to the wrong +// column. Similarly, the `if` blocks without `else` blocks show `^0`, which should point at, or +// one character past, the `if` block's closing brace. In both cases, these are most likely off +// by the number of characters stripped from the beginning of each doc comment line: indent +// whitespace, if any, doc comment prefix (`//!` in this case) and (I assume) one space character +// (?). Note, when viewing `llvm-cov show` results in `--color` mode, the column offset errors are +// more pronounced, and show up in more places, with background color used to show some distinct +// code regions with different coverage counts. +// +// NOTE: Since the doc comment line prefix may vary, one possible solution is to replace each +// character stripped from the beginning of doc comment lines with a space. This will give coverage +// results the correct column offsets, and I think it should compile correctly, but I don't know +// what affect it might have on diagnostic messages from the compiler, and whether anyone would care +// if the indentation changed. I don't know if there is a more viable solution. diff --git a/src/test/run-make/coverage/drop_trait.rs b/src/test/run-make/coverage/drop_trait.rs new file mode 100644 index 000000000..d15bfc0f8 --- /dev/null +++ b/src/test/run-make/coverage/drop_trait.rs @@ -0,0 +1,33 @@ +#![allow(unused_assignments)] +// expect-exit-status-1 + +struct Firework { + strength: i32, +} + +impl Drop for Firework { + fn drop(&mut self) { + println!("BOOM times {}!!!", self.strength); + } +} + +fn main() -> Result<(),u8> { + let _firecracker = Firework { strength: 1 }; + + let _tnt = Firework { strength: 100 }; + + if true { + println!("Exiting with error..."); + return Err(1); + } + + let _ = Firework { strength: 1000 }; + + Ok(()) +} + +// Expected program output: +// Exiting with error... +// BOOM times 100!!! +// BOOM times 1!!! +// Error: 1 diff --git a/src/test/run-make/coverage/generator.rs b/src/test/run-make/coverage/generator.rs new file mode 100644 index 000000000..431999102 --- /dev/null +++ b/src/test/run-make/coverage/generator.rs @@ -0,0 +1,30 @@ +#![feature(generators, generator_trait)] + +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; + +// The following implementation of a function called from a `yield` statement +// (apparently requiring the Result and the `String` type or constructor) +// creates conditions where the `generator::StateTransform` MIR transform will +// drop all `Counter` `Coverage` statements from a MIR. `simplify.rs` has logic +// to handle this condition, and still report dead block coverage. +fn get_u32(val: bool) -> Result<u32, String> { + if val { Ok(1) } else { Err(String::from("some error")) } +} + +fn main() { + let is_true = std::env::args().len() == 1; + let mut generator = || { + yield get_u32(is_true); + return "foo"; + }; + + match Pin::new(&mut generator).resume(()) { + GeneratorState::Yielded(Ok(1)) => {} + _ => panic!("unexpected return from resume"), + } + match Pin::new(&mut generator).resume(()) { + GeneratorState::Complete("foo") => {} + _ => panic!("unexpected return from resume"), + } +} diff --git a/src/test/run-make/coverage/generics.rs b/src/test/run-make/coverage/generics.rs new file mode 100644 index 000000000..18b388684 --- /dev/null +++ b/src/test/run-make/coverage/generics.rs @@ -0,0 +1,48 @@ +#![allow(unused_assignments)] +// expect-exit-status-1 + +struct Firework<T> where T: Copy + std::fmt::Display { + strength: T, +} + +impl<T> Firework<T> where T: Copy + std::fmt::Display { + #[inline(always)] + fn set_strength(&mut self, new_strength: T) { + self.strength = new_strength; + } +} + +impl<T> Drop for Firework<T> where T: Copy + std::fmt::Display { + #[inline(always)] + fn drop(&mut self) { + println!("BOOM times {}!!!", self.strength); + } +} + +fn main() -> Result<(),u8> { + let mut firecracker = Firework { strength: 1 }; + firecracker.set_strength(2); + + let mut tnt = Firework { strength: 100.1 }; + tnt.set_strength(200.1); + tnt.set_strength(300.3); + + if true { + println!("Exiting with error..."); + return Err(1); + } + + + + + + let _ = Firework { strength: 1000 }; + + Ok(()) +} + +// Expected program output: +// Exiting with error... +// BOOM times 100!!! +// BOOM times 1!!! +// Error: 1 diff --git a/src/test/run-make/coverage/if.rs b/src/test/run-make/coverage/if.rs new file mode 100644 index 000000000..8ad5042ff --- /dev/null +++ b/src/test/run-make/coverage/if.rs @@ -0,0 +1,28 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let + is_true + = + std::env::args().len() + == + 1 + ; + let + mut + countdown + = + 0 + ; + if + is_true + { + countdown + = + 10 + ; + } +} diff --git a/src/test/run-make/coverage/if_else.rs b/src/test/run-make/coverage/if_else.rs new file mode 100644 index 000000000..3244e1e3a --- /dev/null +++ b/src/test/run-make/coverage/if_else.rs @@ -0,0 +1,40 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if + is_true + { + countdown + = + 10 + ; + } + else // Note coverage region difference without semicolon + { + countdown + = + 100 + } + + if + is_true + { + countdown + = + 10 + ; + } + else + { + countdown + = + 100 + ; + } +} diff --git a/src/test/run-make/coverage/inline-dead.rs b/src/test/run-make/coverage/inline-dead.rs new file mode 100644 index 000000000..854fa0629 --- /dev/null +++ b/src/test/run-make/coverage/inline-dead.rs @@ -0,0 +1,27 @@ +// Regression test for issue #98833. +// compile-flags: -Zinline-mir -Cdebug-assertions=off + +fn main() { + println!("{}", live::<false>()); + + let f = |x: bool| { + debug_assert!( + x + ); + }; + f(false); +} + +#[inline] +fn live<const B: bool>() -> u32 { + if B { + dead() + } else { + 0 + } +} + +#[inline] +fn dead() -> u32 { + 42 +} diff --git a/src/test/run-make/coverage/inline.rs b/src/test/run-make/coverage/inline.rs new file mode 100644 index 000000000..9cfab9ddb --- /dev/null +++ b/src/test/run-make/coverage/inline.rs @@ -0,0 +1,51 @@ +// compile-flags: -Zinline-mir + +use std::fmt::Display; + +fn main() { + permutations(&['a', 'b', 'c']); +} + +#[inline(always)] +fn permutations<T: Copy + Display>(xs: &[T]) { + let mut ys = xs.to_owned(); + permutate(&mut ys, 0); +} + +fn permutate<T: Copy + Display>(xs: &mut [T], k: usize) { + let n = length(xs); + if k == n { + display(xs); + } else if k < n { + for i in k..n { + swap(xs, i, k); + permutate(xs, k + 1); + swap(xs, i, k); + } + } else { + error(); + } +} + +fn length<T>(xs: &[T]) -> usize { + xs.len() +} + +#[inline] +fn swap<T: Copy>(xs: &mut [T], i: usize, j: usize) { + let t = xs[i]; + xs[i] = xs[j]; + xs[j] = t; +} + +fn display<T: Display>(xs: &[T]) { + for x in xs { + print!("{}", x); + } + println!(); +} + +#[inline(always)] +fn error() { + panic!("error"); +} diff --git a/src/test/run-make/coverage/inner_items.rs b/src/test/run-make/coverage/inner_items.rs new file mode 100644 index 000000000..bcb62b303 --- /dev/null +++ b/src/test/run-make/coverage/inner_items.rs @@ -0,0 +1,57 @@ +#![allow(unused_assignments, unused_variables, dead_code)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if is_true { + countdown = 10; + } + + mod in_mod { + const IN_MOD_CONST: u32 = 1000; + } + + fn in_func(a: u32) { + let b = 1; + let c = a + b; + println!("c = {}", c) + } + + struct InStruct { + in_struct_field: u32, + } + + const IN_CONST: u32 = 1234; + + trait InTrait { + fn trait_func(&mut self, incr: u32); + + fn default_trait_func(&mut self) { + in_func(IN_CONST); + self.trait_func(IN_CONST); + } + } + + impl InTrait for InStruct { + fn trait_func(&mut self, incr: u32) { + self.in_struct_field += incr; + in_func(self.in_struct_field); + } + } + + type InType = String; + + if is_true { + in_func(countdown); + } + + let mut val = InStruct { + in_struct_field: 101, + }; + + val.default_trait_func(); +} diff --git a/src/test/run-make/coverage/issue-83601.rs b/src/test/run-make/coverage/issue-83601.rs new file mode 100644 index 000000000..0b72a8194 --- /dev/null +++ b/src/test/run-make/coverage/issue-83601.rs @@ -0,0 +1,14 @@ +// Shows that rust-lang/rust/83601 is resolved + +#[derive(Debug, PartialEq, Eq)] +struct Foo(u32); + +fn main() { + let bar = Foo(1); + assert_eq!(bar, Foo(1)); + let baz = Foo(0); + assert_ne!(baz, Foo(1)); + println!("{:?}", Foo(1)); + println!("{:?}", bar); + println!("{:?}", baz); +} diff --git a/src/test/run-make/coverage/issue-84561.rs b/src/test/run-make/coverage/issue-84561.rs new file mode 100644 index 000000000..b39a289c4 --- /dev/null +++ b/src/test/run-make/coverage/issue-84561.rs @@ -0,0 +1,182 @@ +// This demonstrated Issue #84561: function-like macros produce unintuitive coverage results. + +// expect-exit-status-101 +#[derive(PartialEq, Eq)] +struct Foo(u32); +fn test3() { + let is_true = std::env::args().len() == 1; + let bar = Foo(1); + assert_eq!(bar, Foo(1)); + let baz = Foo(0); + assert_ne!(baz, Foo(1)); + println!("{:?}", Foo(1)); + println!("{:?}", bar); + println!("{:?}", baz); + + assert_eq!(Foo(1), Foo(1)); + assert_ne!(Foo(0), Foo(1)); + assert_eq!(Foo(2), Foo(2)); + let bar = Foo(0); + assert_ne!(bar, Foo(3)); + assert_ne!(Foo(0), Foo(4)); + assert_eq!(Foo(3), Foo(3), "with a message"); + println!("{:?}", bar); + println!("{:?}", Foo(1)); + + assert_ne!(Foo(0), Foo(5), "{}", if is_true { "true message" } else { "false message" }); + assert_ne!( + Foo(0) + , + Foo(5) + , + "{}" + , + if + is_true + { + "true message" + } else { + "false message" + } + ); + + let is_true = std::env::args().len() == 1; + + assert_eq!( + Foo(1), + Foo(1) + ); + assert_ne!( + Foo(0), + Foo(1) + ); + assert_eq!( + Foo(2), + Foo(2) + ); + let bar = Foo(1); + assert_ne!( + bar, + Foo(3) + ); + if is_true { + assert_ne!( + Foo(0), + Foo(4) + ); + } else { + assert_eq!( + Foo(3), + Foo(3) + ); + } + if is_true { + assert_ne!( + Foo(0), + Foo(4), + "with a message" + ); + } else { + assert_eq!( + Foo(3), + Foo(3), + "with a message" + ); + } + assert_ne!( + if is_true { + Foo(0) + } else { + Foo(1) + }, + Foo(5) + ); + assert_ne!( + Foo(5), + if is_true { + Foo(0) + } else { + Foo(1) + } + ); + assert_ne!( + if is_true { + assert_eq!( + Foo(3), + Foo(3) + ); + Foo(0) + } else { + assert_ne!( + if is_true { + Foo(0) + } else { + Foo(1) + }, + Foo(5) + ); + Foo(1) + }, + Foo(5), + "with a message" + ); + assert_eq!( + Foo(1), + Foo(3), + "this assert should fail" + ); + assert_eq!( + Foo(3), + Foo(3), + "this assert should not be reached" + ); +} + +impl std::fmt::Debug for Foo { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "try and succeed")?; + Ok(()) + } +} + +static mut DEBUG_LEVEL_ENABLED: bool = false; + +macro_rules! debug { + ($($arg:tt)+) => ( + if unsafe { DEBUG_LEVEL_ENABLED } { + println!($($arg)+); + } + ); +} + +fn test1() { + debug!("debug is enabled"); + debug!("debug is enabled"); + let _ = 0; + debug!("debug is enabled"); + unsafe { + DEBUG_LEVEL_ENABLED = true; + } + debug!("debug is enabled"); +} + +macro_rules! call_debug { + ($($arg:tt)+) => ( + fn call_print(s: &str) { + print!("{}", s); + } + + call_print("called from call_debug: "); + debug!($($arg)+); + ); +} + +fn test2() { + call_debug!("debug is enabled"); +} + +fn main() { + test1(); + test2(); + test3(); +} diff --git a/src/test/run-make/coverage/issue-85461.rs b/src/test/run-make/coverage/issue-85461.rs new file mode 100644 index 000000000..a1b9ebb1e --- /dev/null +++ b/src/test/run-make/coverage/issue-85461.rs @@ -0,0 +1,10 @@ +// Regression test for #85461: MSVC sometimes fail to link with dead code and #[inline(always)] + +extern crate inline_always_with_dead_code; + +use inline_always_with_dead_code::{bar, baz}; + +fn main() { + bar::call_me(); + baz::call_me(); +} diff --git a/src/test/run-make/coverage/issue-93054.rs b/src/test/run-make/coverage/issue-93054.rs new file mode 100644 index 000000000..c160b3db0 --- /dev/null +++ b/src/test/run-make/coverage/issue-93054.rs @@ -0,0 +1,28 @@ +// Regression test for #93054: Functions using uninhabited types often only have a single, +// unreachable basic block which doesn't get instrumented. This should not cause llvm-cov to fail. +// Since these kinds functions can't be invoked anyway, it's ok to not have coverage data for them. + +// compile-flags: --edition=2021 + +enum Never { } + +impl Never { + fn foo(self) { + match self { } + make().map(|never| match never { }); + } + + fn bar(&self) { + match *self { } + } +} + +async fn foo2(never: Never) { + match never { } +} + +fn make() -> Option<Never> { + None +} + +fn main() { } diff --git a/src/test/run-make/coverage/lazy_boolean.rs b/src/test/run-make/coverage/lazy_boolean.rs new file mode 100644 index 000000000..bb6219e85 --- /dev/null +++ b/src/test/run-make/coverage/lazy_boolean.rs @@ -0,0 +1,61 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let (mut a, mut b, mut c) = (0, 0, 0); + if is_true { + a = 1; + b = 10; + c = 100; + } + let + somebool + = + a < b + || + b < c + ; + let + somebool + = + b < a + || + b < c + ; + let somebool = a < b && b < c; + let somebool = b < a && b < c; + + if + ! + is_true + { + a = 2 + ; + } + + if + is_true + { + b = 30 + ; + } + else + { + c = 400 + ; + } + + if !is_true { + a = 2; + } + + if is_true { + b = 30; + } else { + c = 400; + } +} diff --git a/src/test/run-make/coverage/lib/doctest_crate.rs b/src/test/run-make/coverage/lib/doctest_crate.rs new file mode 100644 index 000000000..c3210146d --- /dev/null +++ b/src/test/run-make/coverage/lib/doctest_crate.rs @@ -0,0 +1,9 @@ +/// A function run only from within doctests +pub fn fn_run_in_doctests(conditional: usize) { + match conditional { + 1 => assert_eq!(1, 1), // this is run, + 2 => assert_eq!(1, 1), // this, + 3 => assert_eq!(1, 1), // and this too + _ => assert_eq!(1, 2), // however this is not + } +} diff --git a/src/test/run-make/coverage/lib/inline_always_with_dead_code.rs b/src/test/run-make/coverage/lib/inline_always_with_dead_code.rs new file mode 100644 index 000000000..2b21dee6c --- /dev/null +++ b/src/test/run-make/coverage/lib/inline_always_with_dead_code.rs @@ -0,0 +1,22 @@ +// compile-flags: -Cinstrument-coverage -Ccodegen-units=4 -Copt-level=0 + +#![allow(dead_code)] + +mod foo { + #[inline(always)] + pub fn called() { } + + fn uncalled() { } +} + +pub mod bar { + pub fn call_me() { + super::foo::called(); + } +} + +pub mod baz { + pub fn call_me() { + super::foo::called(); + } +} diff --git a/src/test/run-make/coverage/lib/unused_mod_helper.rs b/src/test/run-make/coverage/lib/unused_mod_helper.rs new file mode 100644 index 000000000..ae1cc1531 --- /dev/null +++ b/src/test/run-make/coverage/lib/unused_mod_helper.rs @@ -0,0 +1,3 @@ +pub fn never_called_function() { + println!("I am never called"); +} diff --git a/src/test/run-make/coverage/lib/used_crate.rs b/src/test/run-make/coverage/lib/used_crate.rs new file mode 100644 index 000000000..8b8b1f7f3 --- /dev/null +++ b/src/test/run-make/coverage/lib/used_crate.rs @@ -0,0 +1,100 @@ +#![allow(unused_assignments, unused_variables)] +// compile-flags: -C opt-level=3 # validates coverage now works with optimizations +use std::fmt::Debug; + +pub fn used_function() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let mut countdown = 0; + if is_true { + countdown = 10; + } + use_this_lib_crate(); +} + +pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + println!("used_only_from_bin_crate_generic_function with {:?}", arg); +} +// Expect for above function: `Unexecuted instantiation` (see below) +pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); +} + +pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); +} + +pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); +} + +pub fn unused_generic_function<T: Debug>(arg: T) { + println!("unused_generic_function with {:?}", arg); +} + +pub fn unused_function() { + let is_true = std::env::args().len() == 1; + let mut countdown = 2; + if !is_true { + countdown = 20; + } +} + +fn unused_private_function() { + let is_true = std::env::args().len() == 1; + let mut countdown = 2; + if !is_true { + countdown = 20; + } +} + +fn use_this_lib_crate() { + used_from_bin_crate_and_lib_crate_generic_function("used from library used_crate.rs"); + used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + "used from library used_crate.rs", + ); + let some_vec = vec![5, 6, 7, 8]; + used_only_from_this_lib_crate_generic_function(some_vec); + used_only_from_this_lib_crate_generic_function("used ONLY from library used_crate.rs"); +} + +// FIXME(#79651): "Unexecuted instantiation" errors appear in coverage results, +// for example: +// +// | Unexecuted instantiation: used_crate::used_only_from_bin_crate_generic_function::<_> +// +// These notices appear when `llvm-cov` shows instantiations. This may be a +// default option, but it can be suppressed with: +// +// ```shell +// $ `llvm-cov show --show-instantiations=0 ...` +// ``` +// +// The notice is triggered because the function is unused by the library itself, +// and when the library is compiled, a synthetic function is generated, so +// unused function coverage can be reported. Coverage can be skipped for unused +// generic functions with: +// +// ```shell +// $ `rustc -Zunstable-options -C instrument-coverage=except-unused-generics ...` +// ``` +// +// Even though this function is used by `uses_crate.rs` (and +// counted), with substitutions for `T`, those instantiations are only generated +// when the generic function is actually used (from the binary, not from this +// library crate). So the test result shows coverage for all instantiated +// versions and their generic type substitutions, plus the `Unexecuted +// instantiation` message for the non-substituted version. This is valid, but +// unfortunately a little confusing. +// +// The library crate has its own coverage map, and the only way to show unused +// coverage of a generic function is to include the generic function in the +// coverage map, marked as an "unused function". If the library were used by +// another binary that never used this generic function, then it would be valid +// to show the unused generic, with unknown substitution (`_`). +// +// The alternative is to exclude all generics from being included in the "unused +// functions" list, which would then omit coverage results for +// `unused_generic_function<T>()`, below. diff --git a/src/test/run-make/coverage/lib/used_inline_crate.rs b/src/test/run-make/coverage/lib/used_inline_crate.rs new file mode 100644 index 000000000..4a052756d --- /dev/null +++ b/src/test/run-make/coverage/lib/used_inline_crate.rs @@ -0,0 +1,90 @@ +#![allow(unused_assignments, unused_variables)] + +// compile-flags: -C opt-level=3 # validates coverage now works with optimizations + +use std::fmt::Debug; + +pub fn used_function() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let mut countdown = 0; + if is_true { + countdown = 10; + } + use_this_lib_crate(); +} + +#[inline(always)] +pub fn used_inline_function() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let mut countdown = 0; + if is_true { + countdown = 10; + } + use_this_lib_crate(); +} + + + + + + + +#[inline(always)] +pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + println!("used_only_from_bin_crate_generic_function with {:?}", arg); +} +// Expect for above function: `Unexecuted instantiation` (see notes in `used_crate.rs`) + +#[inline(always)] +pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn unused_generic_function<T: Debug>(arg: T) { + println!("unused_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn unused_function() { + let is_true = std::env::args().len() == 1; + let mut countdown = 2; + if !is_true { + countdown = 20; + } +} + +#[inline(always)] +fn unused_private_function() { + let is_true = std::env::args().len() == 1; + let mut countdown = 2; + if !is_true { + countdown = 20; + } +} + +fn use_this_lib_crate() { + used_from_bin_crate_and_lib_crate_generic_function("used from library used_crate.rs"); + used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + "used from library used_crate.rs", + ); + let some_vec = vec![5, 6, 7, 8]; + used_only_from_this_lib_crate_generic_function(some_vec); + used_only_from_this_lib_crate_generic_function("used ONLY from library used_crate.rs"); +} diff --git a/src/test/run-make/coverage/loop_break_value.rs b/src/test/run-make/coverage/loop_break_value.rs new file mode 100644 index 000000000..dbc4fad7a --- /dev/null +++ b/src/test/run-make/coverage/loop_break_value.rs @@ -0,0 +1,13 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + let result + = + loop + { + break + 10 + ; + } + ; +} diff --git a/src/test/run-make/coverage/loops_branches.rs b/src/test/run-make/coverage/loops_branches.rs new file mode 100644 index 000000000..7116ce47f --- /dev/null +++ b/src/test/run-make/coverage/loops_branches.rs @@ -0,0 +1,61 @@ +#![allow(unused_assignments, unused_variables, while_true)] + +// This test confirms that (1) unexecuted infinite loops are handled correctly by the +// InstrumentCoverage MIR pass; and (2) Counter Expressions that subtract from zero can be dropped. + +struct DebugTest; + +impl std::fmt::Debug for DebugTest { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if true { + if false { + while true { + } + } + write!(f, "cool")?; + } else { + } + + for i in 0..10 { + if true { + if false { + while true {} + } + write!(f, "cool")?; + } else { + } + } + Ok(()) + } +} + +struct DisplayTest; + +impl std::fmt::Display for DisplayTest { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if false { + } else { + if false { + while true {} + } + write!(f, "cool")?; + } + for i in 0..10 { + if false { + } else { + if false { + while true {} + } + write!(f, "cool")?; + } + } + Ok(()) + } +} + +fn main() { + let debug_test = DebugTest; + println!("{:?}", debug_test); + let display_test = DisplayTest; + println!("{}", display_test); +} diff --git a/src/test/run-make/coverage/match_or_pattern.rs b/src/test/run-make/coverage/match_or_pattern.rs new file mode 100644 index 000000000..4c6a8a9b7 --- /dev/null +++ b/src/test/run-make/coverage/match_or_pattern.rs @@ -0,0 +1,45 @@ +#![feature(or_patterns)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut a: u8 = 0; + let mut b: u8 = 0; + if is_true { + a = 2; + b = 0; + } + match (a, b) { + // Or patterns generate MIR `SwitchInt` with multiple targets to the same `BasicBlock`. + // This test confirms a fix for Issue #79569. + (0 | 1, 2 | 3) => {} + _ => {} + } + if is_true { + a = 0; + b = 0; + } + match (a, b) { + (0 | 1, 2 | 3) => {} + _ => {} + } + if is_true { + a = 2; + b = 2; + } + match (a, b) { + (0 | 1, 2 | 3) => {} + _ => {} + } + if is_true { + a = 0; + b = 2; + } + match (a, b) { + (0 | 1, 2 | 3) => {} + _ => {} + } +} diff --git a/src/test/run-make/coverage/nested_loops.rs b/src/test/run-make/coverage/nested_loops.rs new file mode 100644 index 000000000..4c7c78427 --- /dev/null +++ b/src/test/run-make/coverage/nested_loops.rs @@ -0,0 +1,25 @@ +fn main() { + let is_true = std::env::args().len() == 1; + let mut countdown = 10; + + 'outer: while countdown > 0 { + let mut a = 100; + let mut b = 100; + for _ in 0..50 { + if a < 30 { + break; + } + a -= 5; + b -= 5; + if b < 90 { + a -= 10; + if is_true { + break 'outer; + } else { + a -= 2; + } + } + } + countdown -= 1; + } +} diff --git a/src/test/run-make/coverage/no_cov_crate.rs b/src/test/run-make/coverage/no_cov_crate.rs new file mode 100644 index 000000000..0bfbdda2c --- /dev/null +++ b/src/test/run-make/coverage/no_cov_crate.rs @@ -0,0 +1,86 @@ +// Enables `no_coverage` on the entire crate +#![feature(no_coverage)] + +#[no_coverage] +fn do_not_add_coverage_1() { + println!("called but not covered"); +} + +fn do_not_add_coverage_2() { + #![no_coverage] + println!("called but not covered"); +} + +#[no_coverage] +fn do_not_add_coverage_not_called() { + println!("not called and not covered"); +} + +fn add_coverage_1() { + println!("called and covered"); +} + +fn add_coverage_2() { + println!("called and covered"); +} + +fn add_coverage_not_called() { + println!("not called but covered"); +} + +// FIXME: These test-cases illustrate confusing results of nested functions. +// See https://github.com/rust-lang/rust/issues/93319 +mod nested_fns { + #[no_coverage] + pub fn outer_not_covered(is_true: bool) { + fn inner(is_true: bool) { + if is_true { + println!("called and covered"); + } else { + println!("absolutely not covered"); + } + } + println!("called but not covered"); + inner(is_true); + } + + pub fn outer(is_true: bool) { + println!("called and covered"); + inner_not_covered(is_true); + + #[no_coverage] + fn inner_not_covered(is_true: bool) { + if is_true { + println!("called but not covered"); + } else { + println!("absolutely not covered"); + } + } + } + + pub fn outer_both_covered(is_true: bool) { + println!("called and covered"); + inner(is_true); + + fn inner(is_true: bool) { + if is_true { + println!("called and covered"); + } else { + println!("absolutely not covered"); + } + } + } +} + +fn main() { + let is_true = std::env::args().len() == 1; + + do_not_add_coverage_1(); + do_not_add_coverage_2(); + add_coverage_1(); + add_coverage_2(); + + nested_fns::outer_not_covered(is_true); + nested_fns::outer(is_true); + nested_fns::outer_both_covered(is_true); +} diff --git a/src/test/run-make/coverage/overflow.rs b/src/test/run-make/coverage/overflow.rs new file mode 100644 index 000000000..e537b0e95 --- /dev/null +++ b/src/test/run-make/coverage/overflow.rs @@ -0,0 +1,63 @@ +#![allow(unused_assignments)] +// expect-exit-status-101 + +fn might_overflow(to_add: u32) -> u32 { + if to_add > 5 { + println!("this will probably overflow"); + } + let add_to = u32::MAX - 5; + println!("does {} + {} overflow?", add_to, to_add); + let result = to_add + add_to; + println!("continuing after overflow check"); + result +} + +fn main() -> Result<(),u8> { + let mut countdown = 10; + while countdown > 0 { + if countdown == 1 { + let result = might_overflow(10); + println!("Result: {}", result); + } else if countdown < 5 { + let result = might_overflow(1); + println!("Result: {}", result); + } + countdown -= 1; + } + Ok(()) +} + +// Notes: +// 1. Compare this program and its coverage results to those of the very similar test `assert.rs`, +// and similar tests `panic_unwind.rs`, abort.rs` and `try_error_result.rs`. +// 2. This test confirms the coverage generated when a program passes or fails a +// compiler-generated `TerminatorKind::Assert` (based on an overflow check, in this case). +// 3. Similar to how the coverage instrumentation handles `TerminatorKind::Call`, +// compiler-generated assertion failures are assumed to be a symptom of a program bug, not +// expected behavior. To simplify the coverage graphs and keep instrumented programs as +// small and fast as possible, `Assert` terminators are assumed to always succeed, and +// therefore are considered "non-branching" terminators. So, an `Assert` terminator does not +// get its own coverage counter. +// 4. After an unhandled panic or failed Assert, coverage results may not always be intuitive. +// In this test, the final count for the statements after the `if` block in `might_overflow()` +// is 4, even though the lines after `to_add + add_to` were executed only 3 times. Depending +// on the MIR graph and the structure of the code, this count could have been 3 (which might +// have been valid for the overflowed add `+`, but should have been 4 for the lines before +// the overflow. The reason for this potential uncertainty is, a `CounterKind` is incremented +// via StatementKind::Counter at the end of the block, but (as in the case in this test), +// a CounterKind::Expression is always evaluated. In this case, the expression was based on +// a `Counter` incremented as part of the evaluation of the `if` expression, which was +// executed, and counted, 4 times, before reaching the overflow add. + +// If the program did not overflow, the coverage for `might_overflow()` would look like this: +// +// 4| |fn might_overflow(to_add: u32) -> u32 { +// 5| 4| if to_add > 5 { +// 6| 0| println!("this will probably overflow"); +// 7| 4| } +// 8| 4| let add_to = u32::MAX - 5; +// 9| 4| println!("does {} + {} overflow?", add_to, to_add); +// 10| 4| let result = to_add + add_to; +// 11| 4| println!("continuing after overflow check"); +// 12| 4| result +// 13| 4|} diff --git a/src/test/run-make/coverage/panic_unwind.rs b/src/test/run-make/coverage/panic_unwind.rs new file mode 100644 index 000000000..03128c2cc --- /dev/null +++ b/src/test/run-make/coverage/panic_unwind.rs @@ -0,0 +1,31 @@ +#![allow(unused_assignments)] +// expect-exit-status-101 + +fn might_panic(should_panic: bool) { + if should_panic { + println!("panicking..."); + panic!("panics"); + } else { + println!("Don't Panic"); + } +} + +fn main() -> Result<(), u8> { + let mut countdown = 10; + while countdown > 0 { + if countdown == 1 { + might_panic(true); + } else if countdown < 5 { + might_panic(false); + } + countdown -= 1; + } + Ok(()) +} + +// Notes: +// 1. Compare this program and its coverage results to those of the similar tests `abort.rs` and +// `try_error_result.rs`. +// 2. Since the `panic_unwind.rs` test is allowed to unwind, it is also allowed to execute the +// normal program exit cleanup, including writing out the current values of the coverage +// counters. diff --git a/src/test/run-make/coverage/partial_eq.rs b/src/test/run-make/coverage/partial_eq.rs new file mode 100644 index 000000000..4ceaba9b1 --- /dev/null +++ b/src/test/run-make/coverage/partial_eq.rs @@ -0,0 +1,46 @@ +// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the +// structure of this test. + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + major: usize, + minor: usize, + patch: usize, +} + +impl Version { + pub fn new(major: usize, minor: usize, patch: usize) -> Self { + Self { + major, + minor, + patch, + } + } +} + +fn main() { + let version_3_2_1 = Version::new(3, 2, 1); + let version_3_3_0 = Version::new(3, 3, 0); + + println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); +} + +/* + +This test verifies a bug was fixed that otherwise generated this error: + +thread 'rustc' panicked at 'No counters provided the source_hash for function: + Instance { + def: Item(WithOptConstParam { + did: DefId(0:101 ~ autocfg[c44a]::version::{impl#2}::partial_cmp), + const_param_did: None + }), + substs: [] + }' +The `PartialOrd` derived by `Version` happened to generate a MIR that generated coverage +without a code region associated with any `Counter`. Code regions were associated with at least +one expression, which is allowed, but the `function_source_hash` was only passed to the codegen +(coverage mapgen) phase from a `Counter`s code region. A new method was added to pass the +`function_source_hash` without a code region, if necessary. + +*/ diff --git a/src/test/run-make/coverage/simple_loop.rs b/src/test/run-make/coverage/simple_loop.rs new file mode 100644 index 000000000..6f7f23475 --- /dev/null +++ b/src/test/run-make/coverage/simple_loop.rs @@ -0,0 +1,35 @@ +#![allow(unused_assignments)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + + if + is_true + { + countdown + = + 10 + ; + } + + loop + { + if + countdown + == + 0 + { + break + ; + } + countdown + -= + 1 + ; + } +} diff --git a/src/test/run-make/coverage/simple_match.rs b/src/test/run-make/coverage/simple_match.rs new file mode 100644 index 000000000..be99e59a8 --- /dev/null +++ b/src/test/run-make/coverage/simple_match.rs @@ -0,0 +1,43 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 1; + if is_true { + countdown = 0; + } + + for + _ + in + 0..2 + { + let z + ; + match + countdown + { + x + if + x + < + 1 + => + { + z = countdown + ; + let y = countdown + ; + countdown = 10 + ; + } + _ + => + {} + } + } +} diff --git a/src/test/run-make/coverage/tight_inf_loop.rs b/src/test/run-make/coverage/tight_inf_loop.rs new file mode 100644 index 000000000..cef99027a --- /dev/null +++ b/src/test/run-make/coverage/tight_inf_loop.rs @@ -0,0 +1,5 @@ +fn main() { + if false { + loop {} + } +} diff --git a/src/test/run-make/coverage/try_error_result.rs b/src/test/run-make/coverage/try_error_result.rs new file mode 100644 index 000000000..cd0acf723 --- /dev/null +++ b/src/test/run-make/coverage/try_error_result.rs @@ -0,0 +1,118 @@ +#![allow(unused_assignments)] +// expect-exit-status-1 + +fn call(return_error: bool) -> Result<(),()> { + if return_error { + Err(()) + } else { + Ok(()) + } +} + +fn test1() -> Result<(),()> { + let mut + countdown = 10 + ; + for + _ + in + 0..10 + { + countdown + -= 1 + ; + if + countdown < 5 + { + call(/*return_error=*/ true)?; + call(/*return_error=*/ false)?; + } + else + { + call(/*return_error=*/ false)?; + } + } + Ok(()) +} + +struct Thing1; +impl Thing1 { + fn get_thing_2(&self, return_error: bool) -> Result<Thing2,()> { + if return_error { + Err(()) + } else { + Ok(Thing2{}) + } + } +} + +struct Thing2; +impl Thing2 { + fn call(&self, return_error: bool) -> Result<u32,()> { + if return_error { + Err(()) + } else { + Ok(57) + } + } +} + +fn test2() -> Result<(),()> { + let thing1 = Thing1{}; + let mut + countdown = 10 + ; + for + _ + in + 0..10 + { + countdown + -= 1 + ; + if + countdown < 5 + { + thing1.get_thing_2(/*err=*/ false)?.call(/*err=*/ true).expect_err("call should fail"); + thing1 + . + get_thing_2(/*return_error=*/ false) + ? + . + call(/*return_error=*/ true) + . + expect_err( + "call should fail" + ); + let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ true)?; + assert_eq!(val, 57); + let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ false)?; + assert_eq!(val, 57); + } + else + { + let val = thing1.get_thing_2(/*return_error=*/ false)?.call(/*return_error=*/ false)?; + assert_eq!(val, 57); + let val = thing1 + .get_thing_2(/*return_error=*/ false)? + .call(/*return_error=*/ false)?; + assert_eq!(val, 57); + let val = thing1 + .get_thing_2(/*return_error=*/ false) + ? + .call(/*return_error=*/ false) + ? + ; + assert_eq!(val, 57); + } + } + Ok(()) +} + +fn main() -> Result<(),()> { + test1().expect_err("test1 should fail"); + test2() + ? + ; + Ok(()) +} diff --git a/src/test/run-make/coverage/unused.rs b/src/test/run-make/coverage/unused.rs new file mode 100644 index 000000000..fb6113eb0 --- /dev/null +++ b/src/test/run-make/coverage/unused.rs @@ -0,0 +1,39 @@ +fn foo<T>(x: T) { + let mut i = 0; + while i < 10 { + i != 0 || i != 0; + i += 1; + } +} + +fn unused_template_func<T>(x: T) { + let mut i = 0; + while i < 10 { + i != 0 || i != 0; + i += 1; + } +} + +fn unused_func(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn unused_func2(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn unused_func3(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn main() -> Result<(), u8> { + foo::<u32>(0); + foo::<f32>(0.0); + Ok(()) +} diff --git a/src/test/run-make/coverage/unused_mod.rs b/src/test/run-make/coverage/unused_mod.rs new file mode 100644 index 000000000..679b4e531 --- /dev/null +++ b/src/test/run-make/coverage/unused_mod.rs @@ -0,0 +1,6 @@ +#[path = "lib/unused_mod_helper.rs"] +mod unused_module; + +fn main() { + println!("hello world!"); +} diff --git a/src/test/run-make/coverage/uses_crate.rs b/src/test/run-make/coverage/uses_crate.rs new file mode 100644 index 000000000..20cb05fe5 --- /dev/null +++ b/src/test/run-make/coverage/uses_crate.rs @@ -0,0 +1,12 @@ +#![allow(unused_assignments, unused_variables)] +// compile-flags: -C opt-level=3 # validates coverage now works with optimizations +extern crate used_crate; + +fn main() { + used_crate::used_function(); + let some_vec = vec![1, 2, 3, 4]; + used_crate::used_only_from_bin_crate_generic_function(&some_vec); + used_crate::used_only_from_bin_crate_generic_function("used from bin uses_crate.rs"); + used_crate::used_from_bin_crate_and_lib_crate_generic_function(some_vec); + used_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function("interesting?"); +} diff --git a/src/test/run-make/coverage/uses_inline_crate.rs b/src/test/run-make/coverage/uses_inline_crate.rs new file mode 100644 index 000000000..a7fe8532b --- /dev/null +++ b/src/test/run-make/coverage/uses_inline_crate.rs @@ -0,0 +1,17 @@ +#![allow(unused_assignments, unused_variables)] + +// compile-flags: -C opt-level=3 # validates coverage now works with optimizations + +extern crate used_inline_crate; + +fn main() { + used_inline_crate::used_function(); + used_inline_crate::used_inline_function(); + let some_vec = vec![1, 2, 3, 4]; + used_inline_crate::used_only_from_bin_crate_generic_function(&some_vec); + used_inline_crate::used_only_from_bin_crate_generic_function("used from bin uses_crate.rs"); + used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function(some_vec); + used_inline_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + "interesting?", + ); +} diff --git a/src/test/run-make/coverage/while.rs b/src/test/run-make/coverage/while.rs new file mode 100644 index 000000000..781b90b35 --- /dev/null +++ b/src/test/run-make/coverage/while.rs @@ -0,0 +1,5 @@ +fn main() { + let num = 9; + while num >= 10 { + } +} diff --git a/src/test/run-make/coverage/while_early_ret.rs b/src/test/run-make/coverage/while_early_ret.rs new file mode 100644 index 000000000..1fcea9c85 --- /dev/null +++ b/src/test/run-make/coverage/while_early_ret.rs @@ -0,0 +1,42 @@ +#![allow(unused_assignments)] +// expect-exit-status-1 + +fn main() -> Result<(),u8> { + let mut countdown = 10; + while + countdown + > + 0 + { + if + countdown + < + 5 + { + return + if + countdown + > + 8 + { + Ok(()) + } + else + { + Err(1) + } + ; + } + countdown + -= + 1 + ; + } + Ok(()) +} + +// ISSUE(77553): Originally, this test had `Err(1)` on line 22 (instead of `Ok(())`) and +// `std::process::exit(2)` on line 26 (instead of `Err(1)`); and this worked as expected on Linux +// and MacOS. But on Windows (MSVC, at least), the call to `std::process::exit()` exits the program +// without saving the InstrProf coverage counters. The use of `std::process:exit()` is not critical +// to the coverage test for early returns, but this is a limitation that should be fixed. diff --git a/src/test/run-make/coverage/yield.rs b/src/test/run-make/coverage/yield.rs new file mode 100644 index 000000000..ff7616656 --- /dev/null +++ b/src/test/run-make/coverage/yield.rs @@ -0,0 +1,37 @@ +#![feature(generators, generator_trait)] +#![allow(unused_assignments)] + +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; + +fn main() { + let mut generator = || { + yield 1; + return "foo" + }; + + match Pin::new(&mut generator).resume(()) { + GeneratorState::Yielded(1) => {} + _ => panic!("unexpected value from resume"), + } + match Pin::new(&mut generator).resume(()) { + GeneratorState::Complete("foo") => {} + _ => panic!("unexpected value from resume"), + } + + let mut generator = || { + yield 1; + yield 2; + yield 3; + return "foo" + }; + + match Pin::new(&mut generator).resume(()) { + GeneratorState::Yielded(1) => {} + _ => panic!("unexpected value from resume"), + } + match Pin::new(&mut generator).resume(()) { + GeneratorState::Yielded(2) => {} + _ => panic!("unexpected value from resume"), + } +} diff --git a/src/test/run-make/dep-graph/Makefile b/src/test/run-make/dep-graph/Makefile new file mode 100644 index 000000000..88916022c --- /dev/null +++ b/src/test/run-make/dep-graph/Makefile @@ -0,0 +1,12 @@ +-include ../../run-make-fulldeps/tools.mk + +# ignore-cross-compile + +# Just verify that we successfully run and produce dep graphs when requested. + +all: + RUST_DEP_GRAPH=$(TMPDIR)/dep-graph $(RUSTC) \ + -Cincremental=$(TMPDIR)/incr \ + -Zquery-dep-graph -Zdump-dep-graph foo.rs + test -f $(TMPDIR)/dep-graph.txt + test -f $(TMPDIR)/dep-graph.dot diff --git a/src/test/run-make/dep-graph/foo.rs b/src/test/run-make/dep-graph/foo.rs new file mode 100644 index 000000000..f328e4d9d --- /dev/null +++ b/src/test/run-make/dep-graph/foo.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/test/run-make/emit-named-files/Makefile b/src/test/run-make/emit-named-files/Makefile new file mode 100644 index 000000000..03eb83b97 --- /dev/null +++ b/src/test/run-make/emit-named-files/Makefile @@ -0,0 +1,33 @@ +-include ../../run-make-fulldeps/tools.mk + +OUT=$(TMPDIR)/emit + +all: asm llvm-bc llvm-ir obj metadata link dep-info mir + +asm: $(OUT) + $(RUSTC) --emit asm=$(OUT)/libfoo.s foo.rs + test -f $(OUT)/libfoo.s +llvm-bc: $(OUT) + $(RUSTC) --emit llvm-bc=$(OUT)/libfoo.bc foo.rs + test -f $(OUT)/libfoo.bc +llvm-ir: $(OUT) + $(RUSTC) --emit llvm-ir=$(OUT)/libfoo.ll foo.rs + test -f $(OUT)/libfoo.ll +obj: $(OUT) + $(RUSTC) --emit obj=$(OUT)/libfoo.o foo.rs + test -f $(OUT)/libfoo.o +metadata: $(OUT) + $(RUSTC) --emit metadata=$(OUT)/libfoo.rmeta foo.rs + test -f $(OUT)/libfoo.rmeta +link: $(OUT) + $(RUSTC) --emit link=$(OUT)/libfoo.rlib foo.rs + test -f $(OUT)/libfoo.rlib +dep-info: $(OUT) + $(RUSTC) --emit dep-info=$(OUT)/libfoo.d foo.rs + test -f $(OUT)/libfoo.d +mir: $(OUT) + $(RUSTC) --emit mir=$(OUT)/libfoo.mir foo.rs + test -f $(OUT)/libfoo.mir + +$(OUT): + mkdir -p $(OUT) diff --git a/src/test/run-make/emit-named-files/foo.rs b/src/test/run-make/emit-named-files/foo.rs new file mode 100644 index 000000000..c1bfaa6ca --- /dev/null +++ b/src/test/run-make/emit-named-files/foo.rs @@ -0,0 +1 @@ +#![crate_type = "rlib"] diff --git a/src/test/run-make/emit-path-unhashed/Makefile b/src/test/run-make/emit-path-unhashed/Makefile new file mode 100644 index 000000000..b6b2d8af6 --- /dev/null +++ b/src/test/run-make/emit-path-unhashed/Makefile @@ -0,0 +1,37 @@ +-include ../../run-make-fulldeps/tools.mk + +OUT=$(TMPDIR)/emit + +# --emit KIND=PATH should not affect crate hash vs --emit KIND +all: $(OUT)/a/libfoo.rlib $(OUT)/b/libfoo.rlib $(OUT)/c/libfoo.rlib \ + $(TMPDIR)/libfoo.rlib + $(RUSTC) -Zls $(TMPDIR)/libfoo.rlib > $(TMPDIR)/base.txt + $(RUSTC) -Zls $(OUT)/a/libfoo.rlib > $(TMPDIR)/a.txt + $(RUSTC) -Zls $(OUT)/b/libfoo.rlib > $(TMPDIR)/b.txt + $(RUSTC) -Zls $(OUT)/c/libfoo.rlib > $(TMPDIR)/c.txt + + diff $(TMPDIR)/base.txt $(TMPDIR)/a.txt + diff $(TMPDIR)/base.txt $(TMPDIR)/b.txt + + # Different KIND parameters do affect hash. + # diff exits 1 on difference, 2 on trouble + diff $(TMPDIR)/base.txt $(TMPDIR)/c.txt ; test "$$?" -eq 1 + +# Default output name +$(TMPDIR)/libfoo.rlib: foo.rs + $(RUSTC) --emit link foo.rs + +# Output named with -o +$(OUT)/a/libfoo.rlib: foo.rs + mkdir -p $(OUT)/a + $(RUSTC) --emit link -o $@ foo.rs + +# Output named with KIND=PATH +$(OUT)/b/libfoo.rlib: foo.rs + mkdir -p $(OUT)/b + $(RUSTC) --emit link=$@ foo.rs + +# Output multiple kinds +$(OUT)/c/libfoo.rlib: foo.rs + mkdir -p $(OUT)/c + $(RUSTC) --emit link=$@,metadata foo.rs diff --git a/src/test/run-make/emit-path-unhashed/foo.rs b/src/test/run-make/emit-path-unhashed/foo.rs new file mode 100644 index 000000000..c1bfaa6ca --- /dev/null +++ b/src/test/run-make/emit-path-unhashed/foo.rs @@ -0,0 +1 @@ +#![crate_type = "rlib"] diff --git a/src/test/run-make/emit-shared-files/Makefile b/src/test/run-make/emit-shared-files/Makefile new file mode 100644 index 000000000..9f46883be --- /dev/null +++ b/src/test/run-make/emit-shared-files/Makefile @@ -0,0 +1,46 @@ +-include ../../run-make-fulldeps/tools.mk + +INVOCATION_ONLY = $(TMPDIR)/invocation-only +TOOLCHAIN_ONLY = $(TMPDIR)/toolchain-only +ALL_SHARED = $(TMPDIR)/all-shared + +all: invocation-only toolchain-only all-shared + +invocation-only: + $(RUSTDOC) -Z unstable-options --emit=invocation-specific --output $(INVOCATION_ONLY) --resource-suffix=-xxx --theme y.css --extend-css z.css x.rs + [ -e $(INVOCATION_ONLY)/search-index-xxx.js ] + [ -e $(INVOCATION_ONLY)/settings.html ] + [ -e $(INVOCATION_ONLY)/x/all.html ] + [ -e $(INVOCATION_ONLY)/x/index.html ] + [ -e $(INVOCATION_ONLY)/theme-xxx.css ] # generated from z.css + ! [ -e $(INVOCATION_ONLY)/storage-xxx.js ] + ! [ -e $(INVOCATION_ONLY)/SourceSerif4-It.ttf.woff2 ] + + # FIXME: this probably shouldn't have a suffix + [ -e $(INVOCATION_ONLY)/y-xxx.css ] + # FIXME: this is technically incorrect (see `write_shared`) + ! [ -e $(INVOCATION_ONLY)/main-xxx.js ] + +toolchain-only: + $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources --output $(TOOLCHAIN_ONLY) --resource-suffix=-xxx --extend-css z.css x.rs + [ -e $(TOOLCHAIN_ONLY)/storage-xxx.js ] + ! [ -e $(TOOLCHAIN_ONLY)/SourceSerif4-It.ttf.woff2 ] + ! [ -e $(TOOLCHAIN_ONLY)/search-index-xxx.js ] + ! [ -e $(TOOLCHAIN_ONLY)/x/index.html ] + ! [ -e $(TOOLCHAIN_ONLY)/theme.css ] + + [ -e $(TOOLCHAIN_ONLY)/main-xxx.js ] + ! [ -e $(TOOLCHAIN_ONLY)/y-xxx.css ] + +all-shared: + $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources,unversioned-shared-resources --output $(ALL_SHARED) --resource-suffix=-xxx --extend-css z.css x.rs + [ -e $(ALL_SHARED)/storage-xxx.js ] + [ -e $(ALL_SHARED)/SourceSerif4-It.ttf.woff2 ] + ! [ -e $(ALL_SHARED)/search-index-xxx.js ] + ! [ -e $(ALL_SHARED)/settings.html ] + ! [ -e $(ALL_SHARED)/x ] + ! [ -e $(ALL_SHARED)/src ] + ! [ -e $(ALL_SHARED)/theme.css ] + + [ -e $(ALL_SHARED)/main-xxx.js ] + ! [ -e $(ALL_SHARED)/y-xxx.css ] diff --git a/src/test/run-make/emit-shared-files/x.rs b/src/test/run-make/emit-shared-files/x.rs new file mode 100644 index 000000000..5df757613 --- /dev/null +++ b/src/test/run-make/emit-shared-files/x.rs @@ -0,0 +1 @@ +// nothing to see here diff --git a/src/test/run-make/emit-shared-files/y.css b/src/test/run-make/emit-shared-files/y.css new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/test/run-make/emit-shared-files/y.css diff --git a/src/test/run-make/emit-shared-files/z.css b/src/test/run-make/emit-shared-files/z.css new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/test/run-make/emit-shared-files/z.css diff --git a/src/test/run-make/env-dep-info/Makefile b/src/test/run-make/env-dep-info/Makefile new file mode 100644 index 000000000..25d9a31c2 --- /dev/null +++ b/src/test/run-make/env-dep-info/Makefile @@ -0,0 +1,19 @@ +-include ../../run-make-fulldeps/tools.mk + +# FIXME(eddyb) provide `HOST_RUSTC` and `TARGET_RUSTC` +# instead of hardcoding them everywhere they're needed. +ifeq ($(IS_MUSL_HOST),1) +ADDITIONAL_ARGS := $(RUSTFLAGS) +endif + +all: + EXISTING_ENV=1 EXISTING_OPT_ENV=1 $(RUSTC) --emit dep-info main.rs + $(CGREP) "# env-dep:EXISTING_ENV=1" < $(TMPDIR)/main.d + $(CGREP) "# env-dep:EXISTING_OPT_ENV=1" < $(TMPDIR)/main.d + $(CGREP) "# env-dep:NONEXISTENT_OPT_ENV" < $(TMPDIR)/main.d + $(CGREP) "# env-dep:ESCAPE\nESCAPE\\" < $(TMPDIR)/main.d + # Proc macro + $(BARE_RUSTC) $(ADDITIONAL_ARGS) --out-dir $(TMPDIR) macro_def.rs + EXISTING_PROC_MACRO_ENV=1 $(RUSTC) --emit dep-info macro_use.rs + $(CGREP) "# env-dep:EXISTING_PROC_MACRO_ENV=1" < $(TMPDIR)/macro_use.d + $(CGREP) "# env-dep:NONEXISTENT_PROC_MACEO_ENV" < $(TMPDIR)/macro_use.d diff --git a/src/test/run-make/env-dep-info/macro_def.rs b/src/test/run-make/env-dep-info/macro_def.rs new file mode 100644 index 000000000..e328eae48 --- /dev/null +++ b/src/test/run-make/env-dep-info/macro_def.rs @@ -0,0 +1,12 @@ +#![feature(proc_macro_tracked_env)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro] +pub fn access_env_vars(_: TokenStream) -> TokenStream { + let _ = tracked_env::var("EXISTING_PROC_MACRO_ENV"); + let _ = tracked_env::var("NONEXISTENT_PROC_MACEO_ENV"); + TokenStream::new() +} diff --git a/src/test/run-make/env-dep-info/macro_use.rs b/src/test/run-make/env-dep-info/macro_use.rs new file mode 100644 index 000000000..2f5267471 --- /dev/null +++ b/src/test/run-make/env-dep-info/macro_use.rs @@ -0,0 +1,6 @@ +#[macro_use] +extern crate macro_def; + +access_env_vars!(); + +fn main() {} diff --git a/src/test/run-make/env-dep-info/main.rs b/src/test/run-make/env-dep-info/main.rs new file mode 100644 index 000000000..a25246bac --- /dev/null +++ b/src/test/run-make/env-dep-info/main.rs @@ -0,0 +1,6 @@ +fn main() { + env!("EXISTING_ENV"); + option_env!("EXISTING_OPT_ENV"); + option_env!("NONEXISTENT_OPT_ENV"); + option_env!("ESCAPE\nESCAPE\\"); +} diff --git a/src/test/run-make/export-executable-symbols/Makefile b/src/test/run-make/export-executable-symbols/Makefile new file mode 100644 index 000000000..5006f9cb8 --- /dev/null +++ b/src/test/run-make/export-executable-symbols/Makefile @@ -0,0 +1,11 @@ +-include ../../run-make-fulldeps/tools.mk + +# ignore-wasm32 +# ignore-wasm64 +# ignore-none no-std is not supported +# only-linux + +all: + $(RUSTC) -Zexport-executable-symbols main.rs --target $(TARGET) --crate-type=bin + nm $(TMPDIR)/main | $(CGREP) exported_symbol + diff --git a/src/test/run-make/export-executable-symbols/main.rs b/src/test/run-make/export-executable-symbols/main.rs new file mode 100644 index 000000000..c498381a3 --- /dev/null +++ b/src/test/run-make/export-executable-symbols/main.rs @@ -0,0 +1,8 @@ +// edition:2018 + +fn main() {} + +#[no_mangle] +pub fn exported_symbol() -> i8 { + 42 +} diff --git a/src/test/run-make/fmt-write-bloat/Makefile b/src/test/run-make/fmt-write-bloat/Makefile new file mode 100644 index 000000000..26e08086a --- /dev/null +++ b/src/test/run-make/fmt-write-bloat/Makefile @@ -0,0 +1,25 @@ +-include ../../run-make-fulldeps/tools.mk + +# ignore-windows + +ifeq ($(shell $(RUSTC) -vV | grep 'host: $(TARGET)'),) + +# Don't run this test when cross compiling. +all: + +else + +NM = nm + +PANIC_SYMS = panic_bounds_check pad_integral Display Debug + +# Allow for debug_assert!() in debug builds of std. +ifdef NO_DEBUG_ASSERTIONS +PANIC_SYMS += panicking panic_fmt +endif + +all: main.rs + $(RUSTC) $< -O + $(NM) $(call RUN_BINFILE,main) | $(CGREP) -v $(PANIC_SYMS) + +endif diff --git a/src/test/run-make/fmt-write-bloat/main.rs b/src/test/run-make/fmt-write-bloat/main.rs new file mode 100644 index 000000000..e86c48014 --- /dev/null +++ b/src/test/run-make/fmt-write-bloat/main.rs @@ -0,0 +1,32 @@ +#![feature(lang_items)] +#![feature(start)] +#![no_std] + +use core::fmt; +use core::fmt::Write; + +#[link(name = "c")] +extern "C" {} + +struct Dummy; + +impl fmt::Write for Dummy { + #[inline(never)] + fn write_str(&mut self, _: &str) -> fmt::Result { + Ok(()) + } +} + +#[start] +fn main(_: isize, _: *const *const u8) -> isize { + let _ = writeln!(Dummy, "Hello World"); + 0 +} + +#[lang = "eh_personality"] +fn eh_personality() {} + +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/src/test/run-make/git_clone_sha1.sh b/src/test/run-make/git_clone_sha1.sh new file mode 100644 index 000000000..626e4e427 --- /dev/null +++ b/src/test/run-make/git_clone_sha1.sh @@ -0,0 +1,23 @@ +#!/bin/bash -x + +# Usage: $0 project_name url sha1 +# Get the crate with the specified sha1. +# +# all arguments are required. +# +# See below link for git usage: +# https://stackoverflow.com/questions/3489173#14091182 + +# Mandatory arguments: +PROJECT_NAME=$1 +URL=$2 +SHA1=$3 + +function err_exit() { + echo "ERROR:" $* + exit 1 +} + +git clone $URL $PROJECT_NAME || err_exit +cd $PROJECT_NAME || err_exit +git reset --hard $SHA1 || err_exit diff --git a/src/test/run-make/incr-foreign-head-span/Makefile b/src/test/run-make/incr-foreign-head-span/Makefile new file mode 100644 index 000000000..712965eaa --- /dev/null +++ b/src/test/run-make/incr-foreign-head-span/Makefile @@ -0,0 +1,21 @@ +include ../../run-make-fulldeps/tools.mk + +# ignore-none no-std is not supported +# ignore-nvptx64-nvidia-cuda FIXME: can't find crate for 'std' + +# Ensure that modifying a crate on disk (without recompiling it) +# does not cause ICEs in downstream crates. +# Previously, we would call `SourceMap.guess_head_span` on a span +# from an external crate, which would cause us to read an upstream +# source file from disk during compilation of a downstream crate +# See #86480 for more details + +INCR=$(TMPDIR)/incr + +all: + cp first_crate.rs second_crate.rs $(TMPDIR) + $(RUSTC) $(TMPDIR)/first_crate.rs -C incremental=$(INCR) --target $(TARGET) --crate-type lib + $(RUSTC) $(TMPDIR)/second_crate.rs -C incremental=$(INCR) --target $(TARGET) --extern first-crate=$(TMPDIR) --crate-type lib + rm $(TMPDIR)/first_crate.rs + $(RUSTC) $(TMPDIR)/second_crate.rs -C incremental=$(INCR) --target $(TARGET) --cfg second_run --crate-type lib + diff --git a/src/test/run-make/incr-foreign-head-span/first_crate.rs b/src/test/run-make/incr-foreign-head-span/first_crate.rs new file mode 100644 index 000000000..69dd103bf --- /dev/null +++ b/src/test/run-make/incr-foreign-head-span/first_crate.rs @@ -0,0 +1 @@ +pub trait OtherTrait {} diff --git a/src/test/run-make/incr-foreign-head-span/second_crate.rs b/src/test/run-make/incr-foreign-head-span/second_crate.rs new file mode 100644 index 000000000..102f6b26c --- /dev/null +++ b/src/test/run-make/incr-foreign-head-span/second_crate.rs @@ -0,0 +1,8 @@ +extern crate first_crate; +use first_crate::OtherTrait; + +#[cfg(not(second_run))] +trait Foo: OtherTrait {} + +#[cfg(second_run)] +trait Bar: OtherTrait {} diff --git a/src/test/run-make/incr-prev-body-beyond-eof/Makefile b/src/test/run-make/incr-prev-body-beyond-eof/Makefile new file mode 100644 index 000000000..24eea3aca --- /dev/null +++ b/src/test/run-make/incr-prev-body-beyond-eof/Makefile @@ -0,0 +1,19 @@ +# ignore-none no-std is not supported +# ignore-nvptx64-nvidia-cuda FIXME: can't find crate for `std` + +include ../../run-make-fulldeps/tools.mk + +# Tests that we don't ICE during incremental compilation after modifying a +# function span such that its previous end line exceeds the number of lines +# in the new file, but its start line/column and length remain the same. + +SRC=$(TMPDIR)/src +INCR=$(TMPDIR)/incr + +all: + mkdir $(SRC) + mkdir $(INCR) + cp a.rs $(SRC)/main.rs + $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs --target $(TARGET) + cp b.rs $(SRC)/main.rs + $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs --target $(TARGET) diff --git a/src/test/run-make/incr-prev-body-beyond-eof/a.rs b/src/test/run-make/incr-prev-body-beyond-eof/a.rs new file mode 100644 index 000000000..ca70fb563 --- /dev/null +++ b/src/test/run-make/incr-prev-body-beyond-eof/a.rs @@ -0,0 +1,16 @@ +fn main() { + // foo must be used. + foo(); +} + +// For this test to operate correctly, foo's body must start on exactly the same +// line and column and have the exact same length in bytes in a.rs and b.rs. In +// a.rs, the body must end on a line number which does not exist in b.rs. +// Basically, avoid modifying this file, including adding or removing whitespace! +fn foo() { + assert_eq!(1, 1); + + + + +} diff --git a/src/test/run-make/incr-prev-body-beyond-eof/b.rs b/src/test/run-make/incr-prev-body-beyond-eof/b.rs new file mode 100644 index 000000000..a272e44a6 --- /dev/null +++ b/src/test/run-make/incr-prev-body-beyond-eof/b.rs @@ -0,0 +1,12 @@ +fn main() { + // foo must be used. + foo(); +} + +// For this test to operate correctly, foo's body must start on exactly the same +// line and column and have the exact same length in bytes in a.rs and b.rs. In +// a.rs, the body must end on a line number which does not exist in b.rs. +// Basically, avoid modifying this file, including adding or removing whitespace! +fn foo() { + assert_eq!(1, 1);//// +} diff --git a/src/test/run-make/incremental-session-fail/Makefile b/src/test/run-make/incremental-session-fail/Makefile new file mode 100644 index 000000000..0461bb926 --- /dev/null +++ b/src/test/run-make/incremental-session-fail/Makefile @@ -0,0 +1,14 @@ +include ../../run-make-fulldeps/tools.mk + +SESSION_DIR := $(TMPDIR)/session +OUTPUT_FILE := $(TMPDIR)/build-output + +all: + echo $(TMPDIR) + # Make it so that rustc will fail to create a session directory. + touch $(SESSION_DIR) + # Check exit code is 1 for an error, and not 101 for ICE. + $(RUSTC) foo.rs --crate-type=rlib -C incremental=$(SESSION_DIR) > $(OUTPUT_FILE) 2>&1; [ $$? -eq 1 ] + $(CGREP) "Could not create incremental compilation crate directory" < $(OUTPUT_FILE) + # -v tests are fragile, hopefully this text won't change + $(CGREP) -v "internal compiler error" < $(OUTPUT_FILE) diff --git a/src/test/run-make/incremental-session-fail/foo.rs b/src/test/run-make/incremental-session-fail/foo.rs new file mode 100644 index 000000000..d11c69f81 --- /dev/null +++ b/src/test/run-make/incremental-session-fail/foo.rs @@ -0,0 +1 @@ +// intentionally empty diff --git a/src/test/run-make/invalid-so/Makefile b/src/test/run-make/invalid-so/Makefile new file mode 100644 index 000000000..5b82ecd20 --- /dev/null +++ b/src/test/run-make/invalid-so/Makefile @@ -0,0 +1,7 @@ +include ../../run-make-fulldeps/tools.mk + +DYLIB_NAME := $(shell echo | $(RUSTC) --crate-name foo --crate-type dylib --print file-names -) + +all: + echo >> $(TMPDIR)/$(DYLIB_NAME) + $(RUSTC) --crate-type lib --extern foo=$(TMPDIR)/$(DYLIB_NAME) bar.rs 2>&1 | $(CGREP) 'invalid metadata files for crate `foo`' diff --git a/src/test/run-make/invalid-so/bar.rs b/src/test/run-make/invalid-so/bar.rs new file mode 100644 index 000000000..49af74e1b --- /dev/null +++ b/src/test/run-make/invalid-so/bar.rs @@ -0,0 +1 @@ +extern crate foo; diff --git a/src/test/run-make/issue-10971-temps-dir/Makefile b/src/test/run-make/issue-10971-temps-dir/Makefile new file mode 100644 index 000000000..5ce271926 --- /dev/null +++ b/src/test/run-make/issue-10971-temps-dir/Makefile @@ -0,0 +1,10 @@ +-include ../../run-make-fulldeps/tools.mk + +# Regression test for issue #10971 +# Running two invocations in parallel would overwrite each other's temp files. + +all: + touch $(TMPDIR)/lib.rs + + $(RUSTC) --crate-type=lib -Z temps-dir=$(TMPDIR)/temp1 $(TMPDIR)/lib.rs & \ + $(RUSTC) --crate-type=staticlib -Z temps-dir=$(TMPDIR)/temp2 $(TMPDIR)/lib.rs diff --git a/src/test/run-make/issue-36710/Makefile b/src/test/run-make/issue-36710/Makefile new file mode 100644 index 000000000..b5270ad2b --- /dev/null +++ b/src/test/run-make/issue-36710/Makefile @@ -0,0 +1,20 @@ +# ignore-riscv64 $(call RUN,foo) expects to run the target executable natively +# so it won't work with remote-test-server +# ignore-arm Another build using remote-test-server +# ignore-none no-std is not supported +# ignore-wasm32 FIXME: don't attempt to compile C++ to WASM +# ignore-wasm64 FIXME: don't attempt to compile C++ to WASM +# ignore-nvptx64-nvidia-cuda FIXME: can't find crate for `std` +# ignore-musl FIXME: this makefile needs teaching how to use a musl toolchain +# (see dist-i586-gnu-i586-i686-musl Dockerfile) + +include ../../run-make-fulldeps/tools.mk + +all: foo + $(call RUN,foo) + +foo: foo.rs $(call NATIVE_STATICLIB,foo) + $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) --target $(TARGET) + +$(TMPDIR)/libfoo.o: foo.cpp + $(call COMPILE_OBJ_CXX,$@,$<) diff --git a/src/test/run-make/issue-36710/foo.cpp b/src/test/run-make/issue-36710/foo.cpp new file mode 100644 index 000000000..8f878c2c2 --- /dev/null +++ b/src/test/run-make/issue-36710/foo.cpp @@ -0,0 +1,15 @@ +#include <stdint.h> + +struct A { + A() { v = 1234; } + ~A() { v = 1; } + uint32_t v; +}; + +A a; + +extern "C" { + uint32_t get() { + return a.v; + } +} diff --git a/src/test/run-make/issue-36710/foo.rs b/src/test/run-make/issue-36710/foo.rs new file mode 100644 index 000000000..f30a35e27 --- /dev/null +++ b/src/test/run-make/issue-36710/foo.rs @@ -0,0 +1,10 @@ +// Tests that linking to C++ code with global destructors works. + +extern "C" { + fn get() -> u32; +} + +fn main() { + let i = unsafe { get() }; + assert_eq!(i, 1234); +} diff --git a/src/test/run-make/issue-47384/Makefile b/src/test/run-make/issue-47384/Makefile new file mode 100644 index 000000000..f10365f8c --- /dev/null +++ b/src/test/run-make/issue-47384/Makefile @@ -0,0 +1,12 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-linux +# ignore-cross-compile + +all: main.rs + $(RUSTC) --crate-type lib lib.rs + $(RUSTC) --crate-type cdylib -Clink-args="-Tlinker.ld" main.rs + # Ensure `#[used]` and `KEEP`-ed section is there + objdump -s -j".static" $(TMPDIR)/libmain.so + # Ensure `#[no_mangle]` symbol is there + nm $(TMPDIR)/libmain.so | $(CGREP) bar diff --git a/src/test/run-make/issue-47384/lib.rs b/src/test/run-make/issue-47384/lib.rs new file mode 100644 index 000000000..99508bcda --- /dev/null +++ b/src/test/run-make/issue-47384/lib.rs @@ -0,0 +1,12 @@ +mod foo { + #[link_section = ".rodata.STATIC"] + #[used] + static STATIC: [u32; 10] = [1; 10]; +} + +mod bar { + #[no_mangle] + extern "C" fn bar() -> i32 { + 0 + } +} diff --git a/src/test/run-make/issue-47384/linker.ld b/src/test/run-make/issue-47384/linker.ld new file mode 100644 index 000000000..2e70acab3 --- /dev/null +++ b/src/test/run-make/issue-47384/linker.ld @@ -0,0 +1,7 @@ +SECTIONS +{ + .static : ALIGN(4) + { + KEEP(*(.rodata.STATIC)); + } +} diff --git a/src/test/run-make/issue-47384/main.rs b/src/test/run-make/issue-47384/main.rs new file mode 100644 index 000000000..025726325 --- /dev/null +++ b/src/test/run-make/issue-47384/main.rs @@ -0,0 +1 @@ +extern crate lib; diff --git a/src/test/run-make/issue-71519/Makefile b/src/test/run-make/issue-71519/Makefile new file mode 100644 index 000000000..636665ec1 --- /dev/null +++ b/src/test/run-make/issue-71519/Makefile @@ -0,0 +1,6 @@ +-include ../../run-make-fulldeps/tools.mk + +# needs-rust-lld +all: + RUSTC_LOG=rustc_codegen_ssa::back::link=info $(RUSTC) -Z gcc-ld=lld -C link-args=-Wl,-v main.rs 2> $(TMPDIR)/output.txt + $(CGREP) -e "^LLD [0-9]+\.[0-9]+\.[0-9]+" < $(TMPDIR)/output.txt diff --git a/src/test/run-make/issue-71519/main.rs b/src/test/run-make/issue-71519/main.rs new file mode 100644 index 000000000..f8d09e897 --- /dev/null +++ b/src/test/run-make/issue-71519/main.rs @@ -0,0 +1,4 @@ +// test linking using cc with rust-lld injected into search path as ld +// see rust-lang/rust#71519 for more info + +fn main() {} diff --git a/src/test/run-make/issue-83112-incr-test-moved-file/Makefile b/src/test/run-make/issue-83112-incr-test-moved-file/Makefile new file mode 100644 index 000000000..2f796e5b2 --- /dev/null +++ b/src/test/run-make/issue-83112-incr-test-moved-file/Makefile @@ -0,0 +1,25 @@ +include ../../run-make-fulldeps/tools.mk + +# ignore-none no-std is not supported +# ignore-nvptx64-nvidia-cuda FIXME: can't find crate for 'std' + +# Regression test for issue #83112 +# The generated test harness code contains spans with a dummy location, +# but a non-dummy SyntaxContext. Previously, the incremental cache was encoding +# these spans as a full span (with a source file index), instead of skipping +# the encoding of the location information. If the file gest moved, the hash +# of the span will be unchanged (since it has a dummy location), so the incr +# cache would end up try to load a non-existent file using the previously +# enccoded source file id. + +SRC=$(TMPDIR)/src +INCR=$(TMPDIR)/incr + +all: + mkdir $(SRC) + mkdir $(SRC)/mydir + mkdir $(INCR) + cp main.rs $(SRC)/main.rs + $(RUSTC) --test -C incremental=$(INCR) $(SRC)/main.rs --target $(TARGET) + mv $(SRC)/main.rs $(SRC)/mydir/main.rs + $(RUSTC) --test -C incremental=$(INCR) $(SRC)/mydir/main.rs --target $(TARGET) diff --git a/src/test/run-make/issue-83112-incr-test-moved-file/main.rs b/src/test/run-make/issue-83112-incr-test-moved-file/main.rs new file mode 100644 index 000000000..f328e4d9d --- /dev/null +++ b/src/test/run-make/issue-83112-incr-test-moved-file/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/test/run-make/issue-85019-moved-src-dir/Makefile b/src/test/run-make/issue-85019-moved-src-dir/Makefile new file mode 100644 index 000000000..3606d4fdf --- /dev/null +++ b/src/test/run-make/issue-85019-moved-src-dir/Makefile @@ -0,0 +1,28 @@ +include ../../run-make-fulldeps/tools.mk + +INCR=$(TMPDIR)/incr +FIRST_SRC=$(TMPDIR)/first_src +SECOND_SRC=$(TMPDIR)/second_src + +# ignore-none no-std is not supported +# ignore-nvptx64-nvidia-cuda FIXME: can't find crate for 'std' + +# Tests that we don't get an ICE when the working directory +# (but not the build directory!) changes between compilation +# sessions + +all: + mkdir $(INCR) + # Build from 'FIRST_SRC' + mkdir $(FIRST_SRC) + cp my_lib.rs $(FIRST_SRC)/my_lib.rs + cp main.rs $(FIRST_SRC)/main.rs + cd $(FIRST_SRC) && \ + $(RUSTC) -C incremental=$(INCR) --crate-type lib my_lib.rs --target $(TARGET) && \ + $(RUSTC) -C incremental=$(INCR) --extern my_lib=$(TMPDIR)/libmy_lib.rlib main.rs --target $(TARGET) + # Build from 'SECOND_SRC', keeping the output directory and incremental directory + # the same + mv $(FIRST_SRC) $(SECOND_SRC) + cd $(SECOND_SRC) && \ + $(RUSTC) -C incremental=$(INCR) --crate-type lib my_lib.rs --target $(TARGET) && \ + $(RUSTC) -C incremental=$(INCR) --extern my_lib=$(TMPDIR)/libmy_lib.rlib main.rs --target $(TARGET) diff --git a/src/test/run-make/issue-85019-moved-src-dir/main.rs b/src/test/run-make/issue-85019-moved-src-dir/main.rs new file mode 100644 index 000000000..543559a5c --- /dev/null +++ b/src/test/run-make/issue-85019-moved-src-dir/main.rs @@ -0,0 +1,5 @@ +extern crate my_lib; + +fn main() { + my_lib::my_fn("hi"); +} diff --git a/src/test/run-make/issue-85019-moved-src-dir/my_lib.rs b/src/test/run-make/issue-85019-moved-src-dir/my_lib.rs new file mode 100644 index 000000000..432875739 --- /dev/null +++ b/src/test/run-make/issue-85019-moved-src-dir/my_lib.rs @@ -0,0 +1 @@ +pub fn my_fn<T: Copy>(_val: T) {} diff --git a/src/test/run-make/issue-85441/Makefile b/src/test/run-make/issue-85441/Makefile new file mode 100644 index 000000000..c7ae708c1 --- /dev/null +++ b/src/test/run-make/issue-85441/Makefile @@ -0,0 +1,9 @@ +# only-windows-msvc + +-include ../../run-make-fulldeps/tools.mk + +# Tests that WS2_32.dll is not unnecessarily linked, see issue #85441 + +all: + $(RUSTC) empty.rs + objdump -p $(TMPDIR)/empty.exe | $(CGREP) -v -i "WS2_32.dll" diff --git a/src/test/run-make/issue-85441/empty.rs b/src/test/run-make/issue-85441/empty.rs new file mode 100644 index 000000000..f328e4d9d --- /dev/null +++ b/src/test/run-make/issue-85441/empty.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/test/run-make/issue-88756-default-output/Makefile b/src/test/run-make/issue-88756-default-output/Makefile new file mode 100644 index 000000000..cacbcbf39 --- /dev/null +++ b/src/test/run-make/issue-88756-default-output/Makefile @@ -0,0 +1,4 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + $(BARE_RUSTDOC) 2>&1 | sed -E 's@/nightly/|/beta/|/stable/|/1\.[0-9]+\.[0-9]+/@/$$CHANNEL/@g' | diff - output-default.stdout diff --git a/src/test/run-make/issue-88756-default-output/README.md b/src/test/run-make/issue-88756-default-output/README.md new file mode 100644 index 000000000..8cbfac4f7 --- /dev/null +++ b/src/test/run-make/issue-88756-default-output/README.md @@ -0,0 +1 @@ +This is a test to verify that the default behavior of `rustdoc` is printing out help output instead of erroring out (#88756). diff --git a/src/test/run-make/issue-88756-default-output/output-default.stdout b/src/test/run-make/issue-88756-default-output/output-default.stdout new file mode 100644 index 000000000..80cd08ee1 --- /dev/null +++ b/src/test/run-make/issue-88756-default-output/output-default.stdout @@ -0,0 +1,197 @@ +rustdoc [options] <input> + +Options: + -h, --help show this help message + -V, --version print rustdoc's version + -v, --verbose use verbose output + -w, --output-format [html] + the output type to write + --output PATH Which directory to place the output. This option is + deprecated, use --out-dir instead. + -o, --out-dir PATH which directory to place the output + --crate-name NAME + specify the name of this crate + --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro] + Comma separated list of types of crates + for the compiler to emit + -L, --library-path DIR + directory to add to crate search path + --cfg pass a --cfg to rustc + --check-cfg pass a --check-cfg to rustc + --extern NAME[=PATH] + pass an --extern to rustc + --extern-html-root-url NAME=URL + base URL to use for dependencies; for example, + "std=/doc" links std::vec::Vec to + /doc/std/vec/struct.Vec.html + --extern-html-root-takes-precedence + give precedence to `--extern-html-root-url`, not + `html_root_url` + -C, --codegen OPT[=VALUE] + pass a codegen option to rustc + --document-private-items + document private items + --document-hidden-items + document items that have doc(hidden) + --test run code examples as tests + --test-args ARGS + arguments to pass to the test runner + --test-run-directory PATH + The working directory in which to run tests + --target TRIPLE target triple to document + --markdown-css FILES + CSS files to include via <link> in a rendered Markdown + file + --html-in-header FILES + files to include inline in the <head> section of a + rendered Markdown file or generated documentation + --html-before-content FILES + files to include inline between <body> and the content + of a rendered Markdown file or generated documentation + --html-after-content FILES + files to include inline between the content and + </body> of a rendered Markdown file or generated + documentation + --markdown-before-content FILES + files to include inline between <body> and the content + of a rendered Markdown file or generated documentation + --markdown-after-content FILES + files to include inline between the content and + </body> of a rendered Markdown file or generated + documentation + --markdown-playground-url URL + URL to send code snippets to + --markdown-no-toc + don't include table of contents + -e, --extend-css PATH + To add some CSS rules with a given file to generate + doc with your own theme. However, your theme might + break if the rustdoc's generated HTML changes, so be + careful! + -Z FLAG unstable / perma-unstable options (only on nightly + build) + --sysroot PATH Override the system root + --playground-url URL + URL to send code snippets to, may be reset by + --markdown-playground-url or + `#![doc(html_playground_url=...)]` + --display-doctest-warnings + show warnings that originate in doctests + --crate-version VERSION + crate version to print into documentation + --sort-modules-by-appearance + sort modules by where they appear in the program, + rather than alphabetically + --default-theme THEME + Set the default theme. THEME should be the theme name, + generally lowercase. If an unknown default theme is + specified, the builtin default is used. The set of + themes, and the rustdoc built-in default, are not + stable. + --default-setting SETTING[=VALUE] + Default value for a rustdoc setting (used when + "rustdoc-SETTING" is absent from web browser Local + Storage). If VALUE is not supplied, "true" is used. + Supported SETTINGs and VALUEs are not documented and + not stable. + --theme FILES additional themes which will be added to the generated + docs + --check-theme FILES + check if given theme is valid + --resource-suffix PATH + suffix to add to CSS and JavaScript files, e.g., + "light.css" will become "light-suffix.css" + --edition EDITION + edition to use when compiling rust code (default: + 2015) + --color auto|always|never + Configure coloring of output: + auto = colorize, if output goes to a tty (default); + always = always colorize output; + never = never colorize output + --error-format human|json|short + How errors and other messages are produced + --diagnostic-width WIDTH + Provide width of the output for truncated error + messages + --json CONFIG Configure the structure of JSON diagnostics + --disable-minification + Disable minification applied on JS files + -A, --allow LINT Set lint allowed + -W, --warn LINT Set lint warnings + --force-warn LINT + Set lint force-warn + -D, --deny LINT Set lint denied + -F, --forbid LINT Set lint forbidden + --cap-lints LEVEL + Set the most restrictive lint level. More restrictive + lints are capped at this level. By default, it is at + `forbid` level. + --index-page PATH + Markdown file to be used as index page + --enable-index-page + To enable generation of the index page + --static-root-path PATH + Path string to force loading static files from in + output pages. If not set, uses combinations of '../' + to reach the documentation root. + --disable-per-crate-search + disables generating the crate selector on the search + box + --persist-doctests PATH + Directory to persist doctest executables into + --show-coverage + calculate percentage of public items with + documentation + --enable-per-target-ignores + parse ignore-foo for ignoring doctests on a per-target + basis + --runtool The tool to run tests with when building for a different target than host + + --runtool-arg One (of possibly many) arguments to pass to the runtool + + --test-builder PATH + The rustc-like binary to use as the test builder + --check Run rustdoc checks + --generate-redirect-map + Generate JSON file at the top level instead of + generating HTML redirection files + --emit [unversioned-shared-resources,toolchain-shared-resources,invocation-specific] + Comma separated list of types of output for rustdoc to + emit + --no-run Compile doctests without running them + --show-type-layout + Include the memory layout of types in the docs + --nocapture Don't capture stdout and stderr of tests + --generate-link-to-definition + Make the identifiers in the HTML source code pages + navigable + --scrape-examples-output-path collect function call information and output at the given path + + --scrape-examples-target-crate collect function call information for functions from the target crate + + --scrape-tests Include test code when scraping examples + --with-examples path to function call information (for displaying examples in the documentation) + + --plugin-path DIR + removed, see issue #44136 + <https://github.com/rust-lang/rust/issues/44136> for + more information + --passes PASSES removed, see issue #44136 + <https://github.com/rust-lang/rust/issues/44136> for + more information + --plugins PLUGINS + removed, see issue #44136 + <https://github.com/rust-lang/rust/issues/44136> for + more information + --no-defaults removed, see issue #44136 + <https://github.com/rust-lang/rust/issues/44136> for + more information + -r, --input-format [rust] + removed, see issue #44136 + <https://github.com/rust-lang/rust/issues/44136> for + more information + + @path Read newline separated options from `path` + +More information available at https://doc.rust-lang.org/$CHANNEL/rustdoc/what-is-rustdoc.html diff --git a/src/test/run-make/issue-88756-default-output/x.rs b/src/test/run-make/issue-88756-default-output/x.rs new file mode 100644 index 000000000..5df757613 --- /dev/null +++ b/src/test/run-make/issue-88756-default-output/x.rs @@ -0,0 +1 @@ +// nothing to see here diff --git a/src/test/run-make/issue-96498/Makefile b/src/test/run-make/issue-96498/Makefile new file mode 100644 index 000000000..eae6400ae --- /dev/null +++ b/src/test/run-make/issue-96498/Makefile @@ -0,0 +1,8 @@ +# only-windows +# needs-rust-lld + +-include ../../run-make-fulldeps/tools.mk + +# Ensure that LLD can link +all: + $(RUSTC) -C linker=rust-lld foo.rs diff --git a/src/test/run-make/issue-96498/foo.rs b/src/test/run-make/issue-96498/foo.rs new file mode 100644 index 000000000..93ac3641b --- /dev/null +++ b/src/test/run-make/issue-96498/foo.rs @@ -0,0 +1,4 @@ +#![crate_type = "cdylib"] + +#[no_mangle] +extern "C" fn foo() {} diff --git a/src/test/run-make/libtest-thread-limit/Makefile b/src/test/run-make/libtest-thread-limit/Makefile new file mode 100644 index 000000000..29c1bc71d --- /dev/null +++ b/src/test/run-make/libtest-thread-limit/Makefile @@ -0,0 +1,7 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-linux + +all: + $(RUSTC) test.rs --test --target $(TARGET) + $(shell ulimit -p 0 && $(call RUN,test)) diff --git a/src/test/run-make/libtest-thread-limit/test.rs b/src/test/run-make/libtest-thread-limit/test.rs new file mode 100644 index 000000000..26bc29216 --- /dev/null +++ b/src/test/run-make/libtest-thread-limit/test.rs @@ -0,0 +1,20 @@ +#![feature(once_cell)] + +use std::{ + io::ErrorKind, + sync::OnceLock, + thread::{self, Builder, ThreadId}, +}; + +static THREAD_ID: OnceLock<ThreadId> = OnceLock::new(); + +#[test] +fn spawn_thread_would_block() { + assert_eq!(Builder::new().spawn(|| unreachable!()).unwrap_err().kind(), ErrorKind::WouldBlock); + THREAD_ID.set(thread::current().id()).unwrap(); +} + +#[test] +fn run_in_same_thread() { + assert_eq!(*THREAD_ID.get().unwrap(), thread::current().id()); +} diff --git a/src/test/run-make/llvm-outputs/Makefile b/src/test/run-make/llvm-outputs/Makefile new file mode 100644 index 000000000..d7f67577b --- /dev/null +++ b/src/test/run-make/llvm-outputs/Makefile @@ -0,0 +1,5 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + echo 'fn main() {}' | $(BARE_RUSTC) - --out-dir=$(TMPDIR)/random_directory_that_does_not_exist_ir/ --emit=llvm-ir + echo 'fn main() {}' | $(BARE_RUSTC) - --out-dir=$(TMPDIR)/random_directory_that_does_not_exist_bc/ --emit=llvm-bc diff --git a/src/test/run-make/native-link-modifier-bundle/Makefile b/src/test/run-make/native-link-modifier-bundle/Makefile new file mode 100644 index 000000000..e4b0f74ac --- /dev/null +++ b/src/test/run-make/native-link-modifier-bundle/Makefile @@ -0,0 +1,33 @@ +# ignore-cross-compile +# ignore-windows-msvc + +-include ../../run-make-fulldeps/tools.mk + +# We're using the llvm-nm instead of the system nm to ensure it is compatible +# with the LLVM bitcode generated by rustc. +NM = "$(LLVM_BIN_DIR)"/llvm-nm + +all: $(call NATIVE_STATICLIB,native-staticlib) + # Build a staticlib and a rlib, the `native_func` symbol will be bundled into them + $(RUSTC) bundled.rs --crate-type=staticlib --crate-type=rlib + $(NM) $(TMPDIR)/libbundled.a | $(CGREP) -e "T _*native_func" + $(NM) $(TMPDIR)/libbundled.a | $(CGREP) -e "U _*native_func" + $(NM) $(TMPDIR)/libbundled.rlib | $(CGREP) -e "T _*native_func" + $(NM) $(TMPDIR)/libbundled.rlib | $(CGREP) -e "U _*native_func" + + # Build a staticlib and a rlib, the `native_func` symbol will not be bundled into it + $(RUSTC) non-bundled.rs --crate-type=staticlib --crate-type=rlib + $(NM) $(TMPDIR)/libnon_bundled.a | $(CGREP) -ve "T _*native_func" + $(NM) $(TMPDIR)/libnon_bundled.a | $(CGREP) -e "U _*native_func" + $(NM) $(TMPDIR)/libnon_bundled.rlib | $(CGREP) -ve "T _*native_func" + $(NM) $(TMPDIR)/libnon_bundled.rlib | $(CGREP) -e "U _*native_func" + + # Build a cdylib, `native-staticlib` will not appear on the linker line because it was bundled previously + # The cdylib will contain the `native_func` symbol in the end + $(RUSTC) cdylib-bundled.rs --crate-type=cdylib --print link-args | $(CGREP) -ve '-l[" ]*native-staticlib' + $(NM) $(call DYLIB,cdylib_bundled) | $(CGREP) -e "[Tt] _*native_func" + + # Build a cdylib, `native-staticlib` will appear on the linker line because it was not bundled previously + # The cdylib will contain the `native_func` symbol in the end + $(RUSTC) cdylib-non-bundled.rs --crate-type=cdylib --print link-args | $(CGREP) -e '-l[" ]*native-staticlib' + $(NM) $(call DYLIB,cdylib_non_bundled) | $(CGREP) -e "[Tt] _*native_func" diff --git a/src/test/run-make/native-link-modifier-bundle/bundled.rs b/src/test/run-make/native-link-modifier-bundle/bundled.rs new file mode 100644 index 000000000..0bbae8752 --- /dev/null +++ b/src/test/run-make/native-link-modifier-bundle/bundled.rs @@ -0,0 +1,11 @@ +#[link(name = "native-staticlib", kind = "static", modifiers = "+bundle")] +extern "C" { + pub fn native_func(); +} + +#[no_mangle] +pub extern "C" fn wrapped_func() { + unsafe { + native_func(); + } +} diff --git a/src/test/run-make/native-link-modifier-bundle/cdylib-bundled.rs b/src/test/run-make/native-link-modifier-bundle/cdylib-bundled.rs new file mode 100644 index 000000000..729130916 --- /dev/null +++ b/src/test/run-make/native-link-modifier-bundle/cdylib-bundled.rs @@ -0,0 +1 @@ +extern crate bundled; diff --git a/src/test/run-make/native-link-modifier-bundle/cdylib-non-bundled.rs b/src/test/run-make/native-link-modifier-bundle/cdylib-non-bundled.rs new file mode 100644 index 000000000..1df81fd10 --- /dev/null +++ b/src/test/run-make/native-link-modifier-bundle/cdylib-non-bundled.rs @@ -0,0 +1 @@ +extern crate non_bundled; diff --git a/src/test/run-make/native-link-modifier-bundle/native-staticlib.c b/src/test/run-make/native-link-modifier-bundle/native-staticlib.c new file mode 100644 index 000000000..d300fdf1c --- /dev/null +++ b/src/test/run-make/native-link-modifier-bundle/native-staticlib.c @@ -0,0 +1 @@ +void native_func() {} diff --git a/src/test/run-make/native-link-modifier-bundle/non-bundled.rs b/src/test/run-make/native-link-modifier-bundle/non-bundled.rs new file mode 100644 index 000000000..8181e6387 --- /dev/null +++ b/src/test/run-make/native-link-modifier-bundle/non-bundled.rs @@ -0,0 +1,11 @@ +#[link(name = "native-staticlib", kind = "static", modifiers = "-bundle")] +extern "C" { + pub fn native_func(); +} + +#[no_mangle] +pub extern "C" fn wrapped_func() { + unsafe { + native_func(); + } +} diff --git a/src/test/run-make/native-link-modifier-whole-archive/Makefile b/src/test/run-make/native-link-modifier-whole-archive/Makefile new file mode 100644 index 000000000..967cb065c --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/Makefile @@ -0,0 +1,52 @@ +# ignore-cross-compile -- compiling C++ code does not work well when cross-compiling + +# This test case makes sure that native libraries are linked with appropriate semantics +# when the `[+-]bundle,[+-]whole-archive` modifiers are applied to them. +# +# The test works by checking that the resulting executables produce the expected output, +# part of which is emitted by otherwise unreferenced C code. If +whole-archive didn't work +# that code would never make it into the final executable and we'd thus be missing some +# of the output. + +-include ../../run-make-fulldeps/tools.mk + +all: $(TMPDIR)/$(call BIN,directly_linked) \ + $(TMPDIR)/$(call BIN,directly_linked_test_plus_whole_archive) \ + $(TMPDIR)/$(call BIN,directly_linked_test_minus_whole_archive) \ + $(TMPDIR)/$(call BIN,indirectly_linked) \ + $(TMPDIR)/$(call BIN,indirectly_linked_via_attr) + $(call RUN,directly_linked) | $(CGREP) 'static-initializer.directly_linked.' + $(call RUN,directly_linked_test_plus_whole_archive) --nocapture | $(CGREP) 'static-initializer.' + $(call RUN,directly_linked_test_minus_whole_archive) --nocapture | $(CGREP) -v 'static-initializer.' + $(call RUN,indirectly_linked) | $(CGREP) 'static-initializer.indirectly_linked.' + $(call RUN,indirectly_linked_via_attr) | $(CGREP) 'static-initializer.native_lib_in_src.' + +# Native lib linked directly into executable +$(TMPDIR)/$(call BIN,directly_linked): $(call NATIVE_STATICLIB,c_static_lib_with_constructor) + $(RUSTC) directly_linked.rs -l static:+whole-archive=c_static_lib_with_constructor + +# Native lib linked into test executable, +whole-archive +$(TMPDIR)/$(call BIN,directly_linked_test_plus_whole_archive): $(call NATIVE_STATICLIB,c_static_lib_with_constructor) + $(RUSTC) directly_linked_test_plus_whole_archive.rs --test -l static:+whole-archive=c_static_lib_with_constructor +# Native lib linked into test executable, -whole-archive +$(TMPDIR)/$(call BIN,directly_linked_test_minus_whole_archive): $(call NATIVE_STATICLIB,c_static_lib_with_constructor) + $(RUSTC) directly_linked_test_minus_whole_archive.rs --test -l static:-whole-archive=c_static_lib_with_constructor + +# Native lib linked into RLIB via `-l static:-bundle,+whole-archive`, RLIB linked into executable +$(TMPDIR)/$(call BIN,indirectly_linked): $(TMPDIR)/librlib_with_cmdline_native_lib.rlib + $(RUSTC) indirectly_linked.rs + +# Native lib linked into RLIB via #[link] attribute, RLIB linked into executable +$(TMPDIR)/$(call BIN,indirectly_linked_via_attr): $(TMPDIR)/libnative_lib_in_src.rlib + $(RUSTC) indirectly_linked_via_attr.rs + +# Native lib linked into rlib with via commandline +$(TMPDIR)/librlib_with_cmdline_native_lib.rlib: $(call NATIVE_STATICLIB,c_static_lib_with_constructor) + $(RUSTC) rlib_with_cmdline_native_lib.rs --crate-type=rlib -l static:-bundle,+whole-archive=c_static_lib_with_constructor + +# Native lib linked into rlib via `#[link()]` attribute on extern block. +$(TMPDIR)/libnative_lib_in_src.rlib: $(call NATIVE_STATICLIB,c_static_lib_with_constructor) + $(RUSTC) native_lib_in_src.rs --crate-type=rlib + +$(TMPDIR)/libc_static_lib_with_constructor.o: c_static_lib_with_constructor.cpp + $(call COMPILE_OBJ_CXX,$@,$<) diff --git a/src/test/run-make/native-link-modifier-whole-archive/c_static_lib_with_constructor.cpp b/src/test/run-make/native-link-modifier-whole-archive/c_static_lib_with_constructor.cpp new file mode 100644 index 000000000..c687eb0f0 --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/c_static_lib_with_constructor.cpp @@ -0,0 +1,11 @@ +#include <cstdio> + +// Since this is a global variable, its constructor will be called before +// main() is executed. But only if the object file containing it actually +// gets linked into the executable. +struct Foo { + Foo() { + printf("static-initializer."); + fflush(stdout); + } +} FOO; diff --git a/src/test/run-make/native-link-modifier-whole-archive/directly_linked.rs b/src/test/run-make/native-link-modifier-whole-archive/directly_linked.rs new file mode 100644 index 000000000..17518e8b2 --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/directly_linked.rs @@ -0,0 +1,6 @@ +use std::io::Write; + +fn main() { + print!("directly_linked."); + std::io::stdout().flush().unwrap(); +} diff --git a/src/test/run-make/native-link-modifier-whole-archive/directly_linked_test_minus_whole_archive.rs b/src/test/run-make/native-link-modifier-whole-archive/directly_linked_test_minus_whole_archive.rs new file mode 100644 index 000000000..20ed8d9d4 --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/directly_linked_test_minus_whole_archive.rs @@ -0,0 +1,7 @@ +use std::io::Write; + +#[test] +fn test_thing() { + print!("ran the test"); + std::io::stdout().flush().unwrap(); +} diff --git a/src/test/run-make/native-link-modifier-whole-archive/directly_linked_test_plus_whole_archive.rs b/src/test/run-make/native-link-modifier-whole-archive/directly_linked_test_plus_whole_archive.rs new file mode 100644 index 000000000..20ed8d9d4 --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/directly_linked_test_plus_whole_archive.rs @@ -0,0 +1,7 @@ +use std::io::Write; + +#[test] +fn test_thing() { + print!("ran the test"); + std::io::stdout().flush().unwrap(); +} diff --git a/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked.rs b/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked.rs new file mode 100644 index 000000000..c8b83fcfe --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked.rs @@ -0,0 +1,5 @@ +extern crate rlib_with_cmdline_native_lib; + +fn main() { + rlib_with_cmdline_native_lib::hello(); +} diff --git a/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked_via_attr.rs b/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked_via_attr.rs new file mode 100644 index 000000000..b9e347609 --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/indirectly_linked_via_attr.rs @@ -0,0 +1,5 @@ +extern crate native_lib_in_src; + +fn main() { + native_lib_in_src::hello(); +} diff --git a/src/test/run-make/native-link-modifier-whole-archive/native_lib_in_src.rs b/src/test/run-make/native-link-modifier-whole-archive/native_lib_in_src.rs new file mode 100644 index 000000000..971f3be7a --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/native_lib_in_src.rs @@ -0,0 +1,11 @@ +use std::io::Write; + +#[link(name = "c_static_lib_with_constructor", + kind = "static", + modifiers = "-bundle,+whole-archive")] +extern {} + +pub fn hello() { + print!("native_lib_in_src."); + std::io::stdout().flush().unwrap(); +} diff --git a/src/test/run-make/native-link-modifier-whole-archive/rlib_with_cmdline_native_lib.rs b/src/test/run-make/native-link-modifier-whole-archive/rlib_with_cmdline_native_lib.rs new file mode 100644 index 000000000..ef2b702dd --- /dev/null +++ b/src/test/run-make/native-link-modifier-whole-archive/rlib_with_cmdline_native_lib.rs @@ -0,0 +1,6 @@ +use std::io::Write; + +pub fn hello() { + print!("indirectly_linked."); + std::io::stdout().flush().unwrap(); +} diff --git a/src/test/run-make/pass-linker-flags-from-dep/Makefile b/src/test/run-make/pass-linker-flags-from-dep/Makefile new file mode 100644 index 000000000..365216b4c --- /dev/null +++ b/src/test/run-make/pass-linker-flags-from-dep/Makefile @@ -0,0 +1,10 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + # Build deps + $(RUSTC) native_dep_1.rs --crate-type=staticlib + $(RUSTC) native_dep_2.rs --crate-type=staticlib + $(RUSTC) rust_dep.rs -l static:-bundle=native_dep_1 -l link-arg=some_flag -l static:-bundle=native_dep_2 --crate-type=lib -Z unstable-options + + # Check sequence of linker args + $(RUSTC) main.rs --extern lib=$(TMPDIR)/librust_dep.rlib --crate-type=bin --print link-args | $(CGREP) -e 'native_dep_1.*some_flag.*native_dep_2' diff --git a/src/test/run-make/pass-linker-flags-from-dep/main.rs b/src/test/run-make/pass-linker-flags-from-dep/main.rs new file mode 100644 index 000000000..40952fb22 --- /dev/null +++ b/src/test/run-make/pass-linker-flags-from-dep/main.rs @@ -0,0 +1,3 @@ +fn main() { + lib::f(); +} diff --git a/src/test/run-make/pass-linker-flags-from-dep/native_dep_1.rs b/src/test/run-make/pass-linker-flags-from-dep/native_dep_1.rs new file mode 100644 index 000000000..fdb2d9ca6 --- /dev/null +++ b/src/test/run-make/pass-linker-flags-from-dep/native_dep_1.rs @@ -0,0 +1 @@ +pub fn f1() {} diff --git a/src/test/run-make/pass-linker-flags-from-dep/native_dep_2.rs b/src/test/run-make/pass-linker-flags-from-dep/native_dep_2.rs new file mode 100644 index 000000000..f788b7711 --- /dev/null +++ b/src/test/run-make/pass-linker-flags-from-dep/native_dep_2.rs @@ -0,0 +1 @@ +pub fn f2() {} diff --git a/src/test/run-make/pass-linker-flags-from-dep/rust_dep.rs b/src/test/run-make/pass-linker-flags-from-dep/rust_dep.rs new file mode 100644 index 000000000..7f5df1139 --- /dev/null +++ b/src/test/run-make/pass-linker-flags-from-dep/rust_dep.rs @@ -0,0 +1,9 @@ +extern "C" { + pub fn foo(); +} + +pub fn f() { + unsafe { + foo(); + } +} diff --git a/src/test/run-make/pass-linker-flags/Makefile b/src/test/run-make/pass-linker-flags/Makefile new file mode 100644 index 000000000..02b1e2179 --- /dev/null +++ b/src/test/run-make/pass-linker-flags/Makefile @@ -0,0 +1,4 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) rs.rs -Z unstable-options -l static=l1 -l link-arg=a1 -l static=l2 -l link-arg=a2 -l dylib=d1 -l link-arg=a3 --print link-args | $(CGREP) -e 'l1.*a1.*l2.*a2.*d1.*a3' diff --git a/src/test/run-make/pass-linker-flags/rs.rs b/src/test/run-make/pass-linker-flags/rs.rs new file mode 100644 index 000000000..f328e4d9d --- /dev/null +++ b/src/test/run-make/pass-linker-flags/rs.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/Makefile b/src/test/run-make/raw-dylib-alt-calling-convention/Makefile new file mode 100644 index 000000000..a254285ab --- /dev/null +++ b/src/test/run-make/raw-dylib-alt-calling-convention/Makefile @@ -0,0 +1,32 @@ +# Test the behavior of #[link(.., kind = "raw-dylib")] with alternative calling conventions. + +# only-x86 +# only-windows + +-include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) --crate-type lib --crate-name raw_dylib_alt_calling_convention_test lib.rs + $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" + $(call COMPILE_OBJ,"$(TMPDIR)"/extern.obj,extern.c) +ifdef IS_MSVC + $(CC) "$(TMPDIR)"/extern.obj -link -dll -out:"$(TMPDIR)"/extern.dll -noimplib +else + $(CC) "$(TMPDIR)"/extern.obj -shared -o "$(TMPDIR)"/extern.dll +endif + + "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/output.txt output.txt +else + $(DIFF) output.txt "$(TMPDIR)"/output.txt +endif + +ifdef IS_MSVC + "$(TMPDIR)"/driver true > "$(TMPDIR)"/output.msvc.txt +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/output.msvc.txt output.msvc.txt +else + $(DIFF) output.msvc.txt "$(TMPDIR)"/output.msvc.txt +endif +endif diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/driver.rs b/src/test/run-make/raw-dylib-alt-calling-convention/driver.rs new file mode 100644 index 000000000..b7f372c6b --- /dev/null +++ b/src/test/run-make/raw-dylib-alt-calling-convention/driver.rs @@ -0,0 +1,8 @@ +extern crate raw_dylib_alt_calling_convention_test; + +fn main() { + raw_dylib_alt_calling_convention_test::library_function( + std::env::args().skip(1).next().map_or( + false, + |s| std::str::FromStr::from_str(&s).unwrap())); +} diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/extern.c b/src/test/run-make/raw-dylib-alt-calling-convention/extern.c new file mode 100644 index 000000000..0c4d12af9 --- /dev/null +++ b/src/test/run-make/raw-dylib-alt-calling-convention/extern.c @@ -0,0 +1,178 @@ +#include <stdio.h> +#include <stdint.h> + +struct S { + uint8_t x; + int32_t y; +}; + +struct S2 { + int32_t x; + uint8_t y; +}; + +struct S3 { + uint8_t x[5]; +}; + +__declspec(dllexport) void __stdcall stdcall_fn_1(int i) { + printf("stdcall_fn_1(%d)\n", i); + fflush(stdout); +} + +__declspec(dllexport) void __stdcall stdcall_fn_2(uint8_t i, float f) { + printf("stdcall_fn_2(%d, %.1f)\n", i, f); + fflush(stdout); +} + +__declspec(dllexport) void __stdcall stdcall_fn_3(double d) { + printf("stdcall_fn_3(%.1f)\n", d); + fflush(stdout); +} + +__declspec(dllexport) void __stdcall stdcall_fn_4(uint8_t i, uint8_t j, float f) { + printf("stdcall_fn_4(%d, %d, %.1f)\n", i, j, f); + fflush(stdout); +} + +__declspec(dllexport) void __stdcall stdcall_fn_5(struct S s, int i) { + printf("stdcall_fn_5(S { x: %d, y: %d }, %d)\n", s.x, s.y, i); + fflush(stdout); +} + +// Test that stdcall support works correctly with the nullable pointer optimization. +__declspec(dllexport) void __stdcall stdcall_fn_6(struct S* s) { + if (s) { + printf("stdcall_fn_6(S { x: %d, y: %d })\n", s->x, s->y); + } else { + printf("stdcall_fn_6(null)\n"); + } + fflush(stdout); +} + +__declspec(dllexport) void __stdcall stdcall_fn_7(struct S2 s, int i) { + printf("stdcall_fn_7(S2 { x: %d, y: %d }, %d)\n", s.x, s.y, i); + fflush(stdout); +} + +// Verify that we compute the correct amount of space in the argument list for a 5-byte struct. +__declspec(dllexport) void __stdcall stdcall_fn_8(struct S3 s, struct S3 t) { + printf("stdcall_fn_8(S3 { x: [%d, %d, %d, %d, %d] }, S3 { x: [%d, %d, %d, %d, %d] })\n", + s.x[0], s.x[1], s.x[2], s.x[3], s.x[4], + t.x[0], t.x[1], t.x[2], t.x[3], t.x[4] + ); + fflush(stdout); +} + +// test whether f64/double values are aligned on 4-byte or 8-byte boundaries. +__declspec(dllexport) void __stdcall stdcall_fn_9(uint8_t x, double y) { + printf("stdcall_fn_9(%d, %.1f)\n", x, y); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_1(int i) { + printf("fastcall_fn_1(%d)\n", i); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_2(uint8_t i, float f) { + printf("fastcall_fn_2(%d, %.1f)\n", i, f); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_3(double d) { + printf("fastcall_fn_3(%.1f)\n", d); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_4(uint8_t i, uint8_t j, float f) { + printf("fastcall_fn_4(%d, %d, %.1f)\n", i, j, f); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_5(struct S s, int i) { + printf("fastcall_fn_5(S { x: %d, y: %d }, %d)\n", s.x, s.y, i); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_6(struct S* s) { + if (s) { + printf("fastcall_fn_6(S { x: %d, y: %d })\n", s->x, s->y); + } else { + printf("fastcall_fn_6(null)\n"); + } + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_7(struct S2 s, int i) { + printf("fastcall_fn_7(S2 { x: %d, y: %d }, %d)\n", s.x, s.y, i); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_8(struct S3 s, struct S3 t) { + printf("fastcall_fn_8(S3 { x: [%d, %d, %d, %d, %d] }, S3 { x: [%d, %d, %d, %d, %d] })\n", + s.x[0], s.x[1], s.x[2], s.x[3], s.x[4], + t.x[0], t.x[1], t.x[2], t.x[3], t.x[4] + ); + fflush(stdout); +} + +__declspec(dllexport) void __fastcall fastcall_fn_9(uint8_t x, double y) { + printf("fastcall_fn_9(%d, %.1f)\n", x, y); + fflush(stdout); +} + +// GCC doesn't support vectorcall: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485 +#ifdef _MSC_VER +__declspec(dllexport) void __vectorcall vectorcall_fn_1(int i) { + printf("vectorcall_fn_1(%d)\n", i); + fflush(stdout); +} + +__declspec(dllexport) void __vectorcall vectorcall_fn_2(uint8_t i, float f) { + printf("vectorcall_fn_2(%d, %.1f)\n", i, f); + fflush(stdout); +} + +__declspec(dllexport) void __vectorcall vectorcall_fn_3(double d) { + printf("vectorcall_fn_3(%.1f)\n", d); + fflush(stdout); +} + +__declspec(dllexport) void __vectorcall vectorcall_fn_4(uint8_t i, uint8_t j, float f) { + printf("vectorcall_fn_4(%d, %d, %.1f)\n", i, j, f); + fflush(stdout); +} + +__declspec(dllexport) void __vectorcall vectorcall_fn_5(struct S s, int i) { + printf("vectorcall_fn_5(S { x: %d, y: %d }, %d)\n", s.x, s.y, i); + fflush(stdout); +} + +__declspec(dllexport) void __vectorcall vectorcall_fn_6(struct S* s) { + if (s) { + printf("vectorcall_fn_6(S { x: %d, y: %d })\n", s->x, s->y); + } else { + printf("vectorcall_fn_6(null)\n"); + } + fflush(stdout); +} + +__declspec(dllexport) void __vectorcall vectorcall_fn_7(struct S2 s, int i) { + printf("vectorcall_fn_7(S2 { x: %d, y: %d }, %d)\n", s.x, s.y, i); + fflush(stdout); +} + +__declspec(dllexport) void __vectorcall vectorcall_fn_8(struct S3 s, struct S3 t) { + printf("vectorcall_fn_8(S3 { x: [%d, %d, %d, %d, %d] }, S3 { x: [%d, %d, %d, %d, %d] })\n", + s.x[0], s.x[1], s.x[2], s.x[3], s.x[4], + t.x[0], t.x[1], t.x[2], t.x[3], t.x[4] + ); + fflush(stdout); +} + +__declspec(dllexport) void __vectorcall vectorcall_fn_9(uint8_t x, double y) { + printf("vectorcall_fn_9(%d, %.1f)\n", x, y); + fflush(stdout); +} +#endif diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs b/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs new file mode 100644 index 000000000..b5e9415b2 --- /dev/null +++ b/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs @@ -0,0 +1,106 @@ +#![feature(raw_dylib)] +#![feature(abi_vectorcall)] + +#[repr(C)] +#[derive(Clone)] +struct S { + x: u8, + y: i32, +} + +#[repr(C)] +#[derive(Clone)] +struct S2 { + x: i32, + y: u8, +} + +#[repr(C)] +#[derive(Clone)] +struct S3 { + x: [u8; 5], +} + +#[link(name = "extern", kind = "raw-dylib")] +extern "stdcall" { + fn stdcall_fn_1(i: i32); + fn stdcall_fn_2(c: u8, f: f32); + fn stdcall_fn_3(d: f64); + fn stdcall_fn_4(i: u8, j: u8, f: f32); + fn stdcall_fn_5(a: S, b: i32); + fn stdcall_fn_6(a: Option<&S>); + fn stdcall_fn_7(a: S2, b: i32); + fn stdcall_fn_8(a: S3, b: S3); + fn stdcall_fn_9(x: u8, y: f64); +} + +#[link(name = "extern", kind = "raw-dylib")] +extern "fastcall" { + fn fastcall_fn_1(i: i32); + fn fastcall_fn_2(c: u8, f: f32); + fn fastcall_fn_3(d: f64); + fn fastcall_fn_4(i: u8, j: u8, f: f32); + fn fastcall_fn_5(a: S, b: i32); + fn fastcall_fn_6(a: Option<&S>); + fn fastcall_fn_7(a: S2, b: i32); + fn fastcall_fn_8(a: S3, b: S3); + fn fastcall_fn_9(x: u8, y: f64); +} + +#[cfg(target_env = "msvc")] +#[link(name = "extern", kind = "raw-dylib")] +extern "vectorcall" { + fn vectorcall_fn_1(i: i32); + fn vectorcall_fn_2(c: u8, f: f32); + fn vectorcall_fn_3(d: f64); + fn vectorcall_fn_4(i: u8, j: u8, f: f32); + fn vectorcall_fn_5(a: S, b: i32); + fn vectorcall_fn_6(a: Option<&S>); + fn vectorcall_fn_7(a: S2, b: i32); + fn vectorcall_fn_8(a: S3, b: S3); + fn vectorcall_fn_9(x: u8, y: f64); +} + +pub fn library_function(run_msvc_only: bool) { + unsafe { + if !run_msvc_only { + stdcall_fn_1(14); + stdcall_fn_2(16, 3.5); + stdcall_fn_3(3.5); + stdcall_fn_4(1, 2, 3.0); + stdcall_fn_5(S { x: 1, y: 2 }, 16); + stdcall_fn_6(Some(&S { x: 10, y: 12 })); + stdcall_fn_7(S2 { x: 15, y: 16 }, 3); + stdcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }); + stdcall_fn_9(1, 3.0); + + fastcall_fn_1(14); + fastcall_fn_2(16, 3.5); + fastcall_fn_3(3.5); + fastcall_fn_4(1, 2, 3.0); + fastcall_fn_6(Some(&S { x: 10, y: 12 })); + fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }); + fastcall_fn_9(1, 3.0); + } else { + // FIXME: 91167 + // rustc generates incorrect code for the calls to fastcall_fn_5 and fastcall_fn_7 + // on i686-pc-windows-gnu; disabling these until the indicated issue is fixed. + fastcall_fn_5(S { x: 1, y: 2 }, 16); + fastcall_fn_7(S2 { x: 15, y: 16 }, 3); + + // GCC doesn't support vectorcall: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485 + #[cfg(target_env = "msvc")] + { + vectorcall_fn_1(14); + vectorcall_fn_2(16, 3.5); + vectorcall_fn_3(3.5); + vectorcall_fn_4(1, 2, 3.0); + vectorcall_fn_5(S { x: 1, y: 2 }, 16); + vectorcall_fn_6(Some(&S { x: 10, y: 12 })); + vectorcall_fn_7(S2 { x: 15, y: 16 }, 3); + vectorcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }); + vectorcall_fn_9(1, 3.0); + } + } + } +} diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/output.msvc.txt b/src/test/run-make/raw-dylib-alt-calling-convention/output.msvc.txt new file mode 100644 index 000000000..9ddd1b110 --- /dev/null +++ b/src/test/run-make/raw-dylib-alt-calling-convention/output.msvc.txt @@ -0,0 +1,11 @@ +fastcall_fn_5(S { x: 1, y: 2 }, 16) +fastcall_fn_7(S2 { x: 15, y: 16 }, 3) +vectorcall_fn_1(14) +vectorcall_fn_2(16, 3.5) +vectorcall_fn_3(3.5) +vectorcall_fn_4(1, 2, 3.0) +vectorcall_fn_5(S { x: 1, y: 2 }, 16) +vectorcall_fn_6(S { x: 10, y: 12 }) +vectorcall_fn_7(S2 { x: 15, y: 16 }, 3) +vectorcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }) +vectorcall_fn_9(1, 3.0) diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/output.txt b/src/test/run-make/raw-dylib-alt-calling-convention/output.txt new file mode 100644 index 000000000..348bad63e --- /dev/null +++ b/src/test/run-make/raw-dylib-alt-calling-convention/output.txt @@ -0,0 +1,16 @@ +stdcall_fn_1(14) +stdcall_fn_2(16, 3.5) +stdcall_fn_3(3.5) +stdcall_fn_4(1, 2, 3.0) +stdcall_fn_5(S { x: 1, y: 2 }, 16) +stdcall_fn_6(S { x: 10, y: 12 }) +stdcall_fn_7(S2 { x: 15, y: 16 }, 3) +stdcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }) +stdcall_fn_9(1, 3.0) +fastcall_fn_1(14) +fastcall_fn_2(16, 3.5) +fastcall_fn_3(3.5) +fastcall_fn_4(1, 2, 3.0) +fastcall_fn_6(S { x: 10, y: 12 }) +fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }) +fastcall_fn_9(1, 3.0) diff --git a/src/test/run-make/raw-dylib-c/Makefile b/src/test/run-make/raw-dylib-c/Makefile new file mode 100644 index 000000000..713f66507 --- /dev/null +++ b/src/test/run-make/raw-dylib-c/Makefile @@ -0,0 +1,28 @@ +# Test the behavior of #[link(.., kind = "raw-dylib")] on windows-msvc + +# only-windows + +-include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs + $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" + $(RUSTC) --crate-type bin --crate-name raw_dylib_test_bin lib.rs + $(call COMPILE_OBJ,"$(TMPDIR)"/extern_1.obj,extern_1.c) + $(call COMPILE_OBJ,"$(TMPDIR)"/extern_2.obj,extern_2.c) +ifdef IS_MSVC + $(CC) "$(TMPDIR)"/extern_1.obj -link -dll -out:"$(TMPDIR)"/extern_1.dll -noimplib + $(CC) "$(TMPDIR)"/extern_2.obj -link -dll -out:"$(TMPDIR)"/extern_2.dll -noimplib +else + $(CC) "$(TMPDIR)"/extern_1.obj -shared -o "$(TMPDIR)"/extern_1.dll + $(CC) "$(TMPDIR)"/extern_2.obj -shared -o "$(TMPDIR)"/extern_2.dll +endif + "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt + "$(TMPDIR)"/raw_dylib_test_bin > "$(TMPDIR)"/output_bin.txt + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/output.txt output.txt +else + $(DIFF) output.txt "$(TMPDIR)"/output.txt + $(DIFF) output.txt "$(TMPDIR)"/output_bin.txt +endif diff --git a/src/test/run-make/raw-dylib-c/driver.rs b/src/test/run-make/raw-dylib-c/driver.rs new file mode 100644 index 000000000..4059ede11 --- /dev/null +++ b/src/test/run-make/raw-dylib-c/driver.rs @@ -0,0 +1,5 @@ +extern crate raw_dylib_test; + +fn main() { + raw_dylib_test::library_function(); +} diff --git a/src/test/run-make/raw-dylib-c/extern_1.c b/src/test/run-make/raw-dylib-c/extern_1.c new file mode 100644 index 000000000..72737c086 --- /dev/null +++ b/src/test/run-make/raw-dylib-c/extern_1.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +__declspec(dllexport) void extern_fn_1() { + printf("extern_fn_1\n"); + fflush(stdout); +} + +__declspec(dllexport) void extern_fn_2() { + printf("extern_fn_2; didn't get the rename\n"); + fflush(stdout); +} + +__declspec(dllexport) void extern_fn_with_long_name() { + printf("extern_fn_with_long_name; got the rename\n"); + fflush(stdout); +} diff --git a/src/test/run-make/raw-dylib-c/extern_2.c b/src/test/run-make/raw-dylib-c/extern_2.c new file mode 100644 index 000000000..ae87fc3f8 --- /dev/null +++ b/src/test/run-make/raw-dylib-c/extern_2.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +__declspec(dllexport) void extern_fn_3() { + printf("extern_fn_3\n"); + fflush(stdout); +} diff --git a/src/test/run-make/raw-dylib-c/lib.rs b/src/test/run-make/raw-dylib-c/lib.rs new file mode 100644 index 000000000..58f7ccb38 --- /dev/null +++ b/src/test/run-make/raw-dylib-c/lib.rs @@ -0,0 +1,26 @@ +#![feature(raw_dylib, native_link_modifiers_verbatim)] + +#[link(name = "extern_1.dll", kind = "raw-dylib", modifiers = "+verbatim")] +extern { + fn extern_fn_1(); +} + +#[link(name = "extern_2", kind = "raw-dylib")] +extern { + fn extern_fn_3(); +} + +pub fn library_function() { + #[link(name = "extern_1", kind = "raw-dylib")] + extern { fn extern_fn_2(); } + + unsafe { + extern_fn_1(); + extern_fn_2(); + extern_fn_3(); + } +} + +fn main() { + library_function(); +} diff --git a/src/test/run-make/raw-dylib-c/output.txt b/src/test/run-make/raw-dylib-c/output.txt new file mode 100644 index 000000000..7800cba18 --- /dev/null +++ b/src/test/run-make/raw-dylib-c/output.txt @@ -0,0 +1,3 @@ +extern_fn_1 +extern_fn_2; didn't get the rename +extern_fn_3 diff --git a/src/test/run-make/raw-dylib-link-ordinal/Makefile b/src/test/run-make/raw-dylib-link-ordinal/Makefile new file mode 100644 index 000000000..c9baa3c1e --- /dev/null +++ b/src/test/run-make/raw-dylib-link-ordinal/Makefile @@ -0,0 +1,22 @@ +# Test the behavior of #[link(.., kind = "raw-dylib")] and #[link_ordinal] on windows-msvc + +# only-windows + +-include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs + $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" + $(call COMPILE_OBJ,"$(TMPDIR)"/exporter.obj,exporter.c) +ifdef IS_MSVC + $(CC) "$(TMPDIR)"/exporter.obj exporter.def -link -dll -out:"$(TMPDIR)"/exporter.dll -noimplib +else + $(CC) "$(TMPDIR)"/exporter.obj exporter.def -shared -o "$(TMPDIR)"/exporter.dll +endif + "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/output.txt output.txt +else + $(DIFF) output.txt "$(TMPDIR)"/output.txt +endif diff --git a/src/test/run-make/raw-dylib-link-ordinal/driver.rs b/src/test/run-make/raw-dylib-link-ordinal/driver.rs new file mode 100644 index 000000000..4059ede11 --- /dev/null +++ b/src/test/run-make/raw-dylib-link-ordinal/driver.rs @@ -0,0 +1,5 @@ +extern crate raw_dylib_test; + +fn main() { + raw_dylib_test::library_function(); +} diff --git a/src/test/run-make/raw-dylib-link-ordinal/exporter.c b/src/test/run-make/raw-dylib-link-ordinal/exporter.c new file mode 100644 index 000000000..a9dd6da66 --- /dev/null +++ b/src/test/run-make/raw-dylib-link-ordinal/exporter.c @@ -0,0 +1,5 @@ +#include <stdio.h> + +void exported_function() { + printf("exported_function\n"); +} diff --git a/src/test/run-make/raw-dylib-link-ordinal/exporter.def b/src/test/run-make/raw-dylib-link-ordinal/exporter.def new file mode 100644 index 000000000..1a4b4c941 --- /dev/null +++ b/src/test/run-make/raw-dylib-link-ordinal/exporter.def @@ -0,0 +1,3 @@ +LIBRARY exporter +EXPORTS + exported_function @13 NONAME diff --git a/src/test/run-make/raw-dylib-link-ordinal/lib.rs b/src/test/run-make/raw-dylib-link-ordinal/lib.rs new file mode 100644 index 000000000..20609caa5 --- /dev/null +++ b/src/test/run-make/raw-dylib-link-ordinal/lib.rs @@ -0,0 +1,13 @@ +#![feature(raw_dylib)] + +#[link(name = "exporter", kind = "raw-dylib")] +extern { + #[link_ordinal(13)] + fn imported_function(); +} + +pub fn library_function() { + unsafe { + imported_function(); + } +} diff --git a/src/test/run-make/raw-dylib-link-ordinal/output.txt b/src/test/run-make/raw-dylib-link-ordinal/output.txt new file mode 100644 index 000000000..2d0ed60f2 --- /dev/null +++ b/src/test/run-make/raw-dylib-link-ordinal/output.txt @@ -0,0 +1 @@ +exported_function diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/Makefile b/src/test/run-make/raw-dylib-stdcall-ordinal/Makefile new file mode 100644 index 000000000..3360a97b5 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/Makefile @@ -0,0 +1,23 @@ +# Test the behavior of #[link(.., kind = "raw-dylib")], #[link_ordinal], and alternative calling conventions on i686 windows. + +# only-x86 +# only-windows + +-include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs + $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" + $(call COMPILE_OBJ,"$(TMPDIR)"/exporter.obj,exporter.c) +ifdef IS_MSVC + $(CC) "$(TMPDIR)"/exporter.obj exporter-msvc.def -link -dll -out:"$(TMPDIR)"/exporter.dll -noimplib +else + $(CC) "$(TMPDIR)"/exporter.obj exporter-gnu.def -shared -o "$(TMPDIR)"/exporter.dll +endif + "$(TMPDIR)"/driver > "$(TMPDIR)"/actual_output.txt + +ifdef RUSTC_BLESS_TEST + cp "$(TMPDIR)"/actual_output.txt expected_output.txt +else + $(DIFF) expected_output.txt "$(TMPDIR)"/actual_output.txt +endif diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs b/src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs new file mode 100644 index 000000000..4059ede11 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/driver.rs @@ -0,0 +1,5 @@ +extern crate raw_dylib_test; + +fn main() { + raw_dylib_test::library_function(); +} diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/expected_output.txt b/src/test/run-make/raw-dylib-stdcall-ordinal/expected_output.txt new file mode 100644 index 000000000..201577637 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/expected_output.txt @@ -0,0 +1,2 @@ +exported_function_stdcall(6) +exported_function_fastcall(125) diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-gnu.def b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-gnu.def new file mode 100644 index 000000000..8d28d714b --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-gnu.def @@ -0,0 +1,4 @@ +LIBRARY exporter +EXPORTS + exported_function_stdcall@4 @15 NONAME + @exported_function_fastcall@4 @18 NONAME
\ No newline at end of file diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-msvc.def b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-msvc.def new file mode 100644 index 000000000..5a4c79a58 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter-msvc.def @@ -0,0 +1,4 @@ +LIBRARY exporter +EXPORTS + _exported_function_stdcall@4 @15 NONAME + @exported_function_fastcall@4 @18 NONAME
\ No newline at end of file diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c new file mode 100644 index 000000000..1fb45bf01 --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/exporter.c @@ -0,0 +1,11 @@ +#include <stdio.h> + +void __stdcall exported_function_stdcall(int i) { + printf("exported_function_stdcall(%d)\n", i); + fflush(stdout); +} + +void __fastcall exported_function_fastcall(int i) { + printf("exported_function_fastcall(%d)\n", i); + fflush(stdout); +} diff --git a/src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs b/src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs new file mode 100644 index 000000000..07dd3d7be --- /dev/null +++ b/src/test/run-make/raw-dylib-stdcall-ordinal/lib.rs @@ -0,0 +1,20 @@ +#![feature(raw_dylib)] + +#[link(name = "exporter", kind = "raw-dylib")] +extern "stdcall" { + #[link_ordinal(15)] + fn imported_function_stdcall(i: i32); +} + +#[link(name = "exporter", kind = "raw-dylib")] +extern "fastcall" { + #[link_ordinal(18)] + fn imported_function_fastcall(i: i32); +} + +pub fn library_function() { + unsafe { + imported_function_stdcall(6); + imported_function_fastcall(125); + } +} diff --git a/src/test/run-make/remap-path-prefix-dwarf/Makefile b/src/test/run-make/remap-path-prefix-dwarf/Makefile new file mode 100644 index 000000000..561a343d6 --- /dev/null +++ b/src/test/run-make/remap-path-prefix-dwarf/Makefile @@ -0,0 +1,77 @@ +# This test makes sure that --remap-path-prefix has the expected effects on paths in debuginfo. +# It tests several cases, each of them has a detailed description attached to it. + +# ignore-windows + +SRC_DIR := $(abspath .) +SRC_DIR_PARENT := $(abspath ..) + +-include ../../run-make-fulldeps/tools.mk + +all: \ + abs_input_outside_working_dir \ + rel_input_remap_working_dir \ + rel_input_remap_working_dir_parent \ + rel_input_remap_working_dir_child \ + abs_input_inside_working_dir \ + abs_input_outside_working_dir + +# The compiler is called with an *ABSOLUTE PATH* as input, and that absolute path *is* within +# the working directory of the compiler. We are remapping the path that contains `src`. +abs_input_inside_working_dir: + # We explicitly switch to a directory that *is* a prefix of the directory our + # source code is contained in. + cd $(SRC_DIR) && $(RUSTC) $(SRC_DIR)/src/quux.rs -o "$(TMPDIR)/abs_input_inside_working_dir.rlib" -Cdebuginfo=2 --remap-path-prefix $(SRC_DIR)=REMAPPED + # We expect the path to the main source file to be remapped. + "$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_inside_working_dir.rlib | $(CGREP) "REMAPPED/src/quux.rs" + # No weird duplication of remapped components (see #78479) + "$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_inside_working_dir.rlib | $(CGREP) -v "REMAPPED/REMAPPED" + +# The compiler is called with an *ABSOLUTE PATH* as input, and that absolute path is *not* within +# the working directory of the compiler. We are remapping both the path that contains `src` and +# the working directory to the same thing. This setup corresponds to a workaround that is needed +# when trying to remap everything to something that looks like a local path. +# Relative paths are interpreted as relative to the compiler's working directory (e.g. in +# debuginfo). If we also remap the working directory, the compiler strip it from other paths so +# that the final outcome is the desired one again. +abs_input_outside_working_dir: + # We explicitly switch to a directory that is *not* a prefix of the directory our + # source code is contained in. + cd $(TMPDIR) && $(RUSTC) $(SRC_DIR)/src/quux.rs -o "$(TMPDIR)/abs_input_outside_working_dir.rlib" -Cdebuginfo=2 --remap-path-prefix $(SRC_DIR)=REMAPPED --remap-path-prefix $(TMPDIR)=REMAPPED + "$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_outside_working_dir.rlib | $(CGREP) "REMAPPED/src/quux.rs" + # No weird duplication of remapped components (see #78479) + "$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_outside_working_dir.rlib | $(CGREP) -v "REMAPPED/REMAPPED" + +# The compiler is called with a *RELATIVE PATH* as input. We are remapping the working directory of +# the compiler, which naturally is an implicit prefix of our relative input path. Debuginfo will +# expand the relative path to an absolute path and we expect the working directory to be remapped +# in that expansion. +rel_input_remap_working_dir: + cd $(SRC_DIR) && $(RUSTC) src/quux.rs -o "$(TMPDIR)/rel_input_remap_working_dir.rlib" -Cdebuginfo=2 --remap-path-prefix "$(SRC_DIR)=REMAPPED" + "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir.rlib" | $(CGREP) "REMAPPED/src/quux.rs" + # No weird duplication of remapped components (see #78479) + "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir.rlib" | $(CGREP) -v "REMAPPED/REMAPPED" + +# The compiler is called with a *RELATIVE PATH* as input. We are remapping a *SUB-DIRECTORY* of the +# compiler's working directory. This test makes sure that that directory is remapped even though it +# won't actually show up in this form in the compiler's SourceMap and instead is only constructed +# on demand during debuginfo generation. +rel_input_remap_working_dir_child: + cd $(SRC_DIR) && $(RUSTC) src/quux.rs -o "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" -Cdebuginfo=2 --remap-path-prefix "$(SRC_DIR)/src=REMAPPED" + # We expect `src/quux.rs` to have been remapped to `REMAPPED/quux.rs`. + "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" | $(CGREP) "REMAPPED/quux.rs" + # We don't want to find the path that we just remapped anywhere in the DWARF + "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" | $(CGREP) -v "$(SRC_DIR)/src" + # No weird duplication of remapped components (see #78479) + "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_child.rlib" | $(CGREP) -v "REMAPPED/REMAPPED" + +# The compiler is called with a *RELATIVE PATH* as input. We are remapping a *PARENT DIRECTORY* of +# the compiler's working directory. +rel_input_remap_working_dir_parent: + cd $(SRC_DIR) && $(RUSTC) src/quux.rs -o "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" -Cdebuginfo=2 --remap-path-prefix "$(SRC_DIR_PARENT)=REMAPPED" + # We expect `src/quux.rs` to have been remapped to `REMAPPED/remap-path-prefix-dwarf/src/quux.rs`. + "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" | $(CGREP) "REMAPPED/remap-path-prefix-dwarf/src/quux.rs" + # We don't want to find the path that we just remapped anywhere in the DWARF + "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" | $(CGREP) -v "$(SRC_DIR_PARENT)" + # No weird duplication of remapped components (see #78479) + "$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_parent.rlib" | $(CGREP) -v "REMAPPED/REMAPPED" diff --git a/src/test/run-make/remap-path-prefix-dwarf/src/quux.rs b/src/test/run-make/remap-path-prefix-dwarf/src/quux.rs new file mode 100644 index 000000000..38d5ef619 --- /dev/null +++ b/src/test/run-make/remap-path-prefix-dwarf/src/quux.rs @@ -0,0 +1,5 @@ +#![crate_type = "rlib"] + +pub fn foo() { + println!("foo"); +} diff --git a/src/test/run-make/rustc-macro-dep-files/Makefile b/src/test/run-make/rustc-macro-dep-files/Makefile new file mode 100644 index 000000000..a08a63fb4 --- /dev/null +++ b/src/test/run-make/rustc-macro-dep-files/Makefile @@ -0,0 +1,11 @@ +-include ../../run-make-fulldeps/tools.mk + +# FIXME(eddyb) provide `HOST_RUSTC` and `TARGET_RUSTC` +# instead of hardcoding them everywhere they're needed. +ifeq ($(IS_MUSL_HOST),1) +ADDITIONAL_ARGS := $(RUSTFLAGS) +endif +all: + $(BARE_RUSTC) $(ADDITIONAL_ARGS) foo.rs --out-dir $(TMPDIR) + $(RUSTC) bar.rs --target $(TARGET) --emit dep-info + $(CGREP) -v "proc-macro source" < $(TMPDIR)/bar.d diff --git a/src/test/run-make/rustc-macro-dep-files/bar.rs b/src/test/run-make/rustc-macro-dep-files/bar.rs new file mode 100644 index 000000000..4a3b3364b --- /dev/null +++ b/src/test/run-make/rustc-macro-dep-files/bar.rs @@ -0,0 +1,8 @@ +#![no_std] +#![crate_type = "lib"] + +#[macro_use] +extern crate foo; + +#[derive(A)] +struct A; diff --git a/src/test/run-make/rustc-macro-dep-files/foo.rs b/src/test/run-make/rustc-macro-dep-files/foo.rs new file mode 100644 index 000000000..66db1a217 --- /dev/null +++ b/src/test/run-make/rustc-macro-dep-files/foo.rs @@ -0,0 +1,12 @@ +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_derive(A)] +pub fn derive(input: TokenStream) -> TokenStream { + let input = input.to_string(); + assert!(input.contains("struct A ;")); + "struct B;".parse().unwrap() +} diff --git a/src/test/run-make/rustdoc-scrape-examples-invalid-expr/Makefile b/src/test/run-make/rustdoc-scrape-examples-invalid-expr/Makefile new file mode 100644 index 000000000..dce8b83ee --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-invalid-expr/Makefile @@ -0,0 +1,5 @@ +deps := ex + +-include ../rustdoc-scrape-examples-multiple/scrape.mk + +all: scrape diff --git a/src/test/run-make/rustdoc-scrape-examples-invalid-expr/examples/ex.rs b/src/test/run-make/rustdoc-scrape-examples-invalid-expr/examples/ex.rs new file mode 100644 index 000000000..b342b5b0a --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-invalid-expr/examples/ex.rs @@ -0,0 +1,2 @@ +pub struct Foo([usize; foobar::f()]); +fn main() {} diff --git a/src/test/run-make/rustdoc-scrape-examples-invalid-expr/src/lib.rs b/src/test/run-make/rustdoc-scrape-examples-invalid-expr/src/lib.rs new file mode 100644 index 000000000..c30c99dec --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-invalid-expr/src/lib.rs @@ -0,0 +1 @@ +pub const fn f() -> usize { 5 } diff --git a/src/test/run-make/rustdoc-scrape-examples-multiple/Makefile b/src/test/run-make/rustdoc-scrape-examples-multiple/Makefile new file mode 100644 index 000000000..897805e44 --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-multiple/Makefile @@ -0,0 +1,5 @@ +deps := ex ex2 + +-include ./scrape.mk + +all: scrape diff --git a/src/test/run-make/rustdoc-scrape-examples-multiple/examples/ex.rs b/src/test/run-make/rustdoc-scrape-examples-multiple/examples/ex.rs new file mode 100644 index 000000000..01b730c61 --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-multiple/examples/ex.rs @@ -0,0 +1,4 @@ +fn main() { + foobar::ok(); + foobar::ok(); +} diff --git a/src/test/run-make/rustdoc-scrape-examples-multiple/examples/ex2.rs b/src/test/run-make/rustdoc-scrape-examples-multiple/examples/ex2.rs new file mode 100644 index 000000000..f83cf2f27 --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-multiple/examples/ex2.rs @@ -0,0 +1,3 @@ +fn main() { + foobar::ok(); +} diff --git a/src/test/run-make/rustdoc-scrape-examples-multiple/scrape.mk b/src/test/run-make/rustdoc-scrape-examples-multiple/scrape.mk new file mode 100644 index 000000000..d49b6c1f2 --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-multiple/scrape.mk @@ -0,0 +1,21 @@ +-include ../../run-make-fulldeps/tools.mk + +OUTPUT_DIR := "$(TMPDIR)/rustdoc" + +$(TMPDIR)/%.calls: $(TMPDIR)/libfoobar.rmeta + $(RUSTDOC) examples/$*.rs --crate-name $* --crate-type bin --output $(OUTPUT_DIR) \ + --extern foobar=$(TMPDIR)/libfoobar.rmeta \ + -Z unstable-options \ + --scrape-examples-output-path $@ \ + --scrape-examples-target-crate foobar \ + $(extra_flags) + +$(TMPDIR)/lib%.rmeta: src/lib.rs + $(RUSTC) src/lib.rs --crate-name $* --crate-type lib --emit=metadata + +scrape: $(foreach d,$(deps),$(TMPDIR)/$(d).calls) + $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --output $(OUTPUT_DIR) \ + -Z unstable-options \ + $(foreach d,$(deps),--with-examples $(TMPDIR)/$(d).calls) + + $(HTMLDOCCK) $(OUTPUT_DIR) src/lib.rs diff --git a/src/test/run-make/rustdoc-scrape-examples-multiple/src/lib.rs b/src/test/run-make/rustdoc-scrape-examples-multiple/src/lib.rs new file mode 100644 index 000000000..bdfeda92d --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-multiple/src/lib.rs @@ -0,0 +1,6 @@ +// @has foobar/fn.ok.html '//*[@class="docblock scraped-example-list"]//*[@class="prev"]' '' +// @has foobar/fn.ok.html '//*[@class="more-scraped-examples"]' '' +// @has src/ex/ex.rs.html +// @has foobar/fn.ok.html '//a[@href="../src/ex/ex.rs.html#2"]' '' + +pub fn ok() {} diff --git a/src/test/run-make/rustdoc-scrape-examples-ordering/Makefile b/src/test/run-make/rustdoc-scrape-examples-ordering/Makefile new file mode 100644 index 000000000..339d539bf --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-ordering/Makefile @@ -0,0 +1,5 @@ +deps := ex1 ex2 + +-include ../rustdoc-scrape-examples-multiple/scrape.mk + +all: scrape diff --git a/src/test/run-make/rustdoc-scrape-examples-ordering/examples/ex1.rs b/src/test/run-make/rustdoc-scrape-examples-ordering/examples/ex1.rs new file mode 100644 index 000000000..05c18007b --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-ordering/examples/ex1.rs @@ -0,0 +1,11 @@ +fn main() { + foobar::ok(0); + + // this is a + + // .. + + // BIG + + // item +} diff --git a/src/test/run-make/rustdoc-scrape-examples-ordering/examples/ex2.rs b/src/test/run-make/rustdoc-scrape-examples-ordering/examples/ex2.rs new file mode 100644 index 000000000..de21d9061 --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-ordering/examples/ex2.rs @@ -0,0 +1,8 @@ +fn main() { + foobar::ok(1); + // small item +} + +fn f() { + foobar::ok(2); +} diff --git a/src/test/run-make/rustdoc-scrape-examples-ordering/src/lib.rs b/src/test/run-make/rustdoc-scrape-examples-ordering/src/lib.rs new file mode 100644 index 000000000..c53c987a7 --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-ordering/src/lib.rs @@ -0,0 +1,6 @@ +// @has foobar/fn.ok.html '//*[@class="docblock scraped-example-list"]' 'ex2' +// @has foobar/fn.ok.html '//*[@class="more-scraped-examples"]' 'ex1' +// @has foobar/fn.ok.html '//*[@class="highlight focus"]' 'ok' +// @has foobar/fn.ok.html '//*[@class="highlight"]' 'ok' + +pub fn ok(_x: i32) {} diff --git a/src/test/run-make/rustdoc-scrape-examples-remap/Makefile b/src/test/run-make/rustdoc-scrape-examples-remap/Makefile new file mode 100644 index 000000000..dce8b83ee --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-remap/Makefile @@ -0,0 +1,5 @@ +deps := ex + +-include ../rustdoc-scrape-examples-multiple/scrape.mk + +all: scrape diff --git a/src/test/run-make/rustdoc-scrape-examples-remap/examples/ex.rs b/src/test/run-make/rustdoc-scrape-examples-remap/examples/ex.rs new file mode 100644 index 000000000..1438fdba7 --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-remap/examples/ex.rs @@ -0,0 +1,4 @@ +fn main() { + foobar::b::foo(); + foobar::c::foo(); +} diff --git a/src/test/run-make/rustdoc-scrape-examples-remap/src/a.rs b/src/test/run-make/rustdoc-scrape-examples-remap/src/a.rs new file mode 100644 index 000000000..b76b4321d --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-remap/src/a.rs @@ -0,0 +1 @@ +pub fn foo() {} diff --git a/src/test/run-make/rustdoc-scrape-examples-remap/src/lib.rs b/src/test/run-make/rustdoc-scrape-examples-remap/src/lib.rs new file mode 100644 index 000000000..f525a4270 --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-remap/src/lib.rs @@ -0,0 +1,8 @@ +// @has foobar/b/fn.foo.html '//*[@class="scraped-example expanded"]' 'ex.rs' +// @has foobar/c/fn.foo.html '//*[@class="scraped-example expanded"]' 'ex.rs' + +#[path = "a.rs"] +pub mod b; + +#[path = "a.rs"] +pub mod c; diff --git a/src/test/run-make/rustdoc-scrape-examples-test/Makefile b/src/test/run-make/rustdoc-scrape-examples-test/Makefile new file mode 100644 index 000000000..9f80a8d96 --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-test/Makefile @@ -0,0 +1,6 @@ +extra_flags := --scrape-tests +deps := ex + +-include ../rustdoc-scrape-examples-multiple/scrape.mk + +all: scrape diff --git a/src/test/run-make/rustdoc-scrape-examples-test/examples/ex.rs b/src/test/run-make/rustdoc-scrape-examples-test/examples/ex.rs new file mode 100644 index 000000000..d1a9a74e7 --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-test/examples/ex.rs @@ -0,0 +1,6 @@ +fn main() {} + +#[test] +fn a_test() { + foobar::ok(); +} diff --git a/src/test/run-make/rustdoc-scrape-examples-test/src/lib.rs b/src/test/run-make/rustdoc-scrape-examples-test/src/lib.rs new file mode 100644 index 000000000..22be1ad41 --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-test/src/lib.rs @@ -0,0 +1,3 @@ +// @has foobar/fn.ok.html '//*[@class="docblock scraped-example-list"]' '' + +pub fn ok() {} diff --git a/src/test/run-make/rustdoc-scrape-examples-whitespace/Makefile b/src/test/run-make/rustdoc-scrape-examples-whitespace/Makefile new file mode 100644 index 000000000..dce8b83ee --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-whitespace/Makefile @@ -0,0 +1,5 @@ +deps := ex + +-include ../rustdoc-scrape-examples-multiple/scrape.mk + +all: scrape diff --git a/src/test/run-make/rustdoc-scrape-examples-whitespace/examples/ex.rs b/src/test/run-make/rustdoc-scrape-examples-whitespace/examples/ex.rs new file mode 100644 index 000000000..44ff689df --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-whitespace/examples/ex.rs @@ -0,0 +1,8 @@ +struct Foo; +impl Foo { + fn bar() { foobar::ok(); } +} + +fn main() { + Foo::bar(); +} diff --git a/src/test/run-make/rustdoc-scrape-examples-whitespace/src/lib.rs b/src/test/run-make/rustdoc-scrape-examples-whitespace/src/lib.rs new file mode 100644 index 000000000..28c34716c --- /dev/null +++ b/src/test/run-make/rustdoc-scrape-examples-whitespace/src/lib.rs @@ -0,0 +1,3 @@ +// @has foobar/fn.ok.html '//*[@class="docblock scraped-example-list"]//code' ' ' + +pub fn ok() {} diff --git a/src/test/run-make/rustdoc-with-out-dir-option/Makefile b/src/test/run-make/rustdoc-with-out-dir-option/Makefile new file mode 100644 index 000000000..f79fce8ee --- /dev/null +++ b/src/test/run-make/rustdoc-with-out-dir-option/Makefile @@ -0,0 +1,8 @@ +-include ../../run-make-fulldeps/tools.mk + +OUTPUT_DIR := "$(TMPDIR)/rustdoc" + +all: + $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --out-dir $(OUTPUT_DIR) + + $(HTMLDOCCK) $(OUTPUT_DIR) src/lib.rs diff --git a/src/test/run-make/rustdoc-with-out-dir-option/src/lib.rs b/src/test/run-make/rustdoc-with-out-dir-option/src/lib.rs new file mode 100644 index 000000000..044bb6acb --- /dev/null +++ b/src/test/run-make/rustdoc-with-out-dir-option/src/lib.rs @@ -0,0 +1,2 @@ +// @has foobar/fn.ok.html +pub fn ok() {} diff --git a/src/test/run-make/rustdoc-with-output-option/Makefile b/src/test/run-make/rustdoc-with-output-option/Makefile new file mode 100644 index 000000000..654f96725 --- /dev/null +++ b/src/test/run-make/rustdoc-with-output-option/Makefile @@ -0,0 +1,8 @@ +-include ../../run-make-fulldeps/tools.mk + +OUTPUT_DIR := "$(TMPDIR)/rustdoc" + +all: + $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --output $(OUTPUT_DIR) + + $(HTMLDOCCK) $(OUTPUT_DIR) src/lib.rs diff --git a/src/test/run-make/rustdoc-with-output-option/src/lib.rs b/src/test/run-make/rustdoc-with-output-option/src/lib.rs new file mode 100644 index 000000000..044bb6acb --- /dev/null +++ b/src/test/run-make/rustdoc-with-output-option/src/lib.rs @@ -0,0 +1,2 @@ +// @has foobar/fn.ok.html +pub fn ok() {} diff --git a/src/test/run-make/rustdoc-with-short-out-dir-option/Makefile b/src/test/run-make/rustdoc-with-short-out-dir-option/Makefile new file mode 100644 index 000000000..1e9ba71de --- /dev/null +++ b/src/test/run-make/rustdoc-with-short-out-dir-option/Makefile @@ -0,0 +1,8 @@ +-include ../../run-make-fulldeps/tools.mk + +OUTPUT_DIR := "$(TMPDIR)/rustdoc" + +all: + $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib -o $(OUTPUT_DIR) + + $(HTMLDOCCK) $(OUTPUT_DIR) src/lib.rs diff --git a/src/test/run-make/rustdoc-with-short-out-dir-option/src/lib.rs b/src/test/run-make/rustdoc-with-short-out-dir-option/src/lib.rs new file mode 100644 index 000000000..044bb6acb --- /dev/null +++ b/src/test/run-make/rustdoc-with-short-out-dir-option/src/lib.rs @@ -0,0 +1,2 @@ +// @has foobar/fn.ok.html +pub fn ok() {} diff --git a/src/test/run-make/static-pie/Makefile b/src/test/run-make/static-pie/Makefile new file mode 100644 index 000000000..945ec1724 --- /dev/null +++ b/src/test/run-make/static-pie/Makefile @@ -0,0 +1,18 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-x86_64 +# only-linux +# ignore-gnux32 + +# How to manually run this +# $ ./x.py test --target x86_64-unknown-linux-[musl,gnu] src/test/run-make/static-pie + +all: test-clang test-gcc + +test-%: + if ./check_$*_version.sh; then\ + ${RUSTC} -Clinker=$* -Clinker-flavor=gcc --target ${TARGET} -C target-feature=+crt-static test-aslr.rs; \ + ! readelf -l $(call RUN_BINFILE,test-aslr) | $(CGREP) INTERP; \ + readelf -l $(call RUN_BINFILE,test-aslr) | $(CGREP) DYNAMIC; \ + $(call RUN,test-aslr) --test-aslr; \ + fi diff --git a/src/test/run-make/static-pie/check_clang_version.sh b/src/test/run-make/static-pie/check_clang_version.sh new file mode 100755 index 000000000..b8e97c3da --- /dev/null +++ b/src/test/run-make/static-pie/check_clang_version.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -euo pipefail + +if command -v clang > /dev/null +then + CLANG_VERSION=$(echo __clang_major__ | clang -E -x c - | grep -v -e '^#' ) + echo "clang version $CLANG_VERSION detected" + if (( $CLANG_VERSION >= 9 )) + then + echo "clang supports -static-pie" + exit 0 + else + echo "clang too old to support -static-pie, skipping test" + exit 1 + fi +else + echo "No clang version detected" + exit 2 +fi diff --git a/src/test/run-make/static-pie/check_gcc_version.sh b/src/test/run-make/static-pie/check_gcc_version.sh new file mode 100755 index 000000000..d07e1d151 --- /dev/null +++ b/src/test/run-make/static-pie/check_gcc_version.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -euo pipefail + +if command -v gcc > /dev/null +then + GCC_VERSION=$(echo __GNUC__ | gcc -E -x c - | grep -v -e '^#' ) + echo "gcc version $GCC_VERSION detected" + if (( $GCC_VERSION >= 8 )) + then + echo "gcc supports -static-pie" + exit 0 + else + echo "gcc too old to support -static-pie, skipping test" + exit 1 + fi +else + echo "No gcc version detected" + exit 2 +fi diff --git a/src/test/run-make/static-pie/test-aslr.rs b/src/test/run-make/static-pie/test-aslr.rs new file mode 100644 index 000000000..96b17af46 --- /dev/null +++ b/src/test/run-make/static-pie/test-aslr.rs @@ -0,0 +1,43 @@ +const NUM_RUNS: usize = 10; + +fn run_self(exe: &str) -> usize { + use std::process::Command; + let mut set = std::collections::HashSet::new(); + + let mut cmd = Command::new(exe); + cmd.arg("--report"); + (0..NUM_RUNS).for_each(|_| { + set.insert(cmd.output().expect("failed to execute process").stdout); + }); + set.len() +} + +fn main() { + let mut args = std::env::args(); + let arg0 = args.next().unwrap(); + match args.next() { + Some(s) if s.eq("--report") => { + println!("main = {:#?}", &main as *const _); + } + Some(s) if s.eq("--test-no-aslr") => { + let cnt = run_self(&arg0); + if cnt != 1 { + eprintln!("FAIL: {} most likely ASLR", arg0); + std::process::exit(1); + } + println!("PASS: {} does no ASLR", arg0); + } + Some(s) if s.eq("--test-aslr") => { + let cnt = run_self(&arg0); + if cnt == 1 { + eprintln!("FAIL: {} most likely no ASLR", arg0); + std::process::exit(1); + } + println!("PASS: {} does ASLR", arg0); + } + Some(_) | None => { + println!("Usage: {} --test-no-aslr | --test-aslr", arg0); + std::process::exit(1); + } + } +} diff --git a/src/test/run-make/thumb-none-cortex-m/Makefile b/src/test/run-make/thumb-none-cortex-m/Makefile new file mode 100644 index 000000000..13385369e --- /dev/null +++ b/src/test/run-make/thumb-none-cortex-m/Makefile @@ -0,0 +1,38 @@ +-include ../../run-make-fulldeps/tools.mk + +# How to run this +# $ ./x.py clean +# $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi src/test/run-make + +# Supported targets: +# - thumbv6m-none-eabi (Bare Cortex-M0, M0+, M1) +# - thumbv7em-none-eabi (Bare Cortex-M4, M7) +# - thumbv7em-none-eabihf (Bare Cortex-M4F, M7F, FPU, hardfloat) +# - thumbv7m-none-eabi (Bare Cortex-M3) + +# only-thumb + +# For cargo setting +RUSTC := $(RUSTC_ORIGINAL) +LD_LIBRARY_PATH := $(HOST_RPATH_DIR) +# We need to be outside of 'src' dir in order to run cargo +WORK_DIR := $(TMPDIR) + +HERE := $(shell pwd) + +CRATE := cortex-m +CRATE_URL := https://github.com/rust-embedded/cortex-m +CRATE_SHA1 := a448e9156e2cb1e556e5441fd65426952ef4b927 # 0.5.0 + +# Don't make lints fatal, but they need to at least warn or they break Cargo's target info parsing. +export RUSTFLAGS := --cap-lints=warn + +all: + env + mkdir -p $(WORK_DIR) + -cd $(WORK_DIR) && rm -rf $(CRATE) + cd $(WORK_DIR) && bash -x $(HERE)/../git_clone_sha1.sh $(CRATE) $(CRATE_URL) $(CRATE_SHA1) + # HACK(eddyb) sets `RUSTC_BOOTSTRAP=1` so Cargo can accept nightly features. + # These come from the top-level Rust workspace, that this crate is not a + # member of, but Cargo tries to load the workspace `Cargo.toml` anyway. + cd $(WORK_DIR) && cd $(CRATE) && env RUSTC_BOOTSTRAP=1 $(BOOTSTRAP_CARGO) build --target $(TARGET) -v diff --git a/src/test/run-make/thumb-none-qemu/Makefile b/src/test/run-make/thumb-none-qemu/Makefile new file mode 100644 index 000000000..ab8b90e15 --- /dev/null +++ b/src/test/run-make/thumb-none-qemu/Makefile @@ -0,0 +1,27 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-thumb + +# How to run this +# $ ./x.py clean +# $ ./x.py test --target thumbv7m-none-eabi src/test/run-make + +# For cargo setting +export RUSTC := $(RUSTC_ORIGINAL) +export LD_LIBRARY_PATH := $(HOST_RPATH_DIR) +# We need to be outside of 'src' dir in order to run cargo +export WORK_DIR := $(TMPDIR) +export HERE := $(shell pwd) + +## clean up unused env variables which might cause harm. +unexport RUSTC_LINKER +unexport RUSTC_BOOTSTRAP +unexport RUST_BUILD_STAGE +unexport RUST_TEST_THREADS +unexport RUST_TEST_TMPDIR +unexport AR +unexport CC +unexport CXX + +all: + bash script.sh diff --git a/src/test/run-make/thumb-none-qemu/example/.cargo/config b/src/test/run-make/thumb-none-qemu/example/.cargo/config new file mode 100644 index 000000000..8b30310e7 --- /dev/null +++ b/src/test/run-make/thumb-none-qemu/example/.cargo/config @@ -0,0 +1,45 @@ +[target.thumbv6m-none-eabi] +# FIXME: Should be Cortex-M0, but Qemu used by CI is too old +runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.thumbv7m-none-eabi] +runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.thumbv7em-none-eabi] +runner = "qemu-system-arm -cpu cortex-m4 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.thumbv7em-none-eabihf] +runner = "qemu-system-arm -cpu cortex-m4 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.thumbv8m.base-none-eabi] +# FIXME: Should be the Cortex-M23, bt Qemu does not currently support it +runner = "qemu-system-arm -cpu cortex-m33 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.thumbv8m.main-none-eabi] +runner = "qemu-system-arm -cpu cortex-m33 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.thumbv8m.main-none-eabihf] +runner = "qemu-system-arm -cpu cortex-m33 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# uncomment ONE of these three option to make `cargo run` start a GDB session +# which option to pick depends on your system +# runner = "arm-none-eabi-gdb -q -x openocd.gdb" +# runner = "gdb-multiarch -q -x openocd.gdb" +# runner = "gdb -q -x openocd.gdb" + +rustflags = [ + # LLD (shipped with the Rust toolchain) is used as the default linker + "-C", "link-arg=-Tlink.x", + + # if you run into problems with LLD switch to the GNU linker by commenting out + # this line + # "-C", "linker=arm-none-eabi-ld", + + # if you need to link to pre-compiled C libraries provided by a C toolchain + # use GCC as the linker by commenting out both lines above and then + # uncommenting the three lines below + # "-C", "linker=arm-none-eabi-gcc", + # "-C", "link-arg=-Wl,-Tlink.x", + # "-C", "link-arg=-nostartfiles", +] diff --git a/src/test/run-make/thumb-none-qemu/example/Cargo.lock b/src/test/run-make/thumb-none-qemu/example/Cargo.lock new file mode 100644 index 000000000..687b962a8 --- /dev/null +++ b/src/test/run-make/thumb-none-qemu/example/Cargo.lock @@ -0,0 +1,199 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aligned" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "as-slice 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "as-slice" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m-rt" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cortex-m-rt-macros 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m-semihosting" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "example" +version = "0.1.0" +dependencies = [ + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "generic-array" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "panic-halt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "r0" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vcell" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "volatile-register" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94" +"checksum as-slice 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "be6b7e95ac49d753f19cab5a825dea99a1149a04e4e3230b33ae16e120954c04" +"checksum bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +"checksum cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" +"checksum cortex-m-rt 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "33a716cd7d8627fae3892c2eede9249e50d2d79aedfb43ca28dad9a2b23876d9" +"checksum cortex-m-rt-macros 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "72b1073338d1e691b3b7aaf6bd61993e589ececce9242a02dfa5453e1b98918d" +"checksum cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "113ef0ecffee2b62b58f9380f4469099b30e9f9cbee2804771b4203ba1762cfa" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" +"checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" +"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" +"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" diff --git a/src/test/run-make/thumb-none-qemu/example/Cargo.toml b/src/test/run-make/thumb-none-qemu/example/Cargo.toml new file mode 100644 index 000000000..63eb5f90a --- /dev/null +++ b/src/test/run-make/thumb-none-qemu/example/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "example" +version = "0.1.0" +edition = "2018" + +[dependencies] +cortex-m = "0.6.2" +cortex-m-rt = "0.6.11" +panic-halt = "0.2.0" +cortex-m-semihosting = "0.3.1" diff --git a/src/test/run-make/thumb-none-qemu/example/memory.x b/src/test/run-make/thumb-none-qemu/example/memory.x new file mode 100644 index 000000000..dc7ad967a --- /dev/null +++ b/src/test/run-make/thumb-none-qemu/example/memory.x @@ -0,0 +1,23 @@ +/* Device specific memory layout */ + +/* This file is used to build the cortex-m-rt examples, + but not other applications using cortex-m-rt. */ + +MEMORY +{ + /* FLASH and RAM are mandatory memory regions */ + /* Update examples/data_overflow.rs if you change these sizes. */ + FLASH : ORIGIN = 0x00000000, LENGTH = 256K + RAM : ORIGIN = 0x20000000, LENGTH = 64K + + /* More memory regions can declared: for example this is a second RAM region */ + /* CCRAM : ORIGIN = 0x10000000, LENGTH = 8K */ +} + +/* The location of the stack can be overridden using the `_stack_start` symbol. + By default it will be placed at the end of the RAM region */ +/* _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM); */ + +/* The location of the .text section can be overridden using the `_stext` symbol. + By default it will place after .vector_table */ +/* _stext = ORIGIN(FLASH) + 0x40c; */
\ No newline at end of file diff --git a/src/test/run-make/thumb-none-qemu/example/src/main.rs b/src/test/run-make/thumb-none-qemu/example/src/main.rs new file mode 100644 index 000000000..2abfde8e7 --- /dev/null +++ b/src/test/run-make/thumb-none-qemu/example/src/main.rs @@ -0,0 +1,24 @@ +#![no_main] +#![no_std] +use core::fmt::Write; +use cortex_m::asm; +use cortex_m_rt::entry; +use cortex_m_semihosting as semihosting; + +use panic_halt as _; + +#[entry] +fn main() -> ! { + let x = 42; + + loop { + asm::nop(); + + // write something through semihosting interface + let mut hstdout = semihosting::hio::hstdout().unwrap(); + let _ = write!(hstdout, "x = {}\n", x); + + // exit from qemu + semihosting::debug::exit(semihosting::debug::EXIT_SUCCESS); + } +} diff --git a/src/test/run-make/thumb-none-qemu/script.sh b/src/test/run-make/thumb-none-qemu/script.sh new file mode 100644 index 000000000..a8aa72af1 --- /dev/null +++ b/src/test/run-make/thumb-none-qemu/script.sh @@ -0,0 +1,20 @@ +#!/bin/sh +set -exuo pipefail + +CRATE=example + +env | sort +mkdir -p $WORK_DIR +pushd $WORK_DIR + rm -rf $CRATE || echo OK + cp -a $HERE/example . + pushd $CRATE + # HACK(eddyb) sets `RUSTC_BOOTSTRAP=1` so Cargo can accept nightly features. + # These come from the top-level Rust workspace, that this crate is not a + # member of, but Cargo tries to load the workspace `Cargo.toml` anyway. + env RUSTC_BOOTSTRAP=1 RUSTFLAGS="-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x" \ + $BOOTSTRAP_CARGO run --target $TARGET | grep "x = 42" + env RUSTC_BOOTSTRAP=1 RUSTFLAGS="-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x" \ + $BOOTSTRAP_CARGO run --target $TARGET --release | grep "x = 42" + popd +popd diff --git a/src/test/run-make/track-path-dep-info/Makefile b/src/test/run-make/track-path-dep-info/Makefile new file mode 100644 index 000000000..465d37447 --- /dev/null +++ b/src/test/run-make/track-path-dep-info/Makefile @@ -0,0 +1,13 @@ +-include ../../run-make-fulldeps/tools.mk + +# FIXME(eddyb) provide `HOST_RUSTC` and `TARGET_RUSTC` +# instead of hardcoding them everywhere they're needed. +ifeq ($(IS_MUSL_HOST),1) +ADDITIONAL_ARGS := $(RUSTFLAGS) +endif + +all: + # Proc macro + $(BARE_RUSTC) $(ADDITIONAL_ARGS) --out-dir $(TMPDIR) macro_def.rs + EXISTING_PROC_MACRO_ENV=1 $(RUSTC) --emit dep-info macro_use.rs + $(CGREP) "emojis.txt:" < $(TMPDIR)/macro_use.d diff --git a/src/test/run-make/track-path-dep-info/emojis.txt b/src/test/run-make/track-path-dep-info/emojis.txt new file mode 100644 index 000000000..e1a728461 --- /dev/null +++ b/src/test/run-make/track-path-dep-info/emojis.txt @@ -0,0 +1 @@ +👾👾👾👾👾👾 diff --git a/src/test/run-make/track-path-dep-info/macro_def.rs b/src/test/run-make/track-path-dep-info/macro_def.rs new file mode 100644 index 000000000..8777ce21f --- /dev/null +++ b/src/test/run-make/track-path-dep-info/macro_def.rs @@ -0,0 +1,11 @@ +#![feature(track_path)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro] +pub fn access_tracked_paths(_: TokenStream) -> TokenStream { + tracked_path::path("emojis.txt"); + TokenStream::new() +} diff --git a/src/test/run-make/track-path-dep-info/macro_use.rs b/src/test/run-make/track-path-dep-info/macro_use.rs new file mode 100644 index 000000000..3c49fd05d --- /dev/null +++ b/src/test/run-make/track-path-dep-info/macro_use.rs @@ -0,0 +1,6 @@ +#[macro_use] +extern crate macro_def; + +access_tracked_paths!(); + +fn main() {} diff --git a/src/test/run-make/translation/Makefile b/src/test/run-make/translation/Makefile new file mode 100644 index 000000000..bfff75e7a --- /dev/null +++ b/src/test/run-make/translation/Makefile @@ -0,0 +1,59 @@ +include ../../run-make-fulldeps/tools.mk + +# This test uses `ln -s` rather than copying to save testing time, but its +# usage doesn't work on Windows. +# ignore-windows + +SYSROOT:=$(shell $(RUSTC) --print sysroot) +FAKEROOT=$(TMPDIR)/fakeroot + +all: normal custom sysroot + +normal: basic-translation.rs + $(RUSTC) $< 2>&1 | grep "struct literal body without path" + +custom: basic-translation.rs basic-translation.ftl + $(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/basic-translation.ftl 2>&1 | grep "this is a test message" + +# Check that a locale can be loaded from the sysroot given a language +# identifier by making a local copy of the sysroot and adding the custom locale +# to it. +sysroot: basic-translation.rs basic-translation.ftl + mkdir $(FAKEROOT) + ln -s $(SYSROOT)/* $(FAKEROOT) + rm -f $(FAKEROOT)/lib + mkdir $(FAKEROOT)/lib + ln -s $(SYSROOT)/lib/* $(FAKEROOT)/lib + rm -f $(FAKEROOT)/lib/rustlib + mkdir $(FAKEROOT)/lib/rustlib + ln -s $(SYSROOT)/lib/rustlib/* $(FAKEROOT)/lib/rustlib + rm -f $(FAKEROOT)/lib/rustlib/src + mkdir $(FAKEROOT)/lib/rustlib/src + ln -s $(SYSROOT)/lib/rustlib/src/* $(FAKEROOT)/lib/rustlib/src + mkdir -p $(FAKEROOT)/share/locale/zh-CN/ + ln -s $(CURDIR)/basic-translation.ftl $(FAKEROOT)/share/locale/zh-CN/basic-translation.ftl + $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 | grep "this is a test message" + +# Check that the compiler errors out when the sysroot requested cannot be +# found. This test might start failing if there actually exists a Klingon +# translation of rustc's error messages. +sysroot-missing: + $(RUSTC) $< -Ztranslate-lang=tlh 2>&1 || grep "missing locale directory" + +# Check that the compiler errors out when the sysroot requested cannot be +# found. This test might start failing if there actually exists a Klingon +# translation of rustc's error messages. +sysroot-invalid: basic-translation.rs basic-translation.ftl + mkdir $(FAKEROOT) + ln -s $(SYSROOT)/* $(FAKEROOT) + rm -f $(FAKEROOT)/lib + mkdir $(FAKEROOT)/lib + ln -s $(SYSROOT)/lib/* $(FAKEROOT)/lib + rm -f $(FAKEROOT)/lib/rustlib + mkdir $(FAKEROOT)/lib/rustlib + ln -s $(SYSROOT)/lib/rustlib/* $(FAKEROOT)/lib/rustlib + rm -f $(FAKEROOT)/lib/rustlib/src + mkdir $(FAKEROOT)/lib/rustlib/src + ln -s $(SYSROOT)/lib/rustlib/src/* $(FAKEROOT)/lib/rustlib/src + touch $(FAKEROOT)/share/locale/zh-CN/ + $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 || grep "`\$sysroot/share/locales/\$locale` is not a directory" diff --git a/src/test/run-make/translation/basic-translation.ftl b/src/test/run-make/translation/basic-translation.ftl new file mode 100644 index 000000000..4681b879c --- /dev/null +++ b/src/test/run-make/translation/basic-translation.ftl @@ -0,0 +1,2 @@ +parser-struct-literal-body-without-path = this is a test message + .suggestion = this is a test suggestion diff --git a/src/test/run-make/translation/basic-translation.rs b/src/test/run-make/translation/basic-translation.rs new file mode 100644 index 000000000..b8f5bff31 --- /dev/null +++ b/src/test/run-make/translation/basic-translation.rs @@ -0,0 +1,18 @@ +// Exact error being tested isn't relevant, it just needs to be known that it uses Fluent-backed +// diagnostics. + +struct Foo { + val: (), +} + +fn foo() -> Foo { + val: (), +} + +fn main() { + let x = foo(); + x.val == 42; + let x = { + val: (), + }; +} diff --git a/src/test/run-make/unstable-flag-required/Makefile b/src/test/run-make/unstable-flag-required/Makefile new file mode 100644 index 000000000..b8769d5f6 --- /dev/null +++ b/src/test/run-make/unstable-flag-required/Makefile @@ -0,0 +1,4 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTDOC) --output-format=json x.html 2>&1 | diff - output-format-json.stderr diff --git a/src/test/run-make/unstable-flag-required/README.md b/src/test/run-make/unstable-flag-required/README.md new file mode 100644 index 000000000..e5251fdf9 --- /dev/null +++ b/src/test/run-make/unstable-flag-required/README.md @@ -0,0 +1,3 @@ +This is a collection of tests that verify `--unstable-options` is required. +It should eventually be removed in favor of UI tests once compiletest stops +passing --unstable-options by default (#82639). diff --git a/src/test/run-make/unstable-flag-required/output-format-json.stderr b/src/test/run-make/unstable-flag-required/output-format-json.stderr new file mode 100644 index 000000000..fb4079beb --- /dev/null +++ b/src/test/run-make/unstable-flag-required/output-format-json.stderr @@ -0,0 +1,2 @@ +error: the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578) + diff --git a/src/test/run-make/unstable-flag-required/x.rs b/src/test/run-make/unstable-flag-required/x.rs new file mode 100644 index 000000000..5df757613 --- /dev/null +++ b/src/test/run-make/unstable-flag-required/x.rs @@ -0,0 +1 @@ +// nothing to see here diff --git a/src/test/run-make/wasm-abi/Makefile b/src/test/run-make/wasm-abi/Makefile new file mode 100644 index 000000000..61fc4e8f5 --- /dev/null +++ b/src/test/run-make/wasm-abi/Makefile @@ -0,0 +1,7 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-wasm32-bare + +all: + $(RUSTC) foo.rs --target wasm32-unknown-unknown + $(NODE) foo.js $(TMPDIR)/foo.wasm diff --git a/src/test/run-make/wasm-abi/foo.js b/src/test/run-make/wasm-abi/foo.js new file mode 100644 index 000000000..9e9a65401 --- /dev/null +++ b/src/test/run-make/wasm-abi/foo.js @@ -0,0 +1,22 @@ +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +const m = new WebAssembly.Module(buffer); +const i = new WebAssembly.Instance(m, { + host: { + two_i32: () => [100, 101], + two_i64: () => [102n, 103n], + two_f32: () => [104, 105], + two_f64: () => [106, 107], + mishmash: () => [108, 109, 110, 111n, 112, 113], + } +}); + +assert.deepEqual(i.exports.return_two_i32(), [1, 2]) +assert.deepEqual(i.exports.return_two_i64(), [3, 4]) +assert.deepEqual(i.exports.return_two_f32(), [5, 6]) +assert.deepEqual(i.exports.return_two_f64(), [7, 8]) +assert.deepEqual(i.exports.return_mishmash(), [9, 10, 11, 12, 13, 14]) +i.exports.call_imports(); diff --git a/src/test/run-make/wasm-abi/foo.rs b/src/test/run-make/wasm-abi/foo.rs new file mode 100644 index 000000000..0678eb3ff --- /dev/null +++ b/src/test/run-make/wasm-abi/foo.rs @@ -0,0 +1,87 @@ +#![crate_type = "cdylib"] +#![deny(warnings)] +#![feature(wasm_abi)] + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub struct TwoI32 { + pub a: i32, + pub b: i32, +} + +#[no_mangle] +pub extern "wasm" fn return_two_i32() -> TwoI32 { + TwoI32 { a: 1, b: 2 } +} + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub struct TwoI64 { + pub a: i64, + pub b: i64, +} + +#[no_mangle] +pub extern "wasm" fn return_two_i64() -> TwoI64 { + TwoI64 { a: 3, b: 4 } +} + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub struct TwoF32 { + pub a: f32, + pub b: f32, +} + +#[no_mangle] +pub extern "wasm" fn return_two_f32() -> TwoF32 { + TwoF32 { a: 5., b: 6. } +} + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub struct TwoF64 { + pub a: f64, + pub b: f64, +} + +#[no_mangle] +pub extern "wasm" fn return_two_f64() -> TwoF64 { + TwoF64 { a: 7., b: 8. } +} + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub struct Mishmash { + pub a: f64, + pub b: f32, + pub c: i32, + pub d: i64, + pub e: TwoI32, +} + +#[no_mangle] +pub extern "wasm" fn return_mishmash() -> Mishmash { + Mishmash { a: 9., b: 10., c: 11, d: 12, e: TwoI32 { a: 13, b: 14 } } +} + +#[link(wasm_import_module = "host")] +extern "wasm" { + fn two_i32() -> TwoI32; + fn two_i64() -> TwoI64; + fn two_f32() -> TwoF32; + fn two_f64() -> TwoF64; + fn mishmash() -> Mishmash; +} + +#[no_mangle] +pub unsafe extern "C" fn call_imports() { + assert_eq!(two_i32(), TwoI32 { a: 100, b: 101 }); + assert_eq!(two_i64(), TwoI64 { a: 102, b: 103 }); + assert_eq!(two_f32(), TwoF32 { a: 104., b: 105. }); + assert_eq!(two_f64(), TwoF64 { a: 106., b: 107. }); + assert_eq!( + mishmash(), + Mishmash { a: 108., b: 109., c: 110, d: 111, e: TwoI32 { a: 112, b: 113 } } + ); +} diff --git a/src/test/run-make/wasm-custom-section/Makefile b/src/test/run-make/wasm-custom-section/Makefile new file mode 100644 index 000000000..2f48b8525 --- /dev/null +++ b/src/test/run-make/wasm-custom-section/Makefile @@ -0,0 +1,8 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-wasm32-bare + +all: + $(RUSTC) foo.rs --target wasm32-unknown-unknown + $(RUSTC) bar.rs -C lto -O --target wasm32-unknown-unknown + $(NODE) foo.js $(TMPDIR)/bar.wasm diff --git a/src/test/run-make/wasm-custom-section/bar.rs b/src/test/run-make/wasm-custom-section/bar.rs new file mode 100644 index 000000000..c95f3e143 --- /dev/null +++ b/src/test/run-make/wasm-custom-section/bar.rs @@ -0,0 +1,13 @@ +#![crate_type = "cdylib"] +#![deny(warnings)] + +extern crate foo; + +#[link_section = "foo"] +pub static A: [u8; 2] = [5, 6]; + +#[link_section = "baz"] +pub static B: [u8; 2] = [7, 8]; + +#[no_mangle] +pub extern fn foo() {} diff --git a/src/test/run-make/wasm-custom-section/foo.js b/src/test/run-make/wasm-custom-section/foo.js new file mode 100644 index 000000000..57a0f5073 --- /dev/null +++ b/src/test/run-make/wasm-custom-section/foo.js @@ -0,0 +1,36 @@ +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +let m = new WebAssembly.Module(buffer); +let sections = WebAssembly.Module.customSections(m, "baz"); +console.log('section baz', sections); +assert.strictEqual(sections.length, 1); +let section = new Uint8Array(sections[0]); +console.log('contents', section); +assert.strictEqual(section.length, 2); +assert.strictEqual(section[0], 7); +assert.strictEqual(section[1], 8); + +sections = WebAssembly.Module.customSections(m, "bar"); +console.log('section bar', sections); +assert.strictEqual(sections.length, 1, "didn't pick up `bar` section from dependency"); +section = new Uint8Array(sections[0]); +console.log('contents', section); +assert.strictEqual(section.length, 2); +assert.strictEqual(section[0], 3); +assert.strictEqual(section[1], 4); + +sections = WebAssembly.Module.customSections(m, "foo"); +console.log('section foo', sections); +assert.strictEqual(sections.length, 1, "didn't create `foo` section"); +section = new Uint8Array(sections[0]); +console.log('contents', section); +assert.strictEqual(section.length, 4, "didn't concatenate `foo` sections"); +assert.strictEqual(section[0], 5); +assert.strictEqual(section[1], 6); +assert.strictEqual(section[2], 1); +assert.strictEqual(section[3], 2); + +process.exit(0); diff --git a/src/test/run-make/wasm-custom-section/foo.rs b/src/test/run-make/wasm-custom-section/foo.rs new file mode 100644 index 000000000..61f81f024 --- /dev/null +++ b/src/test/run-make/wasm-custom-section/foo.rs @@ -0,0 +1,8 @@ +#![crate_type = "rlib"] +#![deny(warnings)] + +#[link_section = "foo"] +pub static A: [u8; 2] = [1, 2]; + +#[link_section = "bar"] +pub static B: [u8; 2] = [3, 4]; diff --git a/src/test/run-make/wasm-custom-sections-opt/Makefile b/src/test/run-make/wasm-custom-sections-opt/Makefile new file mode 100644 index 000000000..76698c0aa --- /dev/null +++ b/src/test/run-make/wasm-custom-sections-opt/Makefile @@ -0,0 +1,7 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-wasm32-bare + +all: + $(RUSTC) foo.rs -O --target wasm32-unknown-unknown + $(NODE) foo.js $(TMPDIR)/foo.wasm diff --git a/src/test/run-make/wasm-custom-sections-opt/foo.js b/src/test/run-make/wasm-custom-sections-opt/foo.js new file mode 100644 index 000000000..9663f377e --- /dev/null +++ b/src/test/run-make/wasm-custom-sections-opt/foo.js @@ -0,0 +1,15 @@ +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +let m = new WebAssembly.Module(buffer); + +sections = WebAssembly.Module.customSections(m, "foo"); +console.log('section foo', sections); +assert.strictEqual(sections.length, 1, "didn't create `foo` section"); +section = new Uint8Array(sections[0]); +console.log('contents', section); +assert.strictEqual(section.length, 4, "didn't concatenate `foo` sections"); + +process.exit(0); diff --git a/src/test/run-make/wasm-custom-sections-opt/foo.rs b/src/test/run-make/wasm-custom-sections-opt/foo.rs new file mode 100644 index 000000000..9af7728b7 --- /dev/null +++ b/src/test/run-make/wasm-custom-sections-opt/foo.rs @@ -0,0 +1,21 @@ +#![crate_type = "cdylib"] +#![deny(warnings)] + +#[link_section = "foo"] +pub static A: [u8; 2] = [1, 2]; + +// make sure this is in another CGU +pub mod another { + #[link_section = "foo"] + pub static FOO: [u8; 2] = [3, 4]; + + pub fn foo() {} +} + +#[no_mangle] +pub extern fn foo() { + // This will import `another::foo` through ThinLTO passes, and it better not + // also accidentally import the `FOO` custom section into this module as + // well + another::foo(); +} diff --git a/src/test/run-make/wasm-export-all-symbols/Makefile b/src/test/run-make/wasm-export-all-symbols/Makefile new file mode 100644 index 000000000..7e47ba485 --- /dev/null +++ b/src/test/run-make/wasm-export-all-symbols/Makefile @@ -0,0 +1,19 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-wasm32-bare + +all: + $(RUSTC) bar.rs --target wasm32-unknown-unknown + $(RUSTC) foo.rs --target wasm32-unknown-unknown + $(NODE) verify.js $(TMPDIR)/foo.wasm + $(RUSTC) main.rs --target wasm32-unknown-unknown + $(NODE) verify.js $(TMPDIR)/main.wasm + $(RUSTC) bar.rs --target wasm32-unknown-unknown -O + $(RUSTC) foo.rs --target wasm32-unknown-unknown -O + $(NODE) verify.js $(TMPDIR)/foo.wasm + $(RUSTC) main.rs --target wasm32-unknown-unknown -O + $(NODE) verify.js $(TMPDIR)/main.wasm + $(RUSTC) foo.rs --target wasm32-unknown-unknown -C lto + $(NODE) verify.js $(TMPDIR)/foo.wasm + $(RUSTC) main.rs --target wasm32-unknown-unknown -C lto + $(NODE) verify.js $(TMPDIR)/main.wasm diff --git a/src/test/run-make/wasm-export-all-symbols/bar.rs b/src/test/run-make/wasm-export-all-symbols/bar.rs new file mode 100644 index 000000000..ac9c20a57 --- /dev/null +++ b/src/test/run-make/wasm-export-all-symbols/bar.rs @@ -0,0 +1,7 @@ +#![crate_type = "rlib"] + +#[no_mangle] +pub extern fn foo() {} + +#[no_mangle] +pub static FOO: u64 = 42; diff --git a/src/test/run-make/wasm-export-all-symbols/foo.rs b/src/test/run-make/wasm-export-all-symbols/foo.rs new file mode 100644 index 000000000..4811b24bc --- /dev/null +++ b/src/test/run-make/wasm-export-all-symbols/foo.rs @@ -0,0 +1,3 @@ +#![crate_type = "cdylib"] + +extern crate bar; diff --git a/src/test/run-make/wasm-export-all-symbols/main.rs b/src/test/run-make/wasm-export-all-symbols/main.rs new file mode 100644 index 000000000..0edda7d7b --- /dev/null +++ b/src/test/run-make/wasm-export-all-symbols/main.rs @@ -0,0 +1,3 @@ +extern crate bar; + +fn main() {} diff --git a/src/test/run-make/wasm-export-all-symbols/verify.js b/src/test/run-make/wasm-export-all-symbols/verify.js new file mode 100644 index 000000000..72db3356f --- /dev/null +++ b/src/test/run-make/wasm-export-all-symbols/verify.js @@ -0,0 +1,32 @@ +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +let m = new WebAssembly.Module(buffer); +let list = WebAssembly.Module.exports(m); +console.log('exports', list); + +const my_exports = {}; +let nexports = 0; + +for (const entry of list) { + if (entry.kind == 'function'){ + nexports += 1; + } + my_exports[entry.name] = entry.kind; +} + +if (my_exports.foo != "function") + throw new Error("`foo` wasn't defined"); + +if (my_exports.FOO != "global") + throw new Error("`FOO` wasn't defined"); + +if (my_exports.main === undefined) { + if (nexports != 1) + throw new Error("should only have one function export"); +} else { + if (nexports != 2) + throw new Error("should only have two function exports"); +} diff --git a/src/test/run-make/wasm-import-module/Makefile b/src/test/run-make/wasm-import-module/Makefile new file mode 100644 index 000000000..fe63e66f2 --- /dev/null +++ b/src/test/run-make/wasm-import-module/Makefile @@ -0,0 +1,8 @@ +-include ../../run-make-fulldeps/tools.mk + + # only-wasm32-bare + +all: + $(RUSTC) foo.rs --target wasm32-unknown-unknown + $(RUSTC) bar.rs -C lto -O --target wasm32-unknown-unknown + $(NODE) foo.js $(TMPDIR)/bar.wasm diff --git a/src/test/run-make/wasm-import-module/bar.rs b/src/test/run-make/wasm-import-module/bar.rs new file mode 100644 index 000000000..1b988c783 --- /dev/null +++ b/src/test/run-make/wasm-import-module/bar.rs @@ -0,0 +1,18 @@ +#![crate_type = "cdylib"] +#![deny(warnings)] + +extern crate foo; + +#[link(wasm_import_module = "./me")] +extern "C" { + #[link_name = "me_in_dep"] + fn dep(); +} + +#[no_mangle] +pub extern "C" fn foo() { + unsafe { + foo::dep(); + dep(); + } +} diff --git a/src/test/run-make/wasm-import-module/foo.js b/src/test/run-make/wasm-import-module/foo.js new file mode 100644 index 000000000..3ea47fcc9 --- /dev/null +++ b/src/test/run-make/wasm-import-module/foo.js @@ -0,0 +1,18 @@ +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +let m = new WebAssembly.Module(buffer); +let imports = WebAssembly.Module.imports(m); +console.log('imports', imports); +assert.strictEqual(imports.length, 2); + +assert.strictEqual(imports[0].kind, 'function'); +assert.strictEqual(imports[1].kind, 'function'); + +let modules = [imports[0].module, imports[1].module]; +modules.sort(); + +assert.strictEqual(modules[0], './dep'); +assert.strictEqual(modules[1], './me'); diff --git a/src/test/run-make/wasm-import-module/foo.rs b/src/test/run-make/wasm-import-module/foo.rs new file mode 100644 index 000000000..bbeaf99bc --- /dev/null +++ b/src/test/run-make/wasm-import-module/foo.rs @@ -0,0 +1,7 @@ +#![crate_type = "rlib"] +#![deny(warnings)] + +#[link(wasm_import_module = "./dep")] +extern "C" { + pub fn dep(); +} diff --git a/src/test/run-make/wasm-panic-small/Makefile b/src/test/run-make/wasm-panic-small/Makefile new file mode 100644 index 000000000..68397e4bc --- /dev/null +++ b/src/test/run-make/wasm-panic-small/Makefile @@ -0,0 +1,17 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-wasm32-bare + +all: + $(RUSTC) foo.rs -C lto -O --target wasm32-unknown-unknown --cfg a + wc -c < $(TMPDIR)/foo.wasm + [ "`wc -c < $(TMPDIR)/foo.wasm`" -lt "1024" ] + $(RUSTC) foo.rs -C lto -O --target wasm32-unknown-unknown --cfg b + wc -c < $(TMPDIR)/foo.wasm + [ "`wc -c < $(TMPDIR)/foo.wasm`" -lt "5120" ] + $(RUSTC) foo.rs -C lto -O --target wasm32-unknown-unknown --cfg c + wc -c < $(TMPDIR)/foo.wasm + [ "`wc -c < $(TMPDIR)/foo.wasm`" -lt "5120" ] + $(RUSTC) foo.rs -C lto -O --target wasm32-unknown-unknown --cfg d + wc -c < $(TMPDIR)/foo.wasm + [ "`wc -c < $(TMPDIR)/foo.wasm`" -lt "5120" ] diff --git a/src/test/run-make/wasm-panic-small/foo.rs b/src/test/run-make/wasm-panic-small/foo.rs new file mode 100644 index 000000000..6df52affe --- /dev/null +++ b/src/test/run-make/wasm-panic-small/foo.rs @@ -0,0 +1,27 @@ +#![crate_type = "cdylib"] + +#[no_mangle] +#[cfg(a)] +pub fn foo() { + panic!("test"); +} + +#[no_mangle] +#[cfg(b)] +pub fn foo() { + panic!("{}", 1); +} + +#[no_mangle] +#[cfg(c)] +pub fn foo() { + panic!("{}", "a"); +} + +#[no_mangle] +#[cfg(d)] +pub fn foo() -> usize { + use std::cell::Cell; + thread_local!(static A: Cell<Vec<u32>> = Cell::new(Vec::new())); + A.try_with(|x| x.take().len()).unwrap_or(0) +} diff --git a/src/test/run-make/wasm-spurious-import/Makefile b/src/test/run-make/wasm-spurious-import/Makefile new file mode 100644 index 000000000..1bb59dc1b --- /dev/null +++ b/src/test/run-make/wasm-spurious-import/Makefile @@ -0,0 +1,7 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-wasm32-bare + +all: + $(RUSTC) main.rs -C overflow-checks=yes -C panic=abort -C lto -C opt-level=z --target wasm32-unknown-unknown + $(NODE) verify.js $(TMPDIR)/main.wasm diff --git a/src/test/run-make/wasm-spurious-import/main.rs b/src/test/run-make/wasm-spurious-import/main.rs new file mode 100644 index 000000000..fcbead5e2 --- /dev/null +++ b/src/test/run-make/wasm-spurious-import/main.rs @@ -0,0 +1,14 @@ +#![crate_type = "cdylib"] +#![no_std] + +#[panic_handler] +fn my_panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[no_mangle] +pub fn multer(a: i128, b: i128) -> i128 { + // Trigger usage of the __multi3 compiler intrinsic which then leads to an imported + // panic function in case of a bug. We verify that no imports exist in our verifier. + a * b +} diff --git a/src/test/run-make/wasm-spurious-import/verify.js b/src/test/run-make/wasm-spurious-import/verify.js new file mode 100644 index 000000000..d3b2101b6 --- /dev/null +++ b/src/test/run-make/wasm-spurious-import/verify.js @@ -0,0 +1,9 @@ +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +let m = new WebAssembly.Module(buffer); +let imports = WebAssembly.Module.imports(m); +console.log('imports', imports); +assert.strictEqual(imports.length, 0); diff --git a/src/test/run-make/wasm-stringify-ints-small/Makefile b/src/test/run-make/wasm-stringify-ints-small/Makefile new file mode 100644 index 000000000..01e1c6b0c --- /dev/null +++ b/src/test/run-make/wasm-stringify-ints-small/Makefile @@ -0,0 +1,10 @@ +-include ../../run-make-fulldeps/tools.mk + +ifeq ($(TARGET),wasm32-unknown-unknown) +all: + $(RUSTC) foo.rs -C lto -O --target wasm32-unknown-unknown + wc -c < $(TMPDIR)/foo.wasm + [ "`wc -c < $(TMPDIR)/foo.wasm`" -lt "25000" ] +else +all: +endif diff --git a/src/test/run-make/wasm-stringify-ints-small/foo.rs b/src/test/run-make/wasm-stringify-ints-small/foo.rs new file mode 100644 index 000000000..7a947f013 --- /dev/null +++ b/src/test/run-make/wasm-stringify-ints-small/foo.rs @@ -0,0 +1,33 @@ +#![crate_type = "cdylib"] + +extern "C" { + fn observe(ptr: *const u8, len: usize); +} + +macro_rules! s { + ( $( $f:ident -> $t:ty );* $(;)* ) => { + $( + extern "C" { + fn $f() -> $t; + } + let s = $f().to_string(); + observe(s.as_ptr(), s.len()); + )* + }; +} + +#[no_mangle] +pub unsafe extern "C" fn foo() { + s! { + get_u8 -> u8; + get_i8 -> i8; + get_u16 -> u16; + get_i16 -> i16; + get_u32 -> u32; + get_i32 -> i32; + get_u64 -> u64; + get_i64 -> i64; + get_usize -> usize; + get_isize -> isize; + } +} diff --git a/src/test/run-make/wasm-symbols-different-module/Makefile b/src/test/run-make/wasm-symbols-different-module/Makefile new file mode 100644 index 000000000..bb6a5d3c9 --- /dev/null +++ b/src/test/run-make/wasm-symbols-different-module/Makefile @@ -0,0 +1,28 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-wasm32-bare + +all: + $(RUSTC) foo.rs --target wasm32-unknown-unknown + $(NODE) verify-imports.js $(TMPDIR)/foo.wasm a/foo b/foo + $(RUSTC) foo.rs --target wasm32-unknown-unknown -C lto + $(NODE) verify-imports.js $(TMPDIR)/foo.wasm a/foo b/foo + $(RUSTC) foo.rs --target wasm32-unknown-unknown -O + $(NODE) verify-imports.js $(TMPDIR)/foo.wasm a/foo b/foo + $(RUSTC) foo.rs --target wasm32-unknown-unknown -O -C lto + $(NODE) verify-imports.js $(TMPDIR)/foo.wasm a/foo b/foo + + $(RUSTC) bar.rs --target wasm32-unknown-unknown + $(NODE) verify-imports.js $(TMPDIR)/bar.wasm m1/f m1/g m2/f + $(RUSTC) bar.rs --target wasm32-unknown-unknown -C lto + $(NODE) verify-imports.js $(TMPDIR)/bar.wasm m1/f m1/g m2/f + $(RUSTC) bar.rs --target wasm32-unknown-unknown -O + $(NODE) verify-imports.js $(TMPDIR)/bar.wasm m1/f m1/g m2/f + $(RUSTC) bar.rs --target wasm32-unknown-unknown -O -C lto + $(NODE) verify-imports.js $(TMPDIR)/bar.wasm m1/f m1/g m2/f + + $(RUSTC) baz.rs --target wasm32-unknown-unknown + $(NODE) verify-imports.js $(TMPDIR)/baz.wasm sqlite/allocate sqlite/deallocate + + $(RUSTC) log.rs --target wasm32-unknown-unknown + $(NODE) verify-imports.js $(TMPDIR)/log.wasm test/log diff --git a/src/test/run-make/wasm-symbols-different-module/bar.rs b/src/test/run-make/wasm-symbols-different-module/bar.rs new file mode 100644 index 000000000..7567060d7 --- /dev/null +++ b/src/test/run-make/wasm-symbols-different-module/bar.rs @@ -0,0 +1,33 @@ +//! Issue #50021 + +#![crate_type = "cdylib"] + +mod m1 { + #[link(wasm_import_module = "m1")] + extern "C" { + pub fn f(); + } + #[link(wasm_import_module = "m1")] + extern "C" { + pub fn g(); + } +} + +mod m2 { + #[link(wasm_import_module = "m2")] + extern "C" { + pub fn f(_: i32); + } +} + +#[no_mangle] +pub unsafe fn run() { + m1::f(); + m1::g(); + + // In generated code, expected: + // (import "m2" "f" (func $f (param i32))) + // but got: + // (import "m1" "f" (func $f (param i32))) + m2::f(0); +} diff --git a/src/test/run-make/wasm-symbols-different-module/baz.rs b/src/test/run-make/wasm-symbols-different-module/baz.rs new file mode 100644 index 000000000..fbb78619b --- /dev/null +++ b/src/test/run-make/wasm-symbols-different-module/baz.rs @@ -0,0 +1,22 @@ +//! Issue #63562 + +#![crate_type = "cdylib"] + +mod foo { + #[link(wasm_import_module = "sqlite")] + extern "C" { + pub fn allocate(size: usize) -> i32; + pub fn deallocate(ptr: i32, size: usize); + } +} + +#[no_mangle] +pub extern "C" fn allocate() { + unsafe { + foo::allocate(1); + foo::deallocate(1, 2); + } +} + +#[no_mangle] +pub extern "C" fn deallocate() {} diff --git a/src/test/run-make/wasm-symbols-different-module/foo.rs b/src/test/run-make/wasm-symbols-different-module/foo.rs new file mode 100644 index 000000000..a4ba7e714 --- /dev/null +++ b/src/test/run-make/wasm-symbols-different-module/foo.rs @@ -0,0 +1,23 @@ +#![crate_type = "cdylib"] + +mod a { + #[link(wasm_import_module = "a")] + extern "C" { + pub fn foo(); + } +} + +mod b { + #[link(wasm_import_module = "b")] + extern "C" { + pub fn foo(); + } +} + +#[no_mangle] +pub fn start() { + unsafe { + a::foo(); + b::foo(); + } +} diff --git a/src/test/run-make/wasm-symbols-different-module/log.rs b/src/test/run-make/wasm-symbols-different-module/log.rs new file mode 100644 index 000000000..ea3e0b4b2 --- /dev/null +++ b/src/test/run-make/wasm-symbols-different-module/log.rs @@ -0,0 +1,16 @@ +//! Issue #56309 + +#![crate_type = "cdylib"] + +#[link(wasm_import_module = "test")] +extern "C" { + fn log(message_data: u32, message_size: u32); +} + +#[no_mangle] +pub fn main() { + let message = "Hello, world!"; + unsafe { + log(message.as_ptr() as u32, message.len() as u32); + } +} diff --git a/src/test/run-make/wasm-symbols-different-module/verify-imports.js b/src/test/run-make/wasm-symbols-different-module/verify-imports.js new file mode 100644 index 000000000..7e9f90cf8 --- /dev/null +++ b/src/test/run-make/wasm-symbols-different-module/verify-imports.js @@ -0,0 +1,32 @@ +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +let m = new WebAssembly.Module(buffer); +let list = WebAssembly.Module.imports(m); +console.log('imports', list); +if (list.length !== process.argv.length - 3) + throw new Error("wrong number of imports") + +const imports = new Map(); +for (let i = 3; i < process.argv.length; i++) { + const [module, name] = process.argv[i].split('/'); + if (!imports.has(module)) + imports.set(module, new Map()); + imports.get(module).set(name, true); +} + +for (let i of list) { + if (imports.get(i.module) === undefined || imports.get(i.module).get(i.name) === undefined) + throw new Error(`didn't find import of ${i.module}::${i.name}`); + imports.get(i.module).delete(i.name); + + if (imports.get(i.module).size === 0) + imports.delete(i.module); +} + +console.log(imports); +if (imports.size !== 0) { + throw new Error('extra imports'); +} diff --git a/src/test/run-make/wasm-symbols-not-exported/Makefile b/src/test/run-make/wasm-symbols-not-exported/Makefile new file mode 100644 index 000000000..62bd0f087 --- /dev/null +++ b/src/test/run-make/wasm-symbols-not-exported/Makefile @@ -0,0 +1,13 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-wasm32-bare + +all: + $(RUSTC) foo.rs --target wasm32-unknown-unknown + $(NODE) verify-exported-symbols.js $(TMPDIR)/foo.wasm + $(RUSTC) foo.rs --target wasm32-unknown-unknown -O + $(NODE) verify-exported-symbols.js $(TMPDIR)/foo.wasm + $(RUSTC) bar.rs --target wasm32-unknown-unknown + $(NODE) verify-exported-symbols.js $(TMPDIR)/bar.wasm + $(RUSTC) bar.rs --target wasm32-unknown-unknown -O + $(NODE) verify-exported-symbols.js $(TMPDIR)/bar.wasm diff --git a/src/test/run-make/wasm-symbols-not-exported/bar.rs b/src/test/run-make/wasm-symbols-not-exported/bar.rs new file mode 100644 index 000000000..6ffbd3ec6 --- /dev/null +++ b/src/test/run-make/wasm-symbols-not-exported/bar.rs @@ -0,0 +1,35 @@ +#![feature(panic_handler, alloc_error_handler)] +#![crate_type = "cdylib"] +#![no_std] + +use core::alloc::*; + +struct B; + +unsafe impl GlobalAlloc for B { + unsafe fn alloc(&self, x: Layout) -> *mut u8 { + 1 as *mut u8 + } + + unsafe fn dealloc(&self, ptr: *mut u8, x: Layout) { + } +} + +#[global_allocator] +static A: B = B; + +#[no_mangle] +pub extern fn foo(a: u32) -> u32 { + assert_eq!(a, 3); + a * 2 +} + +#[alloc_error_handler] +fn a(_: core::alloc::Layout) -> ! { + loop {} +} + +#[panic_handler] +fn b(_: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/src/test/run-make/wasm-symbols-not-exported/foo.rs b/src/test/run-make/wasm-symbols-not-exported/foo.rs new file mode 100644 index 000000000..d46baee01 --- /dev/null +++ b/src/test/run-make/wasm-symbols-not-exported/foo.rs @@ -0,0 +1,7 @@ +#![crate_type = "cdylib"] + +#[no_mangle] +pub extern fn foo() { + println!("foo"); + panic!("test"); +} diff --git a/src/test/run-make/wasm-symbols-not-exported/verify-exported-symbols.js b/src/test/run-make/wasm-symbols-not-exported/verify-exported-symbols.js new file mode 100644 index 000000000..afc8a7241 --- /dev/null +++ b/src/test/run-make/wasm-symbols-not-exported/verify-exported-symbols.js @@ -0,0 +1,21 @@ +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +let m = new WebAssembly.Module(buffer); +let list = WebAssembly.Module.exports(m); +console.log('exports', list); + +let bad = false; +for (let i = 0; i < list.length; i++) { + const e = list[i]; + if (e.name == "foo" || e.kind != "function") + continue; + + console.log('unexpected exported symbol:', e.name); + bad = true; +} + +if (bad) + process.exit(1); diff --git a/src/test/run-make/wasm-symbols-not-imported/Makefile b/src/test/run-make/wasm-symbols-not-imported/Makefile new file mode 100644 index 000000000..7a923375c --- /dev/null +++ b/src/test/run-make/wasm-symbols-not-imported/Makefile @@ -0,0 +1,13 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-wasm32-bare + +all: + $(RUSTC) foo.rs --target wasm32-unknown-unknown + $(NODE) verify-no-imports.js $(TMPDIR)/foo.wasm + $(RUSTC) foo.rs --target wasm32-unknown-unknown -C lto + $(NODE) verify-no-imports.js $(TMPDIR)/foo.wasm + $(RUSTC) foo.rs --target wasm32-unknown-unknown -O + $(NODE) verify-no-imports.js $(TMPDIR)/foo.wasm + $(RUSTC) foo.rs --target wasm32-unknown-unknown -O -C lto + $(NODE) verify-no-imports.js $(TMPDIR)/foo.wasm diff --git a/src/test/run-make/wasm-symbols-not-imported/foo.rs b/src/test/run-make/wasm-symbols-not-imported/foo.rs new file mode 100644 index 000000000..b25bdc980 --- /dev/null +++ b/src/test/run-make/wasm-symbols-not-imported/foo.rs @@ -0,0 +1,14 @@ +#![crate_type = "cdylib"] +#![no_std] + +use core::panic::PanicInfo; + +#[no_mangle] +pub extern fn foo() { + panic!() +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} diff --git a/src/test/run-make/wasm-symbols-not-imported/verify-no-imports.js b/src/test/run-make/wasm-symbols-not-imported/verify-no-imports.js new file mode 100644 index 000000000..90e3df1d9 --- /dev/null +++ b/src/test/run-make/wasm-symbols-not-imported/verify-no-imports.js @@ -0,0 +1,10 @@ +const fs = require('fs'); +const process = require('process'); +const assert = require('assert'); +const buffer = fs.readFileSync(process.argv[2]); + +let m = new WebAssembly.Module(buffer); +let list = WebAssembly.Module.imports(m); +console.log('imports', list); +if (list.length !== 0) + throw new Error("there are some imports"); diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile new file mode 100644 index 000000000..6a04d3439 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile @@ -0,0 +1,23 @@ +-include ../../run-make-fulldeps/tools.mk + +#only-x86_64-fortanix-unknown-sgx + +# For cargo setting +export RUSTC := $(RUSTC_ORIGINAL) +export LD_LIBRARY_PATH := $(HOST_RPATH_DIR) +# We need to be outside of 'src' dir in order to run cargo +export WORK_DIR := $(TMPDIR) +export TEST_DIR := $(shell pwd) + +## clean up unused env variables which might cause harm. +unexport RUSTC_LINKER +unexport RUSTC_BOOTSTRAP +unexport RUST_BUILD_STAGE +unexport RUST_TEST_THREADS +unexport RUST_TEST_TMPDIR +unexport AR +unexport CC +unexport CXX + +all: + bash script.sh diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_asm.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_asm.checks new file mode 100644 index 000000000..e839c200b --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_asm.checks @@ -0,0 +1,8 @@ +CHECK: cc_plus_one_asm +CHECK-NEXT: movl +CHECK-NEXT: lfence +CHECK-NEXT: inc +CHECK-NEXT: notq (%rsp) +CHECK-NEXT: notq (%rsp) +CHECK-NEXT: lfence +CHECK-NEXT: retq diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c.checks new file mode 100644 index 000000000..b93b33afb --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c.checks @@ -0,0 +1,6 @@ +CHECK: cc_plus_one_c +CHECK: lfence +CHECK: popq +CHECK-NEXT: popq [[REGISTER:%[a-z]+]] +CHECK-NEXT: lfence +CHECK-NEXT: jmpq *[[REGISTER]] diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c_asm.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c_asm.checks new file mode 100644 index 000000000..d1fae3d49 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_c_asm.checks @@ -0,0 +1,15 @@ +CHECK: cc_plus_one_c_asm +CHECK: lfence +CHECK: lfence +CHECK: lfence +CHECK: lfence +CHECK: lfence +CHECK-NEXT: incl +CHECK-NEXT: jmp +CHECK-NEXT: shlq $0, (%rsp) +CHECK-NEXT: lfence +CHECK-NEXT: retq +CHECK: popq +CHECK-NEXT: popq [[REGISTER:%[a-z]+]] +CHECK-NEXT: lfence +CHECK-NEXT: jmpq *[[REGISTER]] diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx.checks new file mode 100644 index 000000000..f96f152c0 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx.checks @@ -0,0 +1,6 @@ +CHECK: cc_plus_one_cxx +CHECK: lfence +CHECK: popq +CHECK-NEXT: popq [[REGISTER:%[a-z]+]] +CHECK-NEXT: lfence +CHECK-NEXT: jmpq *[[REGISTER]] diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx_asm.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx_asm.checks new file mode 100644 index 000000000..e704bf417 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cc_plus_one_cxx_asm.checks @@ -0,0 +1,16 @@ +CHECK: cc_plus_one_cxx_asm +CHECK: lfence +CHECK: lfence +CHECK: lfence +CHECK: movl +CHECK: lfence +CHECK: lfence +CHECK-NEXT: incl +CHECK-NEXT: jmp 0x{{[[:xdigit:]]+}} <cc_plus_one_cxx_asm+0x{{[[:xdigit:]]+}}> +CHECK-NEXT: shlq $0, (%rsp) +CHECK-NEXT: lfence +CHECK-NEXT: retq +CHECK: popq +CHECK-NEXT: popq [[REGISTER:%[a-z]+]] +CHECK-NEXT: lfence +CHECK-NEXT: jmpq *[[REGISTER]] diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_asm.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_asm.checks new file mode 100644 index 000000000..78b18ccbf --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_asm.checks @@ -0,0 +1,7 @@ +CHECK: cmake_plus_one_asm +CHECK-NEXT: movl +CHECK-NEXT: lfence +CHECK-NEXT: incl +CHECK-NEXT: shlq $0, (%rsp) +CHECK-NEXT: lfence +CHECK-NEXT: retq diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c.checks new file mode 100644 index 000000000..f551356b2 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c.checks @@ -0,0 +1,6 @@ +CHECK: cmake_plus_one_c +CHECK: lfence +CHECK: popq +CHECK-NEXT: popq [[REGISTER:%[a-z]+]] +CHECK-NEXT: lfence +CHECK-NEXT: jmpq *[[REGISTER]] diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_asm.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_asm.checks new file mode 100644 index 000000000..87c806f13 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_asm.checks @@ -0,0 +1,16 @@ +CHECK: cmake_plus_one_c_asm +CHECK: lfence +CHECK: lfence +CHECK: lfence +CHECK: lfence +CHECK: movl +CHECK: lfence +CHECK-NEXT: incl +CHECK-NEXT: jmp 0x{{[[:xdigit:]]+}} <cmake_plus_one_c_asm+0x{{[[:xdigit:]]+}}> +CHECK-NEXT: shlq $0, (%rsp) +CHECK-NEXT: lfence +CHECK-NEXT: retq +CHECK: popq +CHECK-NEXT: popq [[REGISTER:%[a-z]+]] +CHECK-NEXT: lfence +CHECK-NEXT: jmpq *[[REGISTER]] diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_global_asm.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_global_asm.checks new file mode 100644 index 000000000..4b66cc5bc --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_c_global_asm.checks @@ -0,0 +1,2 @@ +CHECK: cmake_plus_one_c_global_asm +CHECK: lfence diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx.checks new file mode 100644 index 000000000..0f403e020 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx.checks @@ -0,0 +1,6 @@ +CHECK: cmake_plus_one_cxx +CHECK: lfence +CHECK: popq +CHECK-NEXT: popq [[REGISTER:%[a-z]+]] +CHECK-NEXT: lfence +CHECK-NEXT: jmpq *[[REGISTER]] diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_asm.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_asm.checks new file mode 100644 index 000000000..9cac8711e --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_asm.checks @@ -0,0 +1,16 @@ +CHECK: cmake_plus_one_cxx_asm +CHECK: lfence +CHECK: lfence +CHECK: lfence +CHECK: lfence +CHECK: movl +CHECK: lfence +CHECK-NEXT: incl +CHECK-NEXT: jmp 0x{{[[:xdigit:]]+}} <cmake_plus_one_cxx_asm+0x{{[[:xdigit:]]+}}> +CHECK-NEXT: shlq $0, (%rsp) +CHECK-NEXT: lfence +CHECK-NEXT: retq +CHECK: popq +CHECK-NEXT: popq [[REGISTER:%[a-z]+]] +CHECK-NEXT: lfence +CHECK-NEXT: jmpq *[[REGISTER]] diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_global_asm.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_global_asm.checks new file mode 100644 index 000000000..d4a3d4479 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/cmake_plus_one_cxx_global_asm.checks @@ -0,0 +1,2 @@ +CHECK: cmake_plus_one_cxx_global_asm +CHECK: lfence diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/Cargo.toml b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/Cargo.toml new file mode 100644 index 000000000..3a97c37e9 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "enclave" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[build-dependencies] +cc = "1.0" +cmake = "0.1" diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/build.rs b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/build.rs new file mode 100644 index 000000000..3a7aa1be8 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/build.rs @@ -0,0 +1,30 @@ +fn main() { + cc::Build::new() + .file("foo.c") + .compile("foo_c"); + + cc::Build::new() + .file("foo_asm.s") + .compile("foo_asm"); + + cc::Build::new() + .cpp(true) + .cpp_set_stdlib(None) + .file("foo_cxx.cpp") + .compile("foo_cxx"); + + // When the cmake crate detects the clang compiler, it passes the + // "--target" argument to the linker which subsequently fails. The + // `CMAKE_C_COMPILER_FORCED` option makes sure that `cmake` does not + // tries to test the compiler. From version 3.6 the option + // `CMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY` can be used + // https://cmake.org/cmake/help/v3.5/module/CMakeForceCompiler.html + let dst = cmake::Config::new("libcmake_foo") + .build_target("cmake_foo") + .define("CMAKE_C_COMPILER_FORCED", "1") + .define("CMAKE_CXX_COMPILER_FORCED", "1") + .define("CMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY", "1") + .build(); + println!("cargo:rustc-link-search=native={}/build/", dst.display()); + println!("cargo:rustc-link-lib=static=cmake_foo"); +} diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo.c b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo.c new file mode 100644 index 000000000..dd76d4f30 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo.c @@ -0,0 +1,18 @@ +int cc_plus_one_c(int *arg) { + return *arg + 1; +} + +int cc_plus_one_c_asm(int *arg) { + int value = 0; + + asm volatile ( " movl (%1), %0\n" + " inc %0\n" + " jmp 1f\n" + " retq\n" // never executed, but a shortcut to determine how + // the assembler deals with `ret` instructions + "1:\n" + : "=r"(value) + : "r"(arg) ); + + return value; +} diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_asm.s b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_asm.s new file mode 100644 index 000000000..6d56214e8 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_asm.s @@ -0,0 +1,7 @@ + .text + .global cc_plus_one_asm + .type cc_plus_one_asm, @function +cc_plus_one_asm: + movl (%rdi), %eax + inc %eax + retq diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_cxx.cpp b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_cxx.cpp new file mode 100644 index 000000000..ac6f64ac4 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/foo_cxx.cpp @@ -0,0 +1,21 @@ +extern "C" int cc_plus_one_cxx(int *arg); +extern "C" int cc_plus_one_cxx_asm(int *arg); + +int cc_plus_one_cxx(int *arg) { + return *arg + 1; +} + +int cc_plus_one_cxx_asm(int *arg) { + int value = 0; + + asm volatile ( " movl (%1), %0\n" + " inc %0\n" + " jmp 1f\n" + " retq\n" // never executed, but a shortcut to determine how + // the assembler deals with `ret` instructions + "1:\n" + : "=r"(value) + : "r"(arg) ); + + return value; +} diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/CMakeLists.txt b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/CMakeLists.txt new file mode 100644 index 000000000..27cdf2ecf --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/CMakeLists.txt @@ -0,0 +1,33 @@ +enable_language(C CXX ASM) + +set(C_SOURCES + src/foo.c + ) + +set_source_files_properties(${C_SOURCES} + PROPERTIES + LANGUAGE C) + +set(CXX_SOURCES + src/foo_cxx.cpp + ) + +set_source_files_properties(${CXX_SOURCES} + PROPERTIES + LANGUAGE CXX) + +set(ASM_SOURCES + src/foo_asm.s + ) + +set_source_files_properties(${ASM_SOURCES} + PROPERTIES + LANGUAGE ASM) + +set(SOURCES + ${C_SOURCES} + ${CXX_SOURCES} + ${ASM_SOURCES}) + +add_library(cmake_foo STATIC + ${SOURCES}) diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo.c b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo.c new file mode 100644 index 000000000..c3b731a2d --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo.c @@ -0,0 +1,26 @@ +int cmake_plus_one_c(int *arg) { + return *arg + 1; +} + +int cmake_plus_one_c_asm(int *arg) { + int value = 0; + + asm volatile ( " movl (%1), %0\n" + " inc %0\n" + " jmp 1f\n" + " retq\n" // never executed, but a shortcut to determine how + // the assembler deals with `ret` instructions + "1:\n" + : "=r"(value) + : "r"(arg) ); + + return value; +} + +asm(".text\n" +" .global cmake_plus_one_c_global_asm\n" +" .type cmake_plus_one_c_global_asm, @function\n" +"cmake_plus_one_c_global_asm:\n" +" movl (%rdi), %eax\n" +" inc %eax\n" +" retq\n" ); diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_asm.s b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_asm.s new file mode 100644 index 000000000..64b6b430e --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_asm.s @@ -0,0 +1,7 @@ + .text + .global cmake_plus_one_asm + .type cmake_plus_one_asm, @function +cmake_plus_one_asm: + movl (%rdi), %eax + inc %eax + retq diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_cxx.cpp b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_cxx.cpp new file mode 100644 index 000000000..824e2afeb --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/libcmake_foo/src/foo_cxx.cpp @@ -0,0 +1,29 @@ +extern "C" int cmake_plus_one_cxx(int *arg); +extern "C" int cmake_plus_one_cxx_asm(int *arg); + +int cmake_plus_one_cxx(int *arg) { + return *arg + 1; +} + +int cmake_plus_one_cxx_asm(int *arg) { + int value = 0; + + asm volatile ( " movl (%1), %0\n" + " inc %0\n" + " jmp 1f\n" + " retq\n" // never executed, but a shortcut to determine how + // the assembler deals with `ret` instructions + "1:\n" + : "=r"(value) + : "r"(arg) ); + + return value; +} + +asm(".text\n" +" .global cmake_plus_one_cxx_global_asm\n" +" .type cmake_plus_one_cxx_global_asm, @function\n" +"cmake_plus_one_cxx_global_asm:\n" +" movl (%rdi), %eax\n" +" inc %eax\n" +" retq\n" ); diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs new file mode 100644 index 000000000..cde38aacf --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/enclave/src/main.rs @@ -0,0 +1,49 @@ +std::arch::global_asm!( + r#" + .text + .global rust_plus_one_global_asm + .type rust_plus_one_global_asm, @function +rust_plus_one_global_asm: + movl (%rdi), %eax + inc %eax + retq +"#, + options(att_syntax) +); + +extern "C" { + fn cc_plus_one_c(arg: &u32) -> u32; + fn cc_plus_one_c_asm(arg: &u32) -> u32; + fn cc_plus_one_cxx(arg: &u32) -> u32; + fn cc_plus_one_cxx_asm(arg: &u32) -> u32; + fn cc_plus_one_asm(arg: &u32) -> u32; + fn cmake_plus_one_c(arg: &u32) -> u32; + fn cmake_plus_one_c_asm(arg: &u32) -> u32; + fn cmake_plus_one_cxx(arg: &u32) -> u32; + fn cmake_plus_one_cxx_asm(arg: &u32) -> u32; + fn cmake_plus_one_c_global_asm(arg: &u32) -> u32; + fn cmake_plus_one_cxx_global_asm(arg: &u32) -> u32; + fn cmake_plus_one_asm(arg: &u32) -> u32; + fn rust_plus_one_global_asm(arg: &u32) -> u32; +} + +fn main() { + let value: u32 = 41; + let question = "Answer to the Ultimate Question of Life, the Universe, and Everything:"; + + unsafe { + println!("{}: {}!", question, rust_plus_one_global_asm(&value)); + println!("{}: {}!", question, cc_plus_one_c(&value)); + println!("{}: {}!", question, cc_plus_one_c_asm(&value)); + println!("{}: {}!", question, cc_plus_one_cxx(&value)); + println!("{}: {}!", question, cc_plus_one_cxx_asm(&value)); + println!("{}: {}!", question, cc_plus_one_asm(&value)); + println!("{}: {}!", question, cmake_plus_one_c(&value)); + println!("{}: {}!", question, cmake_plus_one_c_asm(&value)); + println!("{}: {}!", question, cmake_plus_one_cxx(&value)); + println!("{}: {}!", question, cmake_plus_one_cxx_asm(&value)); + println!("{}: {}!", question, cmake_plus_one_c_global_asm(&value)); + println!("{}: {}!", question, cmake_plus_one_cxx_global_asm(&value)); + println!("{}: {}!", question, cmake_plus_one_asm(&value)); + } +} diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/jumpto.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/jumpto.checks new file mode 100644 index 000000000..15211e3ad --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/jumpto.checks @@ -0,0 +1,8 @@ +CHECK: libunwind::Registers_x86_64::jumpto +CHECK: lfence +CHECK: lfence +CHECK: lfence +CHECK: lfence +CHECK: shlq $0, (%rsp) +CHECK-NEXT: lfence +CHECK-NEXT: retq diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks new file mode 100644 index 000000000..0fe88141b --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks @@ -0,0 +1,7 @@ +CHECK: print +CHECK: lfence +CHECK: lfence +CHECK: lfence +CHECK: popq +CHECK: callq 0x{{[[:xdigit:]]*}} <_Unwind_Resume> +CHECK-NEXT: ud2 diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/rust_plus_one_global_asm.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/rust_plus_one_global_asm.checks new file mode 100644 index 000000000..fe6777537 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/rust_plus_one_global_asm.checks @@ -0,0 +1,2 @@ +CHECK: rust_plus_one_global_asm +CHECK: lfence diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh new file mode 100644 index 000000000..54645e9e2 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh @@ -0,0 +1,58 @@ +#!/bin/sh +set -exuo pipefail + +function build { + CRATE=enclave + + mkdir -p $WORK_DIR + pushd $WORK_DIR + rm -rf $CRATE + cp -a $TEST_DIR/enclave . + pushd $CRATE + echo ${WORK_DIR} + # HACK(eddyb) sets `RUSTC_BOOTSTRAP=1` so Cargo can accept nightly features. + # These come from the top-level Rust workspace, that this crate is not a + # member of, but Cargo tries to load the workspace `Cargo.toml` anyway. + env RUSTC_BOOTSTRAP=1 + cargo -v run --target $TARGET + popd + popd +} + +function check { + local func=$1 + local checks="${TEST_DIR}/$2" + local asm=$(mktemp) + local objdump="${BUILD_DIR}/x86_64-unknown-linux-gnu/llvm/build/bin/llvm-objdump" + local filecheck="${BUILD_DIR}/x86_64-unknown-linux-gnu/llvm/build/bin/FileCheck" + + ${objdump} --disassemble-symbols=${func} --demangle \ + ${WORK_DIR}/enclave/target/x86_64-fortanix-unknown-sgx/debug/enclave > ${asm} + ${filecheck} --input-file ${asm} ${checks} +} + +build + +check unw_getcontext unw_getcontext.checks +check "libunwind::Registers_x86_64::jumpto()" jumpto.checks +check "std::io::stdio::_print::h87f0c238421c45bc" print.checks +check rust_plus_one_global_asm rust_plus_one_global_asm.checks \ + || echo "warning: module level assembly currently not hardened" + +check cc_plus_one_c cc_plus_one_c.checks +check cc_plus_one_c_asm cc_plus_one_c_asm.checks +check cc_plus_one_cxx cc_plus_one_cxx.checks +check cc_plus_one_cxx_asm cc_plus_one_cxx_asm.checks +check cc_plus_one_asm cc_plus_one_asm.checks \ + || echo "warning: the cc crate forwards assembly files to the CC compiler." \ + "Clang uses its own intergrated assembler, which does not include the LVI passes." + +check cmake_plus_one_c cmake_plus_one_c.checks +check cmake_plus_one_c_asm cmake_plus_one_c_asm.checks +check cmake_plus_one_c_global_asm cmake_plus_one_c_global_asm.checks \ + || echo "warning: module level assembly currently not hardened" +check cmake_plus_one_cxx cmake_plus_one_cxx.checks +check cmake_plus_one_cxx_asm cmake_plus_one_cxx_asm.checks +check cmake_plus_one_cxx_global_asm cmake_plus_one_cxx_global_asm.checks \ + || echo "warning: module level assembly currently not hardened" +check cmake_plus_one_asm cmake_plus_one_asm.checks diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/unw_getcontext.checks b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/unw_getcontext.checks new file mode 100644 index 000000000..4b7615b11 --- /dev/null +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/unw_getcontext.checks @@ -0,0 +1,6 @@ +CHECK: unw_getcontext +CHECK: lfence +CHECK: lfence +CHECK: shlq $0, (%rsp) +CHECK-NEXT: lfence +CHECK-NEXT: retq |