Skip to content

Commit 5e94a79

Browse files
committed
feat(lint): add clang-tidy support
1 parent c9a3a28 commit 5e94a79

File tree

4 files changed

+106
-6
lines changed

4 files changed

+106
-6
lines changed

package-lock.json

Lines changed: 3 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@
7171
"rxjs": "^7.2.0",
7272
"typedjson": "^1.7.0",
7373
"typeorm": "^0.2.34",
74-
"uuid": "^3.4.0"
74+
"uuid": "^3.4.0",
75+
"yaml": "^1.10.2"
7576
},
7677
"devDependencies": {
7778
"@nestjs/cli": "8.0.2",

src/lint/lint.service.ts

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
// `yaml` is not type safe 🤷‍♂️
2+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
3+
/* eslint-disable @typescript-eslint/no-explicit-any */
4+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
15
import { Injectable, InternalServerErrorException } from '@nestjs/common';
26
import child_process from 'child_process';
37
import fs from 'fs';
48
import * as uuid from 'uuid';
9+
import YAML from 'yaml';
510
import { ConvertToGolangCILintOutput } from './linters/golangcilint';
611
import { ConvertToPylintOutput } from './linters/pylint';
712

@@ -26,7 +31,24 @@ export class LintService {
2631
output = output.substring(0, output.length - 1);
2732
const pylintOutput = ConvertToPylintOutput.toPylintOutput(output);
2833
if (pylintOutput) {
29-
// Remove one point to the score per violation
34+
/*
35+
Example output:
36+
[
37+
{
38+
"type": "convention",
39+
"module": "fib",
40+
"obj": "",
41+
"line": 1,
42+
"column": 0,
43+
"path": "fib.py",
44+
"symbol": "missing-module-docstring",
45+
"message": "Missing module docstring",
46+
"message-id": "C0114"
47+
},
48+
...
49+
50+
Remove one point to the score per violation
51+
*/
3052
return 100 - pylintOutput.length;
3153
}
3254
} catch (e) {
@@ -63,6 +85,31 @@ export class LintService {
6385
const lintOuput =
6486
ConvertToGolangCILintOutput.toGolangCILintOutput(output);
6587
if (lintOuput) {
88+
/*
89+
Example output:
90+
91+
{
92+
"Issues": [
93+
{
94+
"FromLinter": "errcheck",
95+
"Text": "Error return value of `http.ListenAndServe` is not checked",
96+
"Severity": "",
97+
"SourceLines": [
98+
"\thttp.ListenAndServe(\":9567\", nil)"
99+
],
100+
"Replacement": null,
101+
"Pos": {
102+
"Filename": "http.go",
103+
"Offset": 417,
104+
"Line": 27,
105+
"Column": 21
106+
},
107+
"ExpectNoLint": false,
108+
"ExpectedNoLintLinter": ""
109+
}
110+
],
111+
...
112+
*/
66113
if (lintOuput.issues) {
67114
// Remove one point to the score per violation
68115
return 100 - lintOuput.issues.length;
@@ -76,4 +123,56 @@ export class LintService {
76123

77124
throw new InternalServerErrorException();
78125
}
126+
127+
lintCpp(code: string): number {
128+
// clang-tidy doesn't support stdin
129+
const path = `/tmp/codebench_${uuid.v4()}.cpp`;
130+
const outputPath = `${path}.yaml`;
131+
try {
132+
fs.writeFileSync(path, code);
133+
} catch (e) {
134+
throw new InternalServerErrorException(e);
135+
}
136+
137+
const result = child_process.spawnSync('clang-tidy', [
138+
`-export-fixes=${outputPath}`,
139+
path,
140+
'--',
141+
'-Wall',
142+
'-std=c++11',
143+
'-x',
144+
'c++',
145+
]);
146+
147+
if (result.error) {
148+
throw new InternalServerErrorException(result.error);
149+
}
150+
151+
try {
152+
if (fs.existsSync(outputPath)) {
153+
const file = fs.readFileSync(`${path}.yaml`, 'utf8');
154+
const fixes: any = YAML.parse(file);
155+
if (fixes) {
156+
/*
157+
Example file:
158+
---
159+
MainSourceFile: /root/fib.cpp
160+
Diagnostics:
161+
- DiagnosticName: clang-diagnostic-unused-variable
162+
Message: 'unused variable ''d'''
163+
FileOffset: 142
164+
FilePath: /root/fib.cpp
165+
Replacements:
166+
...
167+
*/
168+
if (fixes.Diagnostics) {
169+
return 100 - fixes.Diagnostics.length;
170+
}
171+
}
172+
}
173+
return 100;
174+
} catch (e) {
175+
throw new InternalServerErrorException(e);
176+
}
177+
}
79178
}

src/submissions/submissions.controller.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class SubmissionsController {
4545
break;
4646
case 'cpp':
4747
qualityScore = this.qualityService.run(createSubmissionDTO.code, 'cpp');
48+
lintScore.score = this.lintService.lintCpp(createSubmissionDTO.code);
4849
break;
4950
case 'go':
5051
qualityScore = this.qualityService.run(createSubmissionDTO.code, 'go');

0 commit comments

Comments
 (0)