feat(slang): checked arithmetic, narrow types, and MLIR alignment #993
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Code coverage | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, labeled] | |
| concurrency: | |
| group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| defaults: | |
| run: | |
| shell: "bash -ex {0}" | |
| permissions: | |
| contents: read | |
| jobs: | |
| label-check: | |
| if: >- | |
| (github.event.action == 'labeled' && github.event.label.name == 'ci:coverage' | |
| || github.event.action != 'labeled' && contains(github.event.pull_request.labels.*.name, 'ci:coverage')) | |
| && !github.event.pull_request.head.repo.fork | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - run: 'true' | |
| cooldown-check: | |
| name: Cargo cooldown check | |
| runs-on: ubuntu-24.04 | |
| needs: label-check | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - uses: ./.github/actions/cooldown-check | |
| coverage: | |
| needs: [label-check, cooldown-check] | |
| runs-on: solx-linux-amd64 | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| packages: read | |
| container: | |
| image: ghcr.io/nomicfoundation/solx-ci-runner@sha256:c0866d146261cd0a51dc7d9077444b8ac3dde12c53d2151137834e6be149dbc7 | |
| env: | |
| BOOST_PREFIX: ${{ github.workspace }}/solx-solidity/boost/lib | |
| SOLC_PREFIX: ${{ github.workspace }}/solx-solidity/build | |
| PROFDATA_FILE: solx.profdata | |
| LCOV_FILE: codecov.lcov | |
| OUTPUT_HTML_DIR: COVERAGE | |
| COVERAGE_IGNORE_REGEX: '/(solx-llvm|target-llvm|solx-solidity|\.cargo|cargo/registry)/' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| submodules: true | |
| - name: Checkout submodules | |
| run: | | |
| git config --global --add safe.directory '*' | |
| git submodule update --init --force --depth=1 --recursive --checkout | |
| - name: Build LLVM | |
| uses: ./.github/actions/build-llvm | |
| with: | |
| build-type: 'RelWithDebInfo' | |
| enable-assertions: 'false' | |
| enable-coverage: 'true' | |
| enable-mlir: 'true' | |
| - name: Building solc | |
| uses: ./.github/actions/build-solc | |
| with: | |
| cmake-build-type: RelWithDebInfo | |
| working-dir: 'solx-solidity' | |
| - name: Build solx | |
| id: build-solx | |
| uses: ./.github/actions/build-rust | |
| with: | |
| exec_name: 'solx' | |
| build-type: 'release' | |
| enable-coverage: 'true' | |
| target: 'x86_64-unknown-linux-gnu' | |
| - name: Run integration tests | |
| run: | | |
| TMP="${PROFDATA_FILE}.tmp" | |
| TARGET_DIR="./target/x86_64-unknown-linux-gnu/release" | |
| for TEST_PATH in tests/solidity/simple/default.sol tests/solidity/complex/default; do | |
| WORKDIR=$(basename ${TEST_PATH}) | |
| mkdir -p "${GITHUB_WORKSPACE}/${WORKDIR}" | |
| "${TARGET_DIR}/solx-dev" test solx-tester \ | |
| --binary "${TARGET_DIR}/solx-tester" \ | |
| --solidity-compiler ${{ steps.build-solx.outputs.binary-path }} \ | |
| --path ${TEST_PATH} --workflow build --optimizer M3B3 | |
| mv *.profraw "${GITHUB_WORKSPACE}/${WORKDIR}/" || true | |
| find "${GITHUB_WORKSPACE}/${WORKDIR}" -type f -name '*.profraw' -print > profiles.lst | |
| if [[ -f "${PROFDATA_FILE}" ]]; then | |
| llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" "${PROFDATA_FILE}" @profiles.lst | |
| else | |
| llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" @profiles.lst | |
| fi | |
| mv -f "${TMP}" "${PROFDATA_FILE}" | |
| rm -rf "${GITHUB_WORKSPACE}/${WORKDIR}" | |
| done | |
| - name: Preserve instrumented binary | |
| env: | |
| SOLX_BINARY: ${{ steps.build-solx.outputs.binary-path }} | |
| run: | | |
| cp "${SOLX_BINARY}" "${GITHUB_WORKSPACE}/solx-instrumented" | |
| cp "./target/x86_64-unknown-linux-gnu/release/solx-tester" "${GITHUB_WORKSPACE}/solx-tester-instrumented" | |
| cp "./target/x86_64-unknown-linux-gnu/release/solx-dev" "${GITHUB_WORKSPACE}/solx-dev-instrumented" | |
| - name: Run unit tests | |
| env: | |
| RUSTC_BOOTSTRAP: 1 | |
| RUSTFLAGS: '-C target-feature=+crt-static -C instrument-coverage -C link-arg=-fuse-ld=lld' | |
| LLVM_PROFILE_FILE: 'unit-tests-%p-%m.profraw' | |
| run: cargo fetch --locked && cargo test --frozen --release --target x86_64-unknown-linux-gnu | |
| # Inkwell feature poisoning: solc path builds inkwell with LLVM linking, | |
| # slang path needs no-llvm-linking. cargo clean cannot reliably clear | |
| # cached llvm-sys build script output. Use a separate target directory | |
| # for complete isolation. | |
| - name: Run slang unit tests | |
| if: contains(github.event.pull_request.labels.*.name, 'ci:slang') | |
| env: | |
| RUSTC_BOOTSTRAP: 1 | |
| RUSTFLAGS: '-C instrument-coverage -C link-arg=-fuse-ld=lld' | |
| LLVM_PROFILE_FILE: 'slang-tests-%p-%m.profraw' | |
| CARGO_TARGET_DIR: target-slang | |
| run: cargo test-slang --frozen --release --target x86_64-unknown-linux-gnu | |
| - name: Preserve slang instrumented binary | |
| if: contains(github.event.pull_request.labels.*.name, 'ci:slang') | |
| run: cp "./target-slang/x86_64-unknown-linux-gnu/release/solx" "${GITHUB_WORKSPACE}/solx-slang-instrumented" | |
| - name: Merge slang test profraw | |
| if: contains(github.event.pull_request.labels.*.name, 'ci:slang') | |
| run: | | |
| find . -name 'slang-tests-*.profraw' -print > slang-profiles.lst | |
| if [ -s slang-profiles.lst ]; then | |
| TMP="${PROFDATA_FILE}.tmp" | |
| llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" "${PROFDATA_FILE}" @slang-profiles.lst | |
| mv -f "${TMP}" "${PROFDATA_FILE}" | |
| find . -name 'slang-tests-*.profraw' -delete | |
| fi | |
| - name: Merge unit test profraw | |
| run: | | |
| find . -name 'unit-tests-*.profraw' -print > unit-profiles.lst | |
| if [ -s unit-profiles.lst ]; then | |
| TMP="${PROFDATA_FILE}.tmp" | |
| llvm-profdata merge -sparse -num-threads="$(nproc)" -o "${TMP}" "${PROFDATA_FILE}" @unit-profiles.lst | |
| mv -f "${TMP}" "${PROFDATA_FILE}" | |
| find . -name 'unit-tests-*.profraw' -delete | |
| fi | |
| - name: Generate coverage reports | |
| run: | | |
| OBJECTS=( | |
| -object "${GITHUB_WORKSPACE}/solx-instrumented" | |
| -object "${GITHUB_WORKSPACE}/solx-tester-instrumented" | |
| -object "${GITHUB_WORKSPACE}/solx-dev-instrumented" | |
| ) | |
| [ -f "${GITHUB_WORKSPACE}/solx-slang-instrumented" ] && OBJECTS+=(-object "${GITHUB_WORKSPACE}/solx-slang-instrumented") | |
| llvm-cov show --show-directory-coverage \ | |
| --format=html --output-dir=${OUTPUT_HTML_DIR} \ | |
| --ignore-filename-regex="${COVERAGE_IGNORE_REGEX}" \ | |
| -instr-profile=${PROFDATA_FILE} "${OBJECTS[@]}" | |
| mkdir -p llvm | |
| llvm-cov export --format=lcov \ | |
| --ignore-filename-regex="${COVERAGE_IGNORE_REGEX}" \ | |
| -instr-profile=${PROFDATA_FILE} \ | |
| "${OBJECTS[@]}" > ./llvm/${LCOV_FILE} | |
| - name: Coverage summary | |
| id: coverage-summary | |
| run: | | |
| OBJECTS=( | |
| -object "${GITHUB_WORKSPACE}/solx-instrumented" | |
| -object "${GITHUB_WORKSPACE}/solx-tester-instrumented" | |
| -object "${GITHUB_WORKSPACE}/solx-dev-instrumented" | |
| ) | |
| [ -f "${GITHUB_WORKSPACE}/solx-slang-instrumented" ] && OBJECTS+=(-object "${GITHUB_WORKSPACE}/solx-slang-instrumented") | |
| llvm-cov report -instr-profile=${PROFDATA_FILE} "${OBJECTS[@]}" \ | |
| --ignore-filename-regex="${COVERAGE_IGNORE_REGEX}" \ | |
| > coverage-report.txt | |
| # Parse llvm-cov report into per-crate markdown table | |
| awk ' | |
| /^---/ || /^Filename/ || /^$/ { next } | |
| /^TOTAL/ { | |
| total_lines = $(NF-5); total_missed = $(NF-4) | |
| total_funcs = $(NF-8); total_missed_funcs = $(NF-7) | |
| next | |
| } | |
| NF > 3 { | |
| file = $1 | |
| n = split(file, parts, "/") | |
| crate = "" | |
| for (i = 1; i <= n-1; i++) { | |
| if (parts[i] ~ /^solx/ && parts[i+1] == "src") { | |
| crate = parts[i]; break | |
| } | |
| } | |
| if (crate == "") next | |
| lines[crate] += $(NF-5) | |
| missed[crate] += $(NF-4) | |
| funcs[crate] += $(NF-8) | |
| missed_f[crate] += $(NF-7) | |
| } | |
| END { | |
| for (c in lines) { | |
| lp = (lines[c] > 0) ? (lines[c] - missed[c]) / lines[c] * 100 : 0 | |
| fp = (funcs[c] > 0) ? (funcs[c] - missed_f[c]) / funcs[c] * 100 : 0 | |
| le = (lp >= 80) ? "π’" : (lp >= 50) ? "π‘" : "π΄" | |
| fe = (fp >= 80) ? "π’" : (fp >= 50) ? "π‘" : "π΄" | |
| printf "| %s | %s %.1f%% | %s %.1f%% |\n", c, le, lp, fe, fp | |
| } | |
| lp = (total_lines > 0) ? (total_lines - total_missed) / total_lines * 100 : 0 | |
| fp = (total_funcs > 0) ? (total_funcs - total_missed_funcs) / total_funcs * 100 : 0 | |
| le = (lp >= 80) ? "π’" : (lp >= 50) ? "π‘" : "π΄" | |
| fe = (fp >= 80) ? "π’" : (fp >= 50) ? "π‘" : "π΄" | |
| printf "ZTOTAL| **Total** | **%s %.1f%%** | **%s %.1f%%** |\n", le, lp, fe, fp | |
| }' coverage-report.txt > coverage-rows.txt | |
| { | |
| echo "| Crate | Line Coverage | Function Coverage |" | |
| echo "|:------|:------------:|:-----------------:|" | |
| grep -v '^ZTOTAL' coverage-rows.txt | sort | |
| sed -n 's/^ZTOTAL//p' coverage-rows.txt | |
| } > coverage-summary.md | |
| cat coverage-summary.md >> "${GITHUB_STEP_SUMMARY}" | |
| { | |
| echo "summary<<COVERAGE_EOF" | |
| cat coverage-summary.md | |
| echo "COVERAGE_EOF" | |
| } >> "${GITHUB_OUTPUT}" | |
| - name: Post PR coverage summary | |
| uses: mshick/add-pr-comment@ffd016c7e151d97d69d21a843022fd4cd5b96fe5 # v3.9.0 | |
| with: | |
| message-id: 'coverage-summary' | |
| message: | | |
| ### Coverage Summary | |
| ${{ steps.coverage-summary.outputs.summary }} | |
| [Codecov Report](https://app.codecov.io/gh/${{ github.repository }}/pull/${{ github.event.pull_request.number }}) | [HTML Report](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}#artifacts) | [Workflow Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) | |
| - name: Upload coverage artifacts | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | |
| with: | |
| name: 'Coverage integration tests HTML' | |
| path: ${{ env.OUTPUT_HTML_DIR }} | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 | |
| if: (success() || failure()) | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: 'llvm/codecov.lcov' | |
| slug: ${{ github.repository }} | |
| fail_ci_if_error: false |