Skip to content

Commit 4b9c080

Browse files
committed
C++,Rustのdiagnostic出力に対応
1 parent 1e9160e commit 4b9c080

4 files changed

Lines changed: 549 additions & 68 deletions

File tree

packages/runtime/src/wandbox/cpp.ts

Lines changed: 126 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1-
import { ReplOutput } from "../interface";
1+
import {
2+
Diagnostic,
3+
DiagnosticFrame,
4+
DiagnosticSeverity,
5+
ReplOutput,
6+
} from "../interface";
27
import { compileAndRun, CompilerInfo, SelectedCompiler } from "./api";
38

49
import _stacktrace_cpp from "./cpp/_stacktrace.cpp?raw";
510

11+
const GCC_DIAG_REGEX =
12+
/^(?:.*\/)?([^:\n]+):(\d+):(?:(\d+):)?\s*(fatal error|error|warning|note):\s*(.*)$/;
13+
const LD_DIAG_REGEX =
14+
/^(?:(?:\/usr\/bin\/ld:\s+)?(?:.*\/)?([^:\n]+)):(\d+):(?:\([^)]+\):)?\s*(undefined reference to .*)$/;
15+
616
export function selectCppCompiler(
717
compilerList: CompilerInfo[]
818
): SelectedCompiler {
@@ -73,8 +83,8 @@ export function selectCppCompiler(
7383
}
7484

7585
// その他オプション
76-
options.compilerOptionsRaw.push("-g");
77-
commandline.push("-g");
86+
options.compilerOptionsRaw.push("-g", "-no-pie");
87+
commandline.push("-g", "-no-pie");
7888

7989
options.getCommandlineStr = (filenames: string[]) => {
8090
return [...commandline, ...filenames, "&&", "./a.out"].join(" ");
@@ -87,13 +97,14 @@ export async function cppRunFiles(
8797
options: SelectedCompiler,
8898
files: Record<string, string | undefined>,
8999
filenames: string[],
90-
onOutput: (output: ReplOutput) => void
100+
onOutput: (output: ReplOutput) => void,
101+
onDiagnostic?: (diagnostic: Diagnostic) => void
91102
): Promise<void> {
92-
// Constants for stack trace processing
93-
const WANDBOX_PATH = "/home/wandbox";
94-
95103
// Track state for processing stack traces
96104
let inStackTrace = false;
105+
let signal = "";
106+
let exceptionMessage = "";
107+
const runtimeFrames: DiagnosticFrame[] = [];
97108

98109
await compileAndRun(
99110
{
@@ -108,14 +119,90 @@ export async function cppRunFiles(
108119
(event) => {
109120
const { ndjsonType, output } = event;
110121

122+
// Parse compiler messages for diagnostics
123+
if (ndjsonType === "CompilerMessageE") {
124+
const gccMatch = GCC_DIAG_REGEX.exec(output.message);
125+
if (gccMatch) {
126+
const rawFilename = gccMatch[1].replace(/^\.\//, "");
127+
if (
128+
rawFilename !== "_stacktrace.cpp" &&
129+
!rawFilename.startsWith("<") &&
130+
!rawFilename.includes("/include/")
131+
) {
132+
const lineNum = parseInt(gccMatch[2], 10);
133+
const colNum = gccMatch[3] ? parseInt(gccMatch[3], 10) : undefined;
134+
const sev = gccMatch[4];
135+
const msg = gccMatch[5];
136+
137+
let severity: DiagnosticSeverity = "error";
138+
if (sev === "warning") severity = "warning";
139+
else if (sev === "note") severity = "info";
140+
141+
onDiagnostic?.({
142+
frames: [
143+
{
144+
filename: rawFilename,
145+
startLineNumber: lineNum,
146+
startColumn: colNum,
147+
},
148+
],
149+
message: msg,
150+
severity,
151+
});
152+
}
153+
} else {
154+
const ldMatch = LD_DIAG_REGEX.exec(output.message);
155+
if (ldMatch) {
156+
const rawFilename = ldMatch[1].replace(/^\.\//, "");
157+
if (
158+
rawFilename !== "_stacktrace.cpp" &&
159+
!rawFilename.startsWith("<") &&
160+
!rawFilename.includes("/include/")
161+
) {
162+
const lineNum = parseInt(ldMatch[2], 10);
163+
const msg = ldMatch[3];
164+
onDiagnostic?.({
165+
frames: [
166+
{
167+
filename: rawFilename,
168+
startLineNumber: lineNum,
169+
},
170+
],
171+
message: msg,
172+
severity: "error",
173+
});
174+
}
175+
}
176+
}
177+
}
178+
179+
// Check for exception / terminate message in stderr
180+
if (ndjsonType === "StdErr") {
181+
if (output.message.includes("what():")) {
182+
const idx = output.message.indexOf("what():");
183+
exceptionMessage = output.message.slice(idx + 7).trim();
184+
} else if (
185+
output.message.includes("terminate called after throwing an instance of")
186+
) {
187+
const m =
188+
/terminate called after throwing an instance of '([^']+)'/.exec(
189+
output.message
190+
);
191+
if (m && !exceptionMessage) {
192+
exceptionMessage = m[1];
193+
}
194+
}
195+
}
196+
111197
// Check for signal marker in stderr
112198
if (
113199
ndjsonType === "StdErr" &&
114200
output.message.startsWith("#!my_code_signal:")
115201
) {
202+
signal = output.message.slice(17).trim();
116203
onOutput({
117204
type: "error",
118-
message: output.message.slice(17),
205+
message: signal,
119206
});
120207
return;
121208
}
@@ -135,12 +222,28 @@ export async function cppRunFiles(
135222

136223
// Process stack trace lines
137224
if (inStackTrace && ndjsonType === "StdErr") {
138-
// Filter to show only user source code
139-
if (output.message.includes(WANDBOX_PATH)) {
140-
onOutput({
141-
type: "trace",
142-
message: output.message.replace(`${WANDBOX_PATH}/`, ""),
143-
});
225+
const m = /\sat\s+(?:.*\/)?([^:\s]+):(\d+)/.exec(output.message);
226+
if (
227+
m &&
228+
!output.message.includes("/boost/") &&
229+
!output.message.includes("/include/") &&
230+
!output.message.includes("/opt/wandbox/")
231+
) {
232+
const filename = m[1].replace(/^\.\//, "");
233+
if (filename !== "_stacktrace.cpp") {
234+
const cleanedMessage = output.message.replace(
235+
/\s+at\s+.*\/([^\/]+:\d+.*)$/,
236+
" at $1"
237+
);
238+
onOutput({
239+
type: "trace",
240+
message: cleanedMessage,
241+
});
242+
runtimeFrames.push({
243+
filename,
244+
startLineNumber: parseInt(m[2], 10),
245+
});
246+
}
144247
}
145248
return;
146249
}
@@ -149,4 +252,13 @@ export async function cppRunFiles(
149252
onOutput(output);
150253
}
151254
);
255+
256+
if (runtimeFrames.length > 0) {
257+
const message = exceptionMessage || signal || "Runtime error";
258+
onDiagnostic?.({
259+
frames: runtimeFrames,
260+
message,
261+
severity: "error",
262+
});
263+
}
152264
}

packages/runtime/src/wandbox/runtime.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ export function WandboxProvider({ children }: { children: ReactNode }) {
8989
filenames: string[],
9090
files: Readonly<Record<string, string>>,
9191
onOutput: (output: ReplOutput | UpdatedFile) => void,
92-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
93-
_onDiagnostic?: (diagnostic: Diagnostic) => void
92+
onDiagnostic?: (diagnostic: Diagnostic) => void
9493
) => {
9594
if (!selectedCompiler) {
9695
onOutput({ type: "error", message: "Wandbox is not ready yet." });
@@ -103,15 +102,17 @@ export function WandboxProvider({ children }: { children: ReactNode }) {
103102
selectedCompiler.cpp,
104103
files,
105104
filenames,
106-
onOutput
105+
onOutput,
106+
onDiagnostic
107107
);
108108
break;
109109
case "rust":
110110
await rustRunFiles(
111111
selectedCompiler.rust,
112112
files,
113113
filenames,
114-
onOutput
114+
onOutput,
115+
onDiagnostic
115116
);
116117
break;
117118
default:

0 commit comments

Comments
 (0)