feat(题目): 运行时错误告诉学生第几行、为什么出错(Python 看回溯,C 看信号),不泄露测试数据

原来学生只看到「运行时错误」四个字:主判题 output: false,Python 回溯没回来;
C 的信号值在 info 里,而 info 只给管理员。

- Python 判出 RE 时,拿第一个失败测试点单独重跑一次(内联测试点 + output: true),
  从回溯取行号(扣掉模板前置行)、异常类型和按句式归的类别。主判题仍不开 output,
  免得每个点最多 16MB 的输出回到 worker
- C / C++ 取失败点的信号和退出码(段错误提示 scanf 漏 &、return 非 0 等)
- 写进 statistic_info.runtime_error;不存异常原文(里面常带测试点输入),名字只在
  出现在学生代码里时才存——raise Exception(input()) 实测只落下类型
- 前端按类型翻成中文、摆出出错那行并在编辑器标红;比赛提交不诊断(ACM 只报对错)
- 没有诊断的 RE 不再说「代码格式写错了」

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-27 22:01:30 +08:00
1 parent 2a4405d2ea
commit db70ab62e8
7 files changed
+391 -11

No files matched your search

@@ -0,0 +1,67 @@
<script setup lang="ts">
import { clearErrorMark, showErrorMark } from "oj/problem/utils/errorMark"
import { explainRuntimeError, type RuntimeErrorInfo } from "oj/problem/utils/runtimeError"
/**
* 运行时错误的中文说明卡片(诊断由后端 judge/runtime-diagnosis.ts 给)。
* 知道是第几行的,就把那一行代码摆出来,并在编辑器里标红;卸掉时清掉标记。
*/
const props = defineProps<{
info: RuntimeErrorInfo
/** 这次提交的代码,用来摆出出错的那一行 */
code: string
}>()
const message = computed(() => explainRuntimeError(props.info))
const sourceLine = computed(() => {
const line = props.info.line
if (!line) return null
const text = props.code.split("\n")[line - 1]
return text?.trim() ? text.trim() : null
})
watch(
() => props.info.line,
(line) => {
if (line) showErrorMark(line, null, null)
else clearErrorMark()
},
{ immediate: true },
)
onUnmounted(clearErrorMark)
</script>
<template>
<n-card embedded class="explain-card">
<n-flex vertical :size="12">
<div class="explain">
<b v-if="info.line">第 {{ info.line }} 行运行时出错:</b>{{ message }}
</div>
<pre v-if="sourceLine" class="explain-code">{{ sourceLine }}</pre>
</n-flex>
</n-card>
</template>
<style scoped>
/* 结果弹窗不限宽,长句子不折行会把弹窗撑出屏幕 */
.explain-card {
max-width: 560px;
}
.explain {
font-size: 16px;
line-height: 1.7;
}
.explain-code {
margin: 0;
padding: 8px 12px;
border-radius: 4px;
font-size: 15px;
white-space: pre-wrap;
word-break: break-all;
background-color: rgba(208, 48, 80, 0.12);
}
</style>