Skip to content

Commit

Permalink
feat(lint): add clang-tidy support
Browse files Browse the repository at this point in the history
  • Loading branch information
angristan committed Jul 27, 2021
1 parent c9a3a28 commit 5e94a79
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 6 deletions.
7 changes: 3 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@
"rxjs": "^7.2.0",
"typedjson": "^1.7.0",
"typeorm": "^0.2.34",
"uuid": "^3.4.0"
"uuid": "^3.4.0",
"yaml": "^1.10.2"
},
"devDependencies": {
"@nestjs/cli": "8.0.2",
Expand Down
101 changes: 100 additions & 1 deletion src/lint/lint.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// `yaml` is not type safe 🤷‍♂️
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import child_process from 'child_process';
import fs from 'fs';
import * as uuid from 'uuid';
import YAML from 'yaml';
import { ConvertToGolangCILintOutput } from './linters/golangcilint';
import { ConvertToPylintOutput } from './linters/pylint';

Expand All @@ -26,7 +31,24 @@ export class LintService {
output = output.substring(0, output.length - 1);
const pylintOutput = ConvertToPylintOutput.toPylintOutput(output);
if (pylintOutput) {
// Remove one point to the score per violation
/*
Example output:
[
{
"type": "convention",
"module": "fib",
"obj": "",
"line": 1,
"column": 0,
"path": "fib.py",
"symbol": "missing-module-docstring",
"message": "Missing module docstring",
"message-id": "C0114"
},
...
Remove one point to the score per violation
*/
return 100 - pylintOutput.length;
}
} catch (e) {
Expand Down Expand Up @@ -63,6 +85,31 @@ export class LintService {
const lintOuput =
ConvertToGolangCILintOutput.toGolangCILintOutput(output);
if (lintOuput) {
/*
Example output:
{
"Issues": [
{
"FromLinter": "errcheck",
"Text": "Error return value of `http.ListenAndServe` is not checked",
"Severity": "",
"SourceLines": [
"\thttp.ListenAndServe(\":9567\", nil)"
],
"Replacement": null,
"Pos": {
"Filename": "http.go",
"Offset": 417,
"Line": 27,
"Column": 21
},
"ExpectNoLint": false,
"ExpectedNoLintLinter": ""
}
],
...
*/
if (lintOuput.issues) {
// Remove one point to the score per violation
return 100 - lintOuput.issues.length;
Expand All @@ -76,4 +123,56 @@ export class LintService {

throw new InternalServerErrorException();
}

lintCpp(code: string): number {
// clang-tidy doesn't support stdin
const path = `/tmp/codebench_${uuid.v4()}.cpp`;
const outputPath = `${path}.yaml`;
try {
fs.writeFileSync(path, code);
} catch (e) {
throw new InternalServerErrorException(e);
}

const result = child_process.spawnSync('clang-tidy', [
`-export-fixes=${outputPath}`,
path,
'--',
'-Wall',
'-std=c++11',
'-x',
'c++',
]);

if (result.error) {
throw new InternalServerErrorException(result.error);
}

try {
if (fs.existsSync(outputPath)) {
const file = fs.readFileSync(`${path}.yaml`, 'utf8');
const fixes: any = YAML.parse(file);
if (fixes) {
/*
Example file:
---
MainSourceFile: /root/fib.cpp
Diagnostics:
- DiagnosticName: clang-diagnostic-unused-variable
Message: 'unused variable ''d'''
FileOffset: 142
FilePath: /root/fib.cpp
Replacements:
...
*/
if (fixes.Diagnostics) {
return 100 - fixes.Diagnostics.length;
}
}
}
return 100;
} catch (e) {
throw new InternalServerErrorException(e);
}
}
}
1 change: 1 addition & 0 deletions src/submissions/submissions.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export class SubmissionsController {
break;
case 'cpp':
qualityScore = this.qualityService.run(createSubmissionDTO.code, 'cpp');
lintScore.score = this.lintService.lintCpp(createSubmissionDTO.code);
break;
case 'go':
qualityScore = this.qualityService.run(createSubmissionDTO.code, 'go');
Expand Down

0 comments on commit 5e94a79

Please sign in to comment.