Repository: compiler-explorer/compiler-explorer
Pull request #7355 (2025-02-02 11:37:21)

Zig improvements
- Make it easy/possible to run compiler-explorer-zig locally (see first commit)
- Dont strip debug info, add explicit flag for this (second commit)
- Allow filtering library functions. (it doesn't work super great atm, but at least some of the hundreds of thousands of lines get filtered out).


Patch:
diff --git a/etc/config/algol68.defaults.properties b/etc/config/algol68.defaults.properties
index 3ac9d9101..fc62d9ddf 100644
--- a/etc/config/algol68.defaults.properties
+++ b/etc/config/algol68.defaults.properties
@@ -9,6 +9,7 @@ supportsBinaryObject=true
 
 group.ga68.compilers=ga68default
 compiler.ga68default.exe=/usr/bin/ga68
+group.ga68.compilerType=ga68
 compiler.ga68default.name=ga68
 
 tools=clangquerydefault:clangtidydefault:readelf:nm:llvmdwarfdumpdefault
diff --git a/etc/config/c++.amazon.properties b/etc/config/c++.amazon.properties
index d68dc1e7d..2771c4914 100644
--- a/etc/config/c++.amazon.properties
+++ b/etc/config/c++.amazon.properties
@@ -567,6 +567,8 @@ compiler.clang_clangir.notification=ClangIR enabled clang compiler; see <a href=
 compiler.clang_dascandy_contracts.exe=/opt/compiler-explorer/clang-dascandy-contracts-trunk/bin/clang++
 compiler.clang_dascandy_contracts.semver=(dascandy contracts)
 compiler.clang_dascandy_contracts.notification=Dascandy's contracts compiler; see <a href="https://github.com/dascandy/llvm-project/tree/add-contracts" target="_blank" rel="noopener noreferrer">dascandy/llvm-project<sup><small class="fas fa-external-link-alt opens-new-window" title="Opens in a new window"></small></sup></a>
+# Hide dascandy's contracts compiler; per @dascandy himself that folks looking for contracts should use another compiler
+compiler.clang_dascandy_contracts.hidden=true
 compiler.clang_chrisbazley.exe=/opt/compiler-explorer/clang-chrisbazley-trunk/bin/clang++
 compiler.clang_chrisbazley.semver=(Chris Bazley N3089)
 compiler.clang_chrisbazley.notification=Chris Bazley's N3089 compiler; see <a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3089.pdf" target="_blank" rel="noopener noreferrer">dascandy/llvm-project<sup><small class="fas fa-external-link-alt opens-new-window" title="Opens in a new window"></small></sup></a>
diff --git a/etc/config/modula2.amazon.properties b/etc/config/modula2.amazon.properties
index 617162026..f32bb1f15 100644
--- a/etc/config/modula2.amazon.properties
+++ b/etc/config/modula2.amazon.properties
@@ -16,6 +16,7 @@ externalparser=CEAsmParser
 externalparser.exe=/usr/local/bin/asm-parser
 
 group.gm2.compilers=&gm2assert:gm2131:gm2132:gm2133:gm2141:gm2142:gm2snapshot
+group.gm2.compilerType=gm2
 group.gm2.instructionSet=amd64
 group.gm2.isSemVer=true
 group.gm2.groupName=GCC GM2 x86-64
diff --git a/etc/config/modula2.defaults.properties b/etc/config/modula2.defaults.properties
index 4596dad4b..0e60c03e3 100644
--- a/etc/config/modula2.defaults.properties
+++ b/etc/config/modula2.defaults.properties
@@ -11,5 +11,6 @@ supportsLibraryCodeFilter=true
 
 group.gcc.compilers=m2gdefault
 compiler.m2gdefault.exe=/usr/bin/gm2
+group.gcc.compilerType=gm2
 compiler.m2gdefault.name=gcc default
 
diff --git a/etc/config/rust.amazon.properties b/etc/config/rust.amazon.properties
index e9eaa0dbc..3b59d84da 100644
--- a/etc/config/rust.amazon.properties
+++ b/etc/config/rust.amazon.properties
@@ -21,8 +21,10 @@ group.rust.supportsBinary=true
 group.rust.supportsBinaryObject=true
 compiler.r1840.exe=/opt/compiler-explorer/rust-1.84.0/bin/rustc
 compiler.r1840.semver=1.84.0
+compiler.r1840.libPath=${exePath}/../lib/rustlib/x86_64-unknown-linux-gnu/lib
 compiler.r1830.exe=/opt/compiler-explorer/rust-1.83.0/bin/rustc
 compiler.r1830.semver=1.83.0
+compiler.r1830.libPath=${exePath}/../lib/rustlib/x86_64-unknown-linux-gnu/lib
 compiler.r1820.exe=/opt/compiler-explorer/rust-1.82.0/bin/rustc
 compiler.r1820.semver=1.82.0
 compiler.r1810.exe=/opt/compiler-explorer/rust-1.81.0/bin/rustc
@@ -196,9 +198,11 @@ compiler.r100.semver=1.0.0
 compiler.nightly.exe=/opt/compiler-explorer/rust-nightly/bin/rustc
 compiler.nightly.semver=nightly
 compiler.nightly.isNightly=true
+compiler.nightly.libPath=${exePath}/../lib/rustlib/x86_64-unknown-linux-gnu/lib
 compiler.beta.exe=/opt/compiler-explorer/rust-beta/bin/rustc
 compiler.beta.semver=beta
 compiler.beta.isNightly=true
+compiler.beta.libPath=${exePath}/../lib/rustlib/x86_64-unknown-linux-gnu/lib
 
 # gccrs: main development for the Rust GCC Frontend (github)
 # gcc: regular gcc dev/releases
diff --git a/etc/config/spice.amazon.properties b/etc/config/spice.amazon.properties
index d4ebad7a6..858d7b02b 100644
--- a/etc/config/spice.amazon.properties
+++ b/etc/config/spice.amazon.properties
@@ -1,7 +1,7 @@
 compilers=&spice
-defaultCompiler=spice02100
+defaultCompiler=spice02101
 
-group.spice.compilers=spice01902:spice01903:spice01904:spice01905:spice01906:spice02000:spice02001:spice02002:spice02003:spice02004:spice02005:spice02006:spice02100
+group.spice.compilers=spice01902:spice01903:spice01904:spice01905:spice01906:spice02000:spice02001:spice02002:spice02003:spice02004:spice02005:spice02006:spice02100:spice02101
 group.spice.demangler=/opt/compiler-explorer/gcc-14.2.0/bin/c++filt
 group.spice.objdumper=/opt/compiler-explorer/gcc-14.2.0/bin/objdump
 group.spice.isSemVer=true
@@ -41,6 +41,8 @@ compiler.spice02006.exe=/opt/compiler-explorer/spice-0.20.6/spice
 compiler.spice02006.semver=0.20.6
 compiler.spice02100.exe=/opt/compiler-explorer/spice-0.21.0/spice
 compiler.spice02100.semver=0.21.0
+compiler.spice02101.exe=/opt/compiler-explorer/spice-0.21.1/spice
+compiler.spice02101.semver=0.21.1
 
 #################################
 #################################
diff --git a/etc/config/sponsors.yaml b/etc/config/sponsors.yaml
index c68de6d5f..678c58743 100644
--- a/etc/config/sponsors.yaml
+++ b/etc/config/sponsors.yaml
@@ -511,3 +511,9 @@ levels:
       - Sharad Khanna
       - Kim Gräsman
       - Waffles The Dog
+      - Pablo Halpern
+      - Benjamin FrantzDale
+      - Jevin Sweval
+      - woobwoobwoob
+      - Drew Dormann
+      - Ryan Lee
diff --git a/etc/config/zig.defaults.properties b/etc/config/zig.defaults.properties
index 008aa2159..88b0379ac 100644
--- a/etc/config/zig.defaults.properties
+++ b/etc/config/zig.defaults.properties
@@ -1,9 +1,12 @@
-compilers=/usr/bin/zig
+compilers=zig:/usr/bin/zig
 supportsBinary=true
 compilerType=zig
 objdumper=objdump
 versionFlag=version
 binaryHideFuncRe=^(_.*|call_gmon_start|(de)?register_tm_clones|frame_dummy|.*@plt.*)$
+isSemVer=true
+semver=trunk
+supportsLibraryCodeFilter=true
 
 #################################
 #################################
diff --git a/etc/nsjail/user-execution.cfg b/etc/nsjail/user-execution.cfg
index b4fd18b38..0d57f725b 100644
--- a/etc/nsjail/user-execution.cfg
+++ b/etc/nsjail/user-execution.cfg
@@ -145,6 +145,8 @@ mount {
 }
 ###
 
+###
+# NVidia support
 mount {
     src: "/dev/nvidia0"
     dst: "/dev/nvidia0"
@@ -172,10 +174,33 @@ mount {
     is_bind: true
     mandatory: false
 }
+mount {
+    src: "/dev/nvidia-uvm-tools"
+    dst: "/dev/nvidia-uvm-tools"
+    is_bind: true
+    mandatory: false
+}
+
+mount {
+  src: "/sys/module/nvidia"
+  dst: "/sys/module/nvidia"
+  is_bind: true
+  mandatory: false
+}
+
+mount {
+  src: "/sys/module/nvidia_uvm"
+  dst: "/sys/module/nvidia_uvm"
+  is_bind: true
+  mandatory: false
+}
+# End NVidia support
+###
 
 mount {
     dst: "/proc"
     fstype: "proc"
+    rw: true # Needed for NVidia driver
 }
 
 mount {
diff --git a/lib/compilers/ga68.ts b/lib/compilers/ga68.ts
index 22096d85f..43622602b 100644
--- a/lib/compilers/ga68.ts
+++ b/lib/compilers/ga68.ts
@@ -22,10 +22,27 @@
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 // POSSIBILITY OF SUCH DAMAGE.
 
+import {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js';
+import {CompilationEnvironment} from '../compilation-env.js';
 import {GCCCompiler} from './gcc.js';
 
 export class GA68Compiler extends GCCCompiler {
     static override get key() {
         return 'ga68';
     }
+
+    // This should be autodetected, but ga68 driver (same as gm2) does not
+    // behave as the base gcc driver: the probing fails. Until this is fix (if
+    // ever), force the setting here.
+    constructor(info: PreliminaryCompilerInfo, env: CompilationEnvironment) {
+        super(info, env);
+
+        this.compiler.supportsGccDump = true;
+        this.compiler.removeEmptyGccDump = true;
+
+        // this is not showing-up in the --help, so argument parser doesn't
+        // automatically detect the support.
+        this.compiler.stackUsageArg = '-fstack-usage';
+        this.compiler.supportsStackUsageOutput = true;
+    }
 }
diff --git a/lib/compilers/gm2.ts b/lib/compilers/gm2.ts
index a526db473..b447e634b 100644
--- a/lib/compilers/gm2.ts
+++ b/lib/compilers/gm2.ts
@@ -22,10 +22,27 @@
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 // POSSIBILITY OF SUCH DAMAGE.
 
+import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js';
+import {CompilationEnvironment} from '../compilation-env.js';
 import {GCCCompiler} from './gcc.js';
 
 export class GM2Compiler extends GCCCompiler {
     static override get key() {
         return 'gm2';
     }
+
+    // This should be autodetected, but gm2 driver (same as ga68) does not
+    // behave as the base gcc driver: the probing fails. Until this is fix (if
+    // ever), force the setting here.
+    constructor(info: PreliminaryCompilerInfo, env: CompilationEnvironment) {
+        super(info, env);
+
+        this.compiler.supportsGccDump = true;
+        this.compiler.removeEmptyGccDump = true;
+
+        // this is not showing-up in the --help, so argument parser doesn't
+        // automatically detect the support.
+        this.compiler.stackUsageArg = '-fstack-usage';
+        this.compiler.supportsStackUsageOutput = true;
+    }
 }
diff --git a/lib/compilers/zig.ts b/lib/compilers/zig.ts
index 2c529d513..ce8a04303 100644
--- a/lib/compilers/zig.ts
+++ b/lib/compilers/zig.ts
@@ -121,7 +121,8 @@ export class ZigCompiler extends BaseCompiler {
         if (this.self_hosted_cli) {
             // Versions after 0.6.0 use a different command line interface.
             const outputDir = path.dirname(outputFilename);
-            options.push('--cache-dir', outputDir, '--name', name);
+            // -fno-strip: Do not strip debug info
+            options.push('--cache-dir', outputDir, '--name', name, '-fno-strip');
 
             if (filters.binary) {
                 options.push('-femit-bin=' + desiredName);
diff --git a/lib/utils.ts b/lib/utils.ts
index a92f70470..e8f5e6948 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -36,7 +36,7 @@ import _ from 'underscore';
 import type {CacheableValue} from '../types/cache.interfaces.js';
 import {BasicExecutionResult, UnprocessedExecResult} from '../types/execution/execution.interfaces.js';
 import {LanguageKey} from '../types/languages.interfaces.js';
-import type {ResultLine} from '../types/resultline/resultline.interfaces.js';
+import type {Fix, ResultLine} from '../types/resultline/resultline.interfaces.js';
 
 const tabsRe = /\t/g;
 const lineRe = /\r?\n/;
@@ -231,28 +231,64 @@ export function parseOutput(
 }
 
 export function parseRustOutput(lines: string, inputFilename?: string, pathPrefix?: string) {
+    const quickfixes: {re: RegExp; makeFix: (match: string[]) => Fix}[] = [
+        {
+            re: / *help: add `#!\[feature\((.*?)\)]`/,
+            makeFix: ([_, featureName]) => ({
+                title: `Add feature flag \`${featureName}\``,
+                edits: [
+                    {
+                        line: 1,
+                        column: 1,
+                        endline: 1,
+                        endcolumn: 1,
+                        text: `#![feature(${featureName})]\n`,
+                    },
+                ],
+            }),
+        },
+        {
+            re: / *(\d+) *\+ ( *)use (.*?);$/,
+            makeFix: ([_, line, indentation, path]) => ({
+                title: `Add import for \`${path}\``,
+                edits: [
+                    {
+                        line: Number.parseInt(line),
+                        column: 1,
+                        endline: Number.parseInt(line),
+                        endcolumn: 1,
+                        text: `${indentation}use ${path};\n`,
+                    },
+                ],
+            }),
+        },
+    ];
+
     const re = /^\s+-->\s+<source>[(:](\d+)(:?,?(\d+):?)?[):]*\s*(.*)/;
     const result: ResultLine[] = [];
+    let currentDiagnostic: ResultLine | undefined;
     eachLine(lines, line => {
         line = _parseOutputLine(line, inputFilename, pathPrefix);
         if (line !== null) {
             const lineObj: ResultLine = {text: line};
-            const match = filterEscapeSequences(line).match(re);
+            const filteredLine = filterEscapeSequences(line);
+            const match = filteredLine.match(re);
 
             if (match) {
                 const line = Number.parseInt(match[1]);
                 const column = Number.parseInt(match[3] || '0');
 
-                const previous = result.pop();
-                if (previous !== undefined) {
-                    const text = filterEscapeSequences(previous.text);
-                    previous.tag = {
+                currentDiagnostic = result.pop();
+                if (currentDiagnostic !== undefined) {
+                    const text = filterEscapeSequences(currentDiagnostic.text);
+                    currentDiagnostic.tag = {
                         line,
                         column,
                         text,
                         severity: parseSeverity(text),
+                        fixes: [],
                     };
-                    result.push(previous);
+                    result.push(currentDiagnostic);
                 }
 
                 lineObj.tag = {
@@ -262,6 +298,16 @@ export function parseRustOutput(lines: string, inputFilename?: string, pathPrefi
                     severity: 3,
                 };
             }
+
+            if (currentDiagnostic?.tag?.fixes !== undefined) {
+                for (const {re, makeFix} of quickfixes) {
+                    const match = filteredLine.match(re);
+                    if (match) {
+                        currentDiagnostic.tag.fixes.push(makeFix(match));
+                    }
+                }
+            }
+
             result.push(lineObj);
         }
     });
diff --git a/package.json b/package.json
index a31e2694f..6eeb3ea80 100644
--- a/package.json
+++ b/package.json
@@ -144,8 +144,6 @@
     "@rollup/rollup-win32-x64-msvc": "*"
   },
   "scripts": {
-    "ci-test": "vitest run",
-    "ci-lint": "biome check .",
     "cypress": "cypress run",
     "lint": "biome check . --write",
     "lint-check": "biome check .",


Test patch:
diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml
index 620d1bbb2..1da1b0f6b 100644
--- a/.github/workflows/test-and-deploy.yml
+++ b/.github/workflows/test-and-deploy.yml
@@ -22,7 +22,7 @@ jobs:
       - name: Run checks
         run: |
           npm run lint-check
-          npm run ci-test
+          npm run test
           npm run ts-check
           python3 ./etc/scripts/util/propscheck.py
       - uses: codecov/codecov-action@v4
diff --git a/.github/workflows/test-win.yml b/.github/workflows/test-win.yml
index fe016aa59..c1330e75f 100644
--- a/.github/workflows/test-win.yml
+++ b/.github/workflows/test-win.yml
@@ -29,5 +29,5 @@ jobs:
         run: npm run ts-check
         shell: cmd
       - name: Test
-        run: npm run ci-test
+        run: npm run test
         shell: cmd
diff --git a/test/utils-tests.ts b/test/utils-tests.ts
index db7b6c4c1..6737c335e 100644
--- a/test/utils-tests.ts
+++ b/test/utils-tests.ts
@@ -249,7 +249,7 @@ describe('Rust compiler output', () => {
         expect(utils.parseRustOutput('Unrelated\nLine one\n --> bob.rs:1\nUnrelated', 'bob.rs')).toEqual([
             {text: 'Unrelated'},
             {
-                tag: {column: 0, line: 1, text: 'Line one', severity: 3},
+                tag: {column: 0, line: 1, text: 'Line one', severity: 3, fixes: []},
                 text: 'Line one',
             },
             {
@@ -260,7 +260,7 @@ describe('Rust compiler output', () => {
         ]);
         expect(utils.parseRustOutput('Line one\n --> bob.rs:1:5', 'bob.rs')).toEqual([
             {
-                tag: {column: 5, line: 1, text: 'Line one', severity: 3},
+                tag: {column: 5, line: 1, text: 'Line one', severity: 3, fixes: []},
                 text: 'Line one',
             },
             {
@@ -270,7 +270,7 @@ describe('Rust compiler output', () => {
         ]);
         expect(utils.parseRustOutput('Multiple spaces\n   --> bob.rs:1:5', 'bob.rs')).toEqual([
             {
-                tag: {column: 5, line: 1, text: 'Multiple spaces', severity: 3},
+                tag: {column: 5, line: 1, text: 'Multiple spaces', severity: 3, fixes: []},
                 text: 'Multiple spaces',
             },
             {
@@ -283,7 +283,7 @@ describe('Rust compiler output', () => {
     it('replaces all references to input source', () => {
         expect(utils.parseRustOutput('error: Error in bob.rs\n --> bob.rs:1', 'bob.rs')).toEqual([
             {
-                tag: {column: 0, line: 1, text: 'error: Error in <source>', severity: 3},
+                tag: {column: 0, line: 1, text: 'error: Error in <source>', severity: 3, fixes: []},
                 text: 'error: Error in <source>',
             },
             {
@@ -296,7 +296,7 @@ describe('Rust compiler output', () => {
     it('treats <stdin> as if it were the compiler source', () => {
         expect(utils.parseRustOutput('error: <stdin> is sad\n --> <stdin>:120:25', 'bob.rs')).toEqual([
             {
-                tag: {column: 25, line: 120, text: 'error: <source> is sad', severity: 3},
+                tag: {column: 25, line: 120, text: 'error: <source> is sad', severity: 3, fixes: []},
                 text: 'error: <source> is sad',
             },
             {
@@ -318,6 +318,7 @@ describe('Rust compiler output', () => {
                     column: 27,
                     text: 'error[E0425]: cannot find value `x` in this scope',
                     severity: 3,
+                    fixes: [],
                 },
                 text: 'error[\x1B]8;;https://doc.rust-lang.org/error_codes/E0425.html\x07E0425\x1B]8;;\x07]: cannot find value `x` in this scope',
             },
@@ -332,6 +333,84 @@ describe('Rust compiler output', () => {
             },
         ]);
     });
+
+    it('emits quickfixes', () => {
+        expect(utils.parseRustOutput('error\n --> <source>:42:27\n15  + use std::collections::HashMap;')).toEqual([
+            {
+                tag: {
+                    line: 42,
+                    column: 27,
+                    text: 'error',
+                    severity: 3,
+                    fixes: [
+                        {
+                            title: 'Add import for `std::collections::HashMap`',
+                            edits: [
+                                {
+                                    line: 15,
+                                    column: 1,
+                                    endline: 15,
+                                    endcolumn: 1,
+                                    text: 'use std::collections::HashMap;\n',
+                                },
+                            ],
+                        },
+                    ],
+                },
+                text: 'error',
+            },
+            {
+                tag: {
+                    line: 42,
+                    column: 27,
+                    text: '',
+                    severity: 3,
+                },
+                text: ' --> <source>:42:27',
+            },
+            {text: '15  + use std::collections::HashMap;'},
+        ]);
+
+        expect(
+            utils.parseRustOutput(
+                'error\n --> <source>:42:27\n   = help: add `#![feature(num_midpoint_signed)]` to the crate attributes to enable',
+            ),
+        ).toEqual([
+            {
+                tag: {
+                    line: 42,
+                    column: 27,
+                    text: 'error',
+                    severity: 3,
+                    fixes: [
+                        {
+                            title: 'Add feature flag `num_midpoint_signed`',
+                            edits: [
+                                {
+                                    line: 1,
+                                    column: 1,
+                                    endline: 1,
+                                    endcolumn: 1,
+                                    text: '#![feature(num_midpoint_signed)]\n',
+                                },
+                            ],
+                        },
+                    ],
+                },
+                text: 'error',
+            },
+            {
+                tag: {
+                    line: 42,
+                    column: 27,
+                    text: '',
+                    severity: 3,
+                },
+                text: ' --> <source>:42:27',
+            },
+            {text: '   = help: add `#![feature(num_midpoint_signed)]` to the crate attributes to enable'},
+        ]);
+    });
 });
 
 describe('Tool output', () => {