diff --git a/.gitignore b/.gitignore index d31b5d90603..0b5c90556f6 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,8 @@ _build_playground node_modules *.dump coverage +_coverage/ +bisect*.coverage lib/ocaml tests/build_tests/*/lib/ diff --git a/.ocamlformat-ignore b/.ocamlformat-ignore index d228f90a37a..f58b14a5225 100644 --- a/.ocamlformat-ignore +++ b/.ocamlformat-ignore @@ -1,18 +1,4 @@ compiler/js_parser/** -compiler/ml/cmt_format.ml -compiler/core/js_name_of_module_id.ml -compiler/core/js_pass_debug.ml -compiler/core/lam_util.ml -compiler/core/lam_compile_main.ml -compiler/ext/bs_hash_stubs.ml -compiler/ext/js_reserved_map.ml -compiler/ext/ext_string.ml -compiler/ext/ext_string.mli -compiler/ext/ext_sys.ml -compiler/ext/hash.cppo.ml -compiler/ext/hash_set.cppo.ml -compiler/ext/map.cppo.ml -compiler/ext/ordered_hash_map.cppo.ml -compiler/ext/set.cppo.ml -compiler/ext/vec.cppo.ml +**/*.cppo.ml +**/*.cppo.mli compiler/syntax/compiler-libs-406/* diff --git a/Makefile b/Makefile index 088d3d60003..7239353e8fb 100644 --- a/Makefile +++ b/Makefile @@ -217,6 +217,87 @@ format: | $(YARN_INSTALL_STAMP) checkformat: | $(YARN_INSTALL_STAMP) ./scripts/format_check.sh +# Coverage (bisect_ppx) +# +# Requires the `bisect_ppx` opam package (>= 2.8.0) in your switch: +# opam install bisect_ppx +# or pull it in via the rescript dev-setup deps: +# opam install . --deps-only --with-dev-setup +# +# Quick start: +# make coverage # run full test suite, generate report +# make clean-coverage # remove coverage artifacts +# +# Outputs (under _coverage/): +# html/index.html — human-browsable line-level report +# coverage.json — Coveralls-format JSON, queryable with jq: +# { source_files: [{ name, coverage: [null|N, ...] }] } +# null = not instrumented, 0 = uncovered, N > 0 = hit count +# e.g. uncovered line numbers in one file: +# jq -r --arg f compiler/ml/typecore.ml \ +# '.source_files[] | select(.name==$f) | .coverage +# | to_entries[] | select(.value==0) | (.key+1)' \ +# _coverage/coverage.json + +COVERAGE_DIR := _coverage +COVERAGE_FILES_DIR := $(COVERAGE_DIR)/files +COVERAGE_HTML_DIR := $(COVERAGE_DIR)/html +COVERAGE_JSON := $(COVERAGE_DIR)/coverage.json +COVERAGE_BISECT_PREFIX := $(abspath $(COVERAGE_FILES_DIR))/bisect + +# Re-builds the toolchain with bisect_ppx instrumentation and swaps the +# instrumented binaries into BIN_DIR so any test runner that shells out to +# `bsc` produces .coverage files. +.PHONY: coverage-build +coverage-build: | $(YARN_INSTALL_STAMP) + dune build --instrument-with bisect_ppx + @$(foreach bin,$(COMPILER_DUNE_BINS),touch $(bin);) + @$(foreach bin,$(COMPILER_BIN_NAMES), \ + cp $(DUNE_BIN_DIR)/$(bin)$(PLATFORM_EXE_EXT) $(BIN_DIR)/$(bin).exe && \ + chmod 755 $(BIN_DIR)/$(bin).exe;) + +.PHONY: coverage-prepare +coverage-prepare: clean-coverage coverage-build + mkdir -p $(COVERAGE_FILES_DIR) + +# Build the runtime with the instrumented bsc so subsequent test runs have +# a fresh stdlib. Coverage from the runtime build is discarded so reports +# only reflect what the tests exercised. +.PHONY: coverage-lib +coverage-lib: coverage-prepare + BISECT_FILE=$(COVERAGE_BISECT_PREFIX)-discard BISECT_SILENT=YES \ + yarn workspace @rescript/runtime build + rm -f $(COVERAGE_BISECT_PREFIX)-discard*.coverage + +.PHONY: coverage-run +coverage-run: coverage-lib + BISECT_FILE=$(COVERAGE_BISECT_PREFIX) BISECT_SILENT=YES \ + node scripts/test.js -all + +.PHONY: coverage-report +coverage-report: + bisect-ppx-report html \ + --coverage-path $(COVERAGE_FILES_DIR) \ + --ignore-missing-files \ + -o $(COVERAGE_HTML_DIR) + bisect-ppx-report coveralls \ + --coverage-path $(COVERAGE_FILES_DIR) \ + --ignore-missing-files \ + $(COVERAGE_JSON) + bisect-ppx-report summary \ + --coverage-path $(COVERAGE_FILES_DIR) + @echo "" + @echo "HTML report: $(COVERAGE_HTML_DIR)/index.html" + @echo "JSON data: $(COVERAGE_JSON)" + +.PHONY: coverage +coverage: coverage-run coverage-report + +.PHONY: clean-coverage +clean-coverage: + rm -rf $(COVERAGE_DIR) + find . -name 'bisect*.coverage' -not -path './_build/*' -delete + # Clean clean-gentype: @@ -225,7 +306,7 @@ clean-gentype: clean-tests: clean-gentype -clean: clean-lib clean-compiler clean-rewatch +clean: clean-lib clean-compiler clean-rewatch clean-coverage dev-container: docker build -t rescript-dev-container docker diff --git a/analysis/bin/dune b/analysis/bin/dune index 64c2a78156e..c7be0fc681b 100644 --- a/analysis/bin/dune +++ b/analysis/bin/dune @@ -8,4 +8,6 @@ (package analysis) (modes byte exe) (name main) + (instrumentation + (backend bisect_ppx)) (libraries analysis)) diff --git a/analysis/src/dune b/analysis/src/dune index c1bd828c114..0b27f5aac8d 100644 --- a/analysis/src/dune +++ b/analysis/src/dune @@ -1,5 +1,7 @@ (library (name analysis) + (instrumentation + (backend bisect_ppx)) (flags (-w "+6+26+27+32+33+39")) (libraries unix str ext ml jsonlib syntax reanalyze)) diff --git a/compiler/bsc/dune b/compiler/bsc/dune index 4a50387af2d..97226e1fa77 100644 --- a/compiler/bsc/dune +++ b/compiler/bsc/dune @@ -7,6 +7,8 @@ (name rescript_compiler_main) (public_name bsc) (package rescript) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w +a-4-9-30-40-41-42-48-70)) (libraries common core depends flow_parser gentype syntax)) diff --git a/compiler/common/dune b/compiler/common/dune index b6962b3d215..247250ab20c 100644 --- a/compiler/common/dune +++ b/compiler/common/dune @@ -1,9 +1,8 @@ (library (name common) (wrapped false) - (preprocess - (action - (run %{bin:cppo} %{env:CPPO_FLAGS=} %{input-file}))) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w +a-9-40-42)) (libraries syntax)) diff --git a/compiler/core/dune b/compiler/core/dune index d7d75f34437..0e36c273a23 100644 --- a/compiler/core/dune +++ b/compiler/core/dune @@ -1,9 +1,32 @@ (library (name core) (wrapped false) - (preprocess - (action - (run %{bin:cppo} %{env:CPPO_FLAGS=} %{input-file}))) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w +a-4-9-27-30-40-41-42-48-70)) (libraries depends ext flow_parser frontend gentype)) + +(rule + (target js_name_of_module_id.ml) + (deps js_name_of_module_id.cppo.ml) + (action + (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target}))) + +(rule + (target js_pass_debug.ml) + (deps js_pass_debug.cppo.ml) + (action + (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target}))) + +(rule + (target lam_compile_main.ml) + (deps lam_compile_main.cppo.ml) + (action + (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target}))) + +(rule + (target lam_util.ml) + (deps lam_util.cppo.ml) + (action + (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target}))) diff --git a/compiler/core/js_name_of_module_id.ml b/compiler/core/js_name_of_module_id.cppo.ml similarity index 100% rename from compiler/core/js_name_of_module_id.ml rename to compiler/core/js_name_of_module_id.cppo.ml diff --git a/compiler/core/js_pass_debug.ml b/compiler/core/js_pass_debug.cppo.ml similarity index 100% rename from compiler/core/js_pass_debug.ml rename to compiler/core/js_pass_debug.cppo.ml diff --git a/compiler/core/lam_compile_main.ml b/compiler/core/lam_compile_main.cppo.ml similarity index 100% rename from compiler/core/lam_compile_main.ml rename to compiler/core/lam_compile_main.cppo.ml diff --git a/compiler/core/lam_util.ml b/compiler/core/lam_util.cppo.ml similarity index 100% rename from compiler/core/lam_util.ml rename to compiler/core/lam_util.cppo.ml diff --git a/compiler/depends/dune b/compiler/depends/dune index be76caa4081..1954403aabe 100644 --- a/compiler/depends/dune +++ b/compiler/depends/dune @@ -1,6 +1,8 @@ (library (name depends) (wrapped false) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w +a-4-40-42)) (libraries common)) diff --git a/compiler/ext/bs_hash_stubs.ml b/compiler/ext/bs_hash_stubs.cppo.ml similarity index 100% rename from compiler/ext/bs_hash_stubs.ml rename to compiler/ext/bs_hash_stubs.cppo.ml diff --git a/compiler/ext/dune b/compiler/ext/dune index 1e2117bcce8..c6a8a9dfeb9 100644 --- a/compiler/ext/dune +++ b/compiler/ext/dune @@ -1,20 +1,51 @@ (library (name ext) (wrapped false) - (preprocess - (action - (run - %{bin:cppo} - -V - OCAML:%{ocaml_version} - %{env:CPPO_FLAGS=} - %{input-file}))) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w +a-4-42-40-9-48-70)) (foreign_stubs (language c) (names ext_basic_hash_stubs))) +(rule + (target bs_hash_stubs.ml) + (deps bs_hash_stubs.cppo.ml) + (action + (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target}))) + +(rule + (target js_reserved_map.ml) + (deps js_reserved_map.cppo.ml) + (action + (run + %{bin:cppo} + -V + OCAML:%{ocaml_version} + %{env:CPPO_FLAGS=} + %{deps} + -o + %{target}))) + +(rule + (target ext_sys.ml) + (deps ext_sys.cppo.ml) + (action + (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target}))) + +(rule + (target ext_string.ml) + (deps ext_string.cppo.ml) + (action + (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target}))) + +(rule + (target ext_string.mli) + (deps ext_string.cppo.mli) + (action + (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target}))) + (rule (targets hash_set_string.ml) (deps hash_set.cppo.ml) diff --git a/compiler/ext/ext_string.ml b/compiler/ext/ext_string.cppo.ml similarity index 100% rename from compiler/ext/ext_string.ml rename to compiler/ext/ext_string.cppo.ml diff --git a/compiler/ext/ext_string.mli b/compiler/ext/ext_string.cppo.mli similarity index 100% rename from compiler/ext/ext_string.mli rename to compiler/ext/ext_string.cppo.mli diff --git a/compiler/ext/ext_sys.ml b/compiler/ext/ext_sys.cppo.ml similarity index 100% rename from compiler/ext/ext_sys.ml rename to compiler/ext/ext_sys.cppo.ml diff --git a/compiler/ext/js_reserved_map.ml b/compiler/ext/js_reserved_map.cppo.ml similarity index 100% rename from compiler/ext/js_reserved_map.ml rename to compiler/ext/js_reserved_map.cppo.ml diff --git a/compiler/frontend/dune b/compiler/frontend/dune index d4a7e7dfc74..c94fbd55e93 100644 --- a/compiler/frontend/dune +++ b/compiler/frontend/dune @@ -1,6 +1,8 @@ (library (name frontend) (wrapped false) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w +a-4-9-40-42-70)) (libraries common ml)) diff --git a/compiler/gentype/dune b/compiler/gentype/dune index d5aa7137b20..4c6fb3af5cf 100644 --- a/compiler/gentype/dune +++ b/compiler/gentype/dune @@ -1,6 +1,8 @@ (library (name gentype) (wrapped false) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w +a-4-9-40-41-42-48-70)) (libraries ml)) diff --git a/compiler/ml/cmt_format.ml b/compiler/ml/cmt_format.cppo.ml similarity index 100% rename from compiler/ml/cmt_format.ml rename to compiler/ml/cmt_format.cppo.ml diff --git a/compiler/ml/dune b/compiler/ml/dune index f4dde0ef5bb..a5a53b70c3f 100644 --- a/compiler/ml/dune +++ b/compiler/ml/dune @@ -1,9 +1,14 @@ (library (name ml) (wrapped false) - (preprocess - (action - (run %{bin:cppo} %{env:CPPO_FLAGS=} %{input-file}))) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w +a-4-42-40-41-44-45-9-48-67-70)) (libraries ext flow_parser)) + +(rule + (target cmt_format.ml) + (deps cmt_format.cppo.ml) + (action + (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target}))) diff --git a/compiler/syntax/cli/dune b/compiler/syntax/cli/dune index ba5f1ea4ce8..90903d2e792 100644 --- a/compiler/syntax/cli/dune +++ b/compiler/syntax/cli/dune @@ -9,6 +9,8 @@ (package rescript) (enabled_if (<> %{profile} browser)) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w +a-4-42-40-9-48-70)) (libraries syntax)) diff --git a/compiler/syntax/src/dune b/compiler/syntax/src/dune index 7765dcbf61c..9fa08e74eb4 100644 --- a/compiler/syntax/src/dune +++ b/compiler/syntax/src/dune @@ -1,6 +1,8 @@ (library (name syntax) (wrapped false) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w +a-4-42-40-9-48-70)) (libraries ml)) diff --git a/rescript.opam b/rescript.opam index 40a9251350f..c19070af555 100644 --- a/rescript.opam +++ b/rescript.opam @@ -30,6 +30,7 @@ depends: [ "ounit2" {with-test & = "2.2.7"} "odoc" {with-doc} "ocaml-lsp-server" {with-dev-setup & = "1.22.0"} + "bisect_ppx" {with-dev-setup & >= "2.8.0"} # Test dependencies that would be broken on Windows runners "js_of_ocaml" {os != "win32" & with-test & = "6.0.1"} diff --git a/rescript.opam.template b/rescript.opam.template index e5629e01d6a..e8b2fe6abd9 100644 --- a/rescript.opam.template +++ b/rescript.opam.template @@ -8,6 +8,7 @@ depends: [ "ounit2" {with-test & = "2.2.7"} "odoc" {with-doc} "ocaml-lsp-server" {with-dev-setup & = "1.22.0"} + "bisect_ppx" {with-dev-setup & >= "2.8.0"} # Test dependencies that would be broken on Windows runners "js_of_ocaml" {os != "win32" & with-test & = "6.0.1"} diff --git a/tests/ounit_tests/dune b/tests/ounit_tests/dune index e0bcd078bcb..73bd6f0ce25 100644 --- a/tests/ounit_tests/dune +++ b/tests/ounit_tests/dune @@ -9,6 +9,8 @@ (package rescript) (enabled_if (<> %{profile} browser)) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w +a-4-9-30-40-41-42-48-70)) (libraries core ounit2 analysis)) diff --git a/tools/bin/dune b/tools/bin/dune index 674eca5b67d..f3a87dbbaf3 100644 --- a/tools/bin/dune +++ b/tools/bin/dune @@ -10,5 +10,7 @@ ; The main module that will become the binary. (name main) (libraries tools) + (instrumentation + (backend bisect_ppx)) (flags (:standard -w "+6+26+27+32+33+39"))) diff --git a/tools/src/dune b/tools/src/dune index ec90d74ab02..765cece335f 100644 --- a/tools/src/dune +++ b/tools/src/dune @@ -1,5 +1,7 @@ (library (name tools) + (instrumentation + (backend bisect_ppx)) (flags (-w "+6+26+27+32+33+39")) (libraries analysis cmarkit))