33
44import * as lsp from "vscode-languageserver"
55
6- import type { FuncFile } from "@server/languages/func/psi/FuncFile"
7- import { Node } from 'web-tree-sitter'
8- import { UnusedInspection } from "./UnusedInspection"
9- import { Inspection , InspectionIds } from "./Inspection"
10- import { RecursiveVisitor } from "@server/visitor/visitor" ;
11- import { Func } from "@server/languages/func/psi/Decls" ;
12- import { asLspRange } from "@server/utils/position"
13- import { closestNamedSibling , parentOfType , parentOfTypeWithCb } from "@server/psi/utils"
14- import { Referent } from "@server/languages/func/psi/Referent"
15- import { FunCBindingResolver } from "@server/languages/func/psi/BindingResolver" ;
16- import { FUNC_PARSED_FILES_CACHE } from "@server/files"
6+ import { Node } from "web-tree-sitter"
177
8+ import type { FuncFile } from "@server/languages/func/psi/FuncFile"
9+
10+ import { RecursiveVisitor } from "@server/visitor/visitor"
11+ import { Func } from "@server/languages/func/psi/Decls"
12+ import { asLspRange } from "@server/utils/position"
13+ import { closestNamedSibling , parentOfType , parentOfTypeWithCb } from "@server/psi/utils"
14+ import { Referent } from "@server/languages/func/psi/Referent"
15+ import { FunCBindingResolver } from "@server/languages/func/psi/BindingResolver"
16+ import { FUNC_PARSED_FILES_CACHE } from "@server/files"
17+
18+ import { UnusedInspection } from "./UnusedInspection"
19+ import { Inspection , InspectionIds } from "./Inspection"
1820
1921export class UnusedImpureInspection extends UnusedInspection implements Inspection {
20- public readonly id : "unused-impure" = InspectionIds . UNUSED_IMPURE ;
21-
22- private impureMap : Map < string , Func > ;
23- private dropableMap : Map < string , Func > ;
24- private resultsCache : Map < string , boolean > ;
25- private impureBuiltins : Set < string > ;
26-
27- constructor ( ) {
28- super ( ) ;
29- this . resultsCache = new Map ( ) ;
30- this . impureMap = new Map ( ) ;
31- this . dropableMap = new Map ( ) ;
22+ public readonly id : "unused-impure" = InspectionIds . UNUSED_IMPURE
23+
24+ private readonly impureMap : Map < string , Func >
25+ private readonly dropableMap : Map < string , Func >
26+ private readonly resultsCache : Map < string , boolean >
27+ private readonly impureBuiltins : Set < string >
28+
29+ public constructor ( ) {
30+ super ( )
31+ this . resultsCache = new Map ( )
32+ this . impureMap = new Map ( )
33+ this . dropableMap = new Map ( )
3234 this . impureBuiltins = new Set ( [
3335 "throw" ,
3436 "throw_if" ,
@@ -37,120 +39,143 @@ export class UnusedImpureInspection extends UnusedInspection implements Inspecti
3739 "throw_arg_op" ,
3840 "throw_arg_unless" ,
3941 "~dump" ,
40- "~strdump"
41- ] ) ;
42+ "~strdump" ,
43+ ] )
4244 }
4345
44- private getCallDef ( call : Node , mode : ' dropable' | ' impure' = ' dropable' ) {
45- let callDef : Func | undefined ;
46- const lookupMap = mode == ' dropable' ? this . dropableMap : this . impureMap ;
47- const callType = call . type ;
46+ private getCallDef ( call : Node , mode : " dropable" | " impure" = " dropable" ) : Func | undefined {
47+ let callDef : Func | undefined
48+ const lookupMap = mode == " dropable" ? this . dropableMap : this . impureMap
49+ const callType = call . type
4850 if ( callType == "function_application" ) {
49- const funcIdentifier = call . childForFieldName ( "callee" ) ;
51+ const funcIdentifier = call . childForFieldName ( "callee" )
5052 if ( funcIdentifier ) {
51- callDef = lookupMap . get ( funcIdentifier . text ) ;
53+ callDef = lookupMap . get ( funcIdentifier . text )
5254 }
5355 } else if ( callType == "method_call" ) {
54- const funcIdentifier = call . childForFieldName ( "method_name" ) ;
56+ const funcIdentifier = call . childForFieldName ( "method_name" )
5557 if ( funcIdentifier ) {
56- const methodName = funcIdentifier . text ;
57- callDef = lookupMap . get ( methodName ) ;
58- if ( ! callDef ) {
59- callDef = lookupMap . get ( "~" + methodName ) ;
60- }
58+ const methodName = funcIdentifier . text
59+ callDef = lookupMap . get ( methodName )
60+ callDef ??= lookupMap . get ( "~" + methodName )
6161 }
6262 } else {
63- throw new Error ( `Unsupported call type ${ call } ` )
63+ throw new Error ( `Unsupported call type ${ call . toString ( ) } ` )
6464 }
6565
66- return callDef ;
66+ return callDef
6767 }
68- private isImpureBuiltIn ( call : Node ) {
68+ private isImpureBuiltIn ( call : Node ) : boolean {
69+ let calleeName : Node | null
6970 switch ( call . type ) {
70- case "function_application" :
71- return this . impureBuiltins . has ( call . childForFieldName ( "callee" ) ! . text ) ;
72- case "method_call" :
73- return this . impureBuiltins . has ( call . childForFieldName ( "method_name" ) ! . text ) ;
71+ case "function_application" : {
72+ calleeName = call . childForFieldName ( "callee" )
73+ if ( calleeName ) {
74+ return this . impureBuiltins . has ( calleeName . text )
75+ }
76+ break
77+ }
78+ case "method_call" : {
79+ calleeName = call . childForFieldName ( "method_name" )
80+ if ( calleeName ) {
81+ return this . impureBuiltins . has ( calleeName . text )
82+ }
83+ }
7484 }
75- return false ;
85+ return false
7686 }
7787
78- private isCall ( call : Node ) {
79- return call . type == "function_application" || call . type == "method_call" ;
88+ private isCall ( call : Node ) : boolean {
89+ return call . type == "function_application" || call . type == "method_call"
8090 }
8191
82- private setCache ( node : Node , result : boolean ) {
83- const cacheKey = [ node . startPosition . row , node . startPosition . column , node . endPosition . row , node . endPosition . column ] . join ( ':' ) ;
84- this . resultsCache . set ( cacheKey , result ) ;
92+ private setCache ( node : Node , result : boolean ) : void {
93+ const cacheKey = [
94+ node . startPosition . row ,
95+ node . startPosition . column ,
96+ node . endPosition . row ,
97+ node . endPosition . column ,
98+ ] . join ( ":" )
99+ this . resultsCache . set ( cacheKey , result )
85100 }
86- private getCache ( node : Node ) {
87- const cacheKey = [ node . startPosition . row , node . startPosition . column , node . endPosition . row , node . endPosition . column ] . join ( ':' ) ;
88- return this . resultsCache . get ( cacheKey ) ;
101+ private getCache ( node : Node ) : boolean | undefined {
102+ const cacheKey = [
103+ node . startPosition . row ,
104+ node . startPosition . column ,
105+ node . endPosition . row ,
106+ node . endPosition . column ,
107+ ] . join ( ":" )
108+ return this . resultsCache . get ( cacheKey )
89109 }
90110
91111 protected checkFile ( file : FuncFile , diagnostics : lsp . Diagnostic [ ] ) : void {
92112 // Populate impure functions map
93113 FUNC_PARSED_FILES_CACHE . forEach ( parsedFile => {
94114 parsedFile . getFunctions ( ) . forEach ( f => {
95115 if ( f . isImpure ) {
96- this . impureMap . set ( f . name ( true ) , f ) ;
116+ this . impureMap . set ( f . name ( true ) , f )
97117 } else {
98- this . dropableMap . set ( f . name ( true ) , f ) ;
118+ this . dropableMap . set ( f . name ( true ) , f )
99119 }
100- } ) ;
120+ } )
101121 } )
102- const bindResolver = new FunCBindingResolver ( file ) ;
122+ const bindResolver = new FunCBindingResolver ( file )
103123 RecursiveVisitor . visit ( file . rootNode , ( node ) : boolean => {
104124 if ( ! this . isCall ( node ) ) {
105- return true ;
125+ return true
106126 }
107- let willDrop = false ;
127+ let willDrop = false
108128 // Skip impure builtins calls
109129 if ( this . isImpureBuiltIn ( node ) ) {
110- return true ;
130+ return true
111131 }
112132 // const droppableDef = this.getCallDef(node)
113133 if ( this . checkCallWillDrop ( node , file , bindResolver ) ) {
114- willDrop = true ;
115- const range = asLspRange ( node ) ;
134+ willDrop = true
135+ const range = asLspRange ( node )
116136 diagnostics . push ( {
117137 severity : lsp . DiagnosticSeverity . Error ,
118- code : ' unused-impure' ,
138+ code : " unused-impure" ,
119139 range,
120140 message : "This call will be dropped due to lack of impure specifier!" ,
121- source : "func"
141+ source : "func" ,
122142 } )
123143 }
124- this . setCache ( node , willDrop ) ;
125- return true ;
144+ this . setCache ( node , willDrop )
145+ return true
126146 } )
127147 }
128148
129- private checkCallWillDrop ( node : Node , file : FuncFile , bindResolver : FunCBindingResolver ) {
130- const cachedRes = this . getCache ( node ) ;
149+ private checkCallWillDrop (
150+ node : Node ,
151+ file : FuncFile ,
152+ bindResolver : FunCBindingResolver ,
153+ ) : boolean {
154+ const cachedRes = this . getCache ( node )
131155 if ( cachedRes !== undefined ) {
132- return cachedRes ;
156+ return cachedRes
133157 }
134158
135- const definition = this . getCallDef ( node , ' dropable' )
159+ const definition = this . getCallDef ( node , " dropable" )
136160
137161 if ( ! definition ) {
138162 // If no dropable def found, check that impure is implicit just in case
139- const willDrop = ! ( this . getCallDef ( node , ' impure' ) || this . isImpureBuiltIn ( node ) )
140- this . setCache ( node , willDrop ) ;
141- return willDrop ;
163+ const willDrop = ! ( this . getCallDef ( node , " impure" ) ?? this . isImpureBuiltIn ( node ) )
164+ this . setCache ( node , willDrop )
165+ return willDrop
142166 }
143167
144- const returnExp = definition . returnType ( ) ;
168+ const returnExp = definition . returnType ( )
145169 if ( returnExp !== null ) {
146170 // If return type of a function is empty tensor - check no more.
147- if ( returnExp . node . text == '()' ) {
148- return true ;
171+ if ( returnExp . node . text == "()" ) {
172+ return true
149173 }
150174 }
151- const expressionParent = parentOfTypeWithCb < { parent : Node , origin : Node } > ( node ,
175+ const expressionParent = parentOfTypeWithCb < { parent : Node ; origin : Node } > (
176+ node ,
152177 ( parent , origin ) => {
153- return { parent, origin }
178+ return { parent, origin}
154179 } ,
155180 "expression_statement" ,
156181 "return_statement" ,
@@ -160,13 +185,13 @@ export class UnusedImpureInspection extends UnusedInspection implements Inspecti
160185 "while_statement" ,
161186 "do_while_statement" ,
162187 "repeat_statement" ,
163- "return_statement"
164- ) ;
188+ "return_statement" ,
189+ )
165190 if ( ! expressionParent ) {
166191 // Could happen in incomplete code
167- return false ;
192+ return false
168193 }
169- const parentType = expressionParent . parent . type ;
194+ const parentType = expressionParent . parent . type
170195 // If call is in the block_statement of any kind, it will be a child of expression_statement
171196 // Otherwise it is in condition block of if/while/do while
172197 // Or in arguments clause of other function_application/method_call
@@ -175,58 +200,62 @@ export class UnusedImpureInspection extends UnusedInspection implements Inspecti
175200 return this . checkCallWillDrop ( expressionParent . parent , file , bindResolver )
176201 }
177202 // If expression is in condition or return statement it will not be dropped
178- return false ;
203+ return false
179204 }
180205
181206 // We are in the expression expression_statement
182207 // Bind the values from the expression
183- const resolvedBinding = bindResolver . resolve ( expressionParent . parent ) ;
208+ const resolvedBinding = bindResolver . resolve ( expressionParent . parent )
184209 // If no lvalue, non-impure call will drop
185- if ( resolvedBinding . bindings . size == 0 ) {
186- return true ;
210+ if ( resolvedBinding . bindings . size === 0 ) {
211+ return true
187212 }
188213 // If no identifiers referenced in lvalue, means those are whole type and will be dropped
189214 // const affectedIdentifiers = resolvedBinding.bindings.values()
190215
191- for ( let boundValue of resolvedBinding . bindings . values ( ) ) {
216+ for ( const boundValue of resolvedBinding . bindings . values ( ) ) {
192217 // Find references to the bound variables from below the current expression.
193- const references = new Referent ( boundValue . identifier , file ) . findReferences ( { limit : Infinity } ) . filter (
194- ref => ref . node . startIndex >= expressionParent . parent . endIndex
195- ) ;
218+ const references = new Referent ( boundValue . identifier , file )
219+ . findReferences ( { limit : Infinity } )
220+ . filter ( ref => ref . node . startIndex >= expressionParent . parent . endIndex )
196221 // Has to be referenced in non impure call, conditional or return statement to not drop
197- for ( let ref of references ) {
198- const parent = parentOfType ( ref . node ,
222+ for ( const ref of references ) {
223+ const parent = parentOfType (
224+ ref . node ,
199225 "expression_statement" , // But don't go above expression_statement
200226 "function_application" ,
201227 "method_call" ,
202228 "if_statement" ,
203229 "while_statement" ,
204230 "do_while_statement" ,
205231 "repeat_statement" ,
206- "return_statement"
232+ "return_statement" ,
207233 )
208234 if ( ! parent ) {
209- continue ;
235+ continue
210236 }
211237 if ( parent . type !== "expression_statement" ) {
212- let willDrop = false ;
238+ let willDrop = false
213239 if ( this . isCall ( parent ) ) {
214240 willDrop = this . checkCallWillDrop ( parent , file , bindResolver )
215- this . setCache ( parent , willDrop ) ;
241+ this . setCache ( parent , willDrop )
216242 }
217- return willDrop ;
243+ return willDrop
218244 }
219245 // Check reference in method call
220- const refSibling = closestNamedSibling ( ref . node , 'next' , ( sibl => sibl . type == "method_call" ) )
246+ const refSibling = closestNamedSibling (
247+ ref . node ,
248+ "next" ,
249+ sibl => sibl . type == "method_call" ,
250+ )
221251 if ( refSibling ) {
222252 // If this is a droppable call, go to next ref, else expression is not droppable
223253 if ( ! this . checkCallWillDrop ( refSibling , file , bindResolver ) ) {
224- return false ;
254+ return false
225255 }
226256 }
227257 }
228258 }
229- return true ;
259+ return true
230260 }
231261}
232-
0 commit comments