1
1
import * as cp from "node:child_process"
2
+ import { CompilerError , Severity , TactCompiler } from "@server/compiler/TactCompiler"
3
+ import { getDocumentSettings } from "@server/utils/settings"
2
4
3
- export enum Severity {
4
- INFO = 1 ,
5
- LOW = 2 ,
6
- MEDIUM = 3 ,
7
- HIGH = 4 ,
8
- CRITICAL = 5 ,
5
+ export interface MistiJsonOutput {
6
+ readonly kind : "warnings"
7
+ readonly warnings : MistiProjectWarning [ ]
9
8
}
10
9
11
- export interface CompilerError {
12
- severity : Severity
13
- line : number
14
- character : number
15
- message : string
16
- file : string
17
- length ?: number
10
+ export interface MistiProjectWarning {
11
+ readonly projectName ?: string
12
+ readonly warnings : string [ ]
18
13
}
19
14
20
- interface MistiJsonOutput {
21
- kind : "warnings"
22
- warnings : MistiProjectWarning [ ]
23
- }
24
-
25
- interface MistiProjectWarning {
26
- projectName ?: string
27
- warnings : string [ ]
28
- }
29
-
30
- interface MistiWarning {
31
- file : string
32
- line : number | string
33
- col : number | string
34
- detectorId ?: string
35
- severity : string
36
- message : string
15
+ export interface MistiWarning {
16
+ readonly file : string
17
+ readonly line : number | string
18
+ readonly col : number | string
19
+ readonly detectorId ?: string
20
+ readonly severity : string
21
+ readonly message : string
37
22
}
38
23
39
24
export class MistiAnalyzer {
40
25
private static parseCompilerOutput ( output : string ) : CompilerError [ ] {
41
26
const errors : CompilerError [ ] = [ ]
42
27
const jsonStart = output . indexOf ( "{" )
43
- if ( jsonStart === - 1 ) {
44
- return MistiAnalyzer . parseTactCompilerOutput ( output )
28
+ const jsonEnd = output . lastIndexOf ( "}" )
29
+ if ( jsonStart === - 1 || jsonEnd === - 1 ) {
30
+ return TactCompiler . parseCompilerOutput ( output )
45
31
}
46
32
47
- const jsonString = output . slice ( jsonStart )
33
+ const jsonString = output . slice ( jsonStart , jsonEnd + 1 )
48
34
try {
49
35
const jsonData = JSON . parse ( jsonString ) as MistiJsonOutput
50
36
for ( const projectWarning of jsonData . warnings ) {
51
37
if ( ! Array . isArray ( projectWarning . warnings ) ) continue
52
38
53
- for ( const warningStr of projectWarning . warnings ) {
39
+ for ( const warningJSON of projectWarning . warnings ) {
54
40
try {
55
- const warning = JSON . parse ( warningStr ) as MistiWarning
56
- const errorObj : CompilerError = {
57
- file : warning . file ,
41
+ const warning = JSON . parse ( warningJSON ) as MistiWarning
42
+ errors . push ( {
43
+ file : warning . file . trim ( ) ,
58
44
line : Number ( warning . line ) - 1 ,
59
45
character : Number ( warning . col ) - 1 ,
60
- message : `[${ warning . severity . toUpperCase ( ) } ] ${ warning . detectorId ? warning . detectorId + ": " : "" } ${ warning . message } ` ,
46
+ message : `[${ warning . severity . toUpperCase ( ) } ] ${ warning . message } ` ,
47
+ id : warning . detectorId ?? "" ,
61
48
severity : MistiAnalyzer . mapSeverity ( warning . severity ) ,
62
- }
63
- errors . push ( errorObj )
64
- console . info (
65
- `[MistiAnalyzer] Parsed warning from JSON: ${ JSON . stringify ( errorObj ) } ` ,
66
- )
49
+ } )
67
50
} catch {
68
- console . error ( `Failed to parse internal warning: ${ warningStr } ` )
51
+ console . error ( `Failed to parse internal warning: ${ warningJSON } ` )
69
52
}
70
53
}
71
54
}
@@ -75,85 +58,30 @@ export class MistiAnalyzer {
75
58
console . error ( `Failed to parse JSON output: ${ error } ` )
76
59
}
77
60
78
- return MistiAnalyzer . parseTactCompilerOutput ( output )
79
- }
80
-
81
- private static parseTactCompilerOutput ( output : string ) : CompilerError [ ] {
82
- const errors : CompilerError [ ] = [ ]
83
- const lines = output . split ( "\n" )
84
- for ( let i = 0 ; i < lines . length ; i ++ ) {
85
- const line = lines [ i ]
86
- const match =
87
- / ^ ( C o m p i l a t i o n e r r o r : | S y n t a x e r r o r : | E r r o r : ) \s * ( [ ^ : ] + ) : ( \d + ) : ( \d + ) : \s * ( .+ ) $ / . exec (
88
- line ,
89
- )
90
- if ( ! match ) continue
91
- const prefix = match [ 1 ]
92
- const file = match [ 2 ]
93
- const lineNum = match [ 3 ]
94
- const char = match [ 4 ]
95
- const rawMessage = match [ 5 ]
96
- let fullMessage = `${ prefix } ${ file } :${ lineNum } :${ char } : ${ rawMessage } \n`
97
- let contextFound = false
98
- for ( let j = i + 1 ; j < lines . length ; j ++ ) {
99
- const nextLine = lines [ j ]
100
- if (
101
- nextLine . startsWith ( "Compilation error:" ) ||
102
- nextLine . startsWith ( "Syntax error:" ) ||
103
- nextLine . startsWith ( "Error:" )
104
- )
105
- break
106
- if ( nextLine . includes ( "Line" ) || nextLine . includes ( "|" ) || nextLine . includes ( "^" ) ) {
107
- contextFound = true
108
- fullMessage += nextLine + "\n"
109
- i = j
110
- }
111
- }
112
- const error : CompilerError = {
113
- file,
114
- line : Number . parseInt ( lineNum , 10 ) - 1 ,
115
- character : Number . parseInt ( char , 10 ) - 1 ,
116
- message : fullMessage . trim ( ) ,
117
- severity : Severity . HIGH ,
118
- }
119
-
120
- if ( contextFound ) {
121
- const caretLine = fullMessage . split ( "\n" ) . find ( l => l . includes ( "^" ) )
122
- if ( caretLine ) error . length = caretLine . trim ( ) . length
123
- }
124
- errors . push ( error )
125
- console . info ( `[MistiAnalyzer] Parsed error: ${ JSON . stringify ( error ) } ` )
126
- }
127
- return errors
61
+ return TactCompiler . parseCompilerOutput ( output )
128
62
}
129
63
130
64
private static mapSeverity ( sev : string ) : Severity {
131
- switch ( sev . toUpperCase ( ) ) {
132
- case "INFO" : {
133
- return Severity . INFO
134
- }
135
- case "LOW" : {
136
- return Severity . LOW
137
- }
138
- case "MEDIUM" : {
139
- return Severity . MEDIUM
140
- }
141
- case "HIGH" : {
142
- return Severity . HIGH
143
- }
144
- case "CRITICAL" : {
145
- return Severity . CRITICAL
146
- }
147
- default : {
148
- return Severity . HIGH
149
- }
150
- }
65
+ const s = sev . toUpperCase ( )
66
+ if ( s === "INFO" ) return Severity . INFO
67
+ if ( s === "LOW" ) return Severity . LOW
68
+ if ( s === "MEDIUM" ) return Severity . MEDIUM
69
+ if ( s === "HIGH" ) return Severity . HIGH
70
+ if ( s === "CRITICAL" ) return Severity . CRITICAL
71
+ return Severity . HIGH
151
72
}
152
73
153
- public static async analyze ( _filePath : string ) : Promise < CompilerError [ ] > {
74
+ public static async analyze ( filePath : string ) : Promise < CompilerError [ ] > {
75
+ // if (existsSync("./tact.config.json")) {
76
+ // console.warn("Tact config not found")
77
+ // return []
78
+ // }
79
+
80
+ const settings = await getDocumentSettings ( `file://${ filePath } ` )
81
+
154
82
return new Promise ( ( resolve , reject ) => {
155
83
const process = cp . exec (
156
- `npx misti ./tact.config.json --output-format json` ,
84
+ `${ settings . linters . misti . binPath } ./tact.config.json --output-format json` ,
157
85
( _error , stdout , stderr ) => {
158
86
const output = stdout + "\n" + stderr
159
87
const errors = this . parseCompilerOutput ( output )
0 commit comments