@@ -9,10 +9,10 @@ import { UnusedInspection } from "./UnusedInspection"
99import { Inspection , InspectionIds } from "./Inspection"
1010import { RecursiveVisitor } from "@server/visitor/visitor" ;
1111import { Func } from "@server/languages/func/psi/Decls" ;
12- import { convertTy , typeOf } from "@server/languages/func/types/infer" ;
1312import { asLspRange } from "@server/utils/position"
1413import { closestNamedSibling , parentOfType , parentOfTypeWithCb } from "@server/psi/utils"
1514import { Referent } from "@server/languages/func/psi/Referent"
15+ import { FunCBindingResolver } from "../psi/BindingResolver"
1616
1717
1818export class UnusedImpureInspection extends UnusedInspection implements Inspection {
@@ -26,27 +26,40 @@ export class UnusedImpureInspection extends UnusedInspection implements Inspecti
2626 impureMap . set ( f . name ( true ) , f ) ;
2727 }
2828 } ) ;
29+ const bindResolver = new FunCBindingResolver ( file ) ;
2930 RecursiveVisitor . visit ( file . rootNode , ( node ) : boolean => {
31+ let droppableDef : Func | undefined ;
32+
3033 if ( node . type == "function_application" ) {
31- const funcIdentifier = node . children . find ( n => n ?. type === "identifier" )
34+ const funcIdentifier = node . childForFieldName ( "callee" ) ;
35+ if ( funcIdentifier ) {
36+ droppableDef = impureMap . get ( funcIdentifier . text ) ;
37+ }
38+ } else if ( node . type == "method_call" ) {
39+ const funcIdentifier = node . childForFieldName ( "method_name" ) ;
3240 if ( funcIdentifier ) {
33- const droppableDef = impureMap . get ( funcIdentifier . text ) ;
34- if ( droppableDef && this . checkCallWillDrop ( node , droppableDef , file ) ) {
35- const range = asLspRange ( node ) ;
36- diagnostics . push ( {
37- severity : lsp . DiagnosticSeverity . Error ,
38- range,
39- message : "This call will be dropped due to lack of impure specifier!" ,
40- source : "func"
41- } )
41+ const methodName = funcIdentifier . text ;
42+ droppableDef = impureMap . get ( methodName ) ;
43+ if ( ! droppableDef ) {
44+ droppableDef = impureMap . get ( "~" + methodName ) ;
4245 }
4346 }
4447 }
48+
49+ if ( droppableDef && this . checkCallWillDrop ( node , droppableDef , file , bindResolver ) ) {
50+ const range = asLspRange ( node ) ;
51+ diagnostics . push ( {
52+ severity : lsp . DiagnosticSeverity . Error ,
53+ range,
54+ message : "This call will be dropped due to lack of impure specifier!" ,
55+ source : "func"
56+ } )
57+ }
4558 return true ;
4659 } )
4760 }
4861
49- private checkCallWillDrop ( node : Node , definition : Func , file : FuncFile ) {
62+ private checkCallWillDrop ( node : Node , definition : Func , file : FuncFile , bindResolver : FunCBindingResolver ) {
5063 const returnExp = definition . returnType ( ) ;
5164 if ( returnExp !== null ) {
5265 // If return type of a function is empty tensor - check no more.
@@ -65,31 +78,33 @@ export class UnusedImpureInspection extends UnusedInspection implements Inspecti
6578 "if_statement" ,
6679 "while_statement" ,
6780 "do_while_statement" ,
68- "repeat_statement"
81+ "repeat_statement" ,
82+ "return_statement"
6983 ) ;
7084 // If call is in the block_statement of any kind, it will be a child of expression_statement
7185 // Otherwise it is in condition block of if/while/do while
7286 // Or in arguments clause of other function_application/method_call
7387 if ( ! expressionParent || expressionParent . parent . type !== "expression_statement" ) {
88+ // If expression is in condition or return statement it will not be dropped
7489 return false ;
7590 }
7691
7792 // We are in the expression expression_statement
7893 // Closest previous sibling got to be lvalue expression
7994 // (identifier/tensor_expression/tuple_expression)
80- const lValue = closestNamedSibling ( expressionParent . origin , 'prev' , ( sibling ) => sibling . type !== "comment" ) ;
95+ const resolvedBinding = bindResolver . resolve ( expressionParent . parent ) ;
8196 // If no lvalue, non-impure call will drop
82- if ( ! lValue ) {
97+ if ( resolvedBinding . bindings . size == 0 ) {
8398 return true ;
8499 }
85100 // If no identifiers referenced in lvalue, means those are whole type and will be dropped
86- const affectedIdentifiers = lValue . descendantsOfType ( "identifier" ) ;
101+ // const affectedIdentifiers = resolvedBinding.bindings.values()
87102
88- for ( let refValue of affectedIdentifiers ) {
103+ for ( let refValue of resolvedBinding . bindings . values ( ) ) {
89104 if ( ! refValue ) {
90105 continue ;
91106 }
92- const references = new Referent ( refValue , file ) . findReferences ( { } ) // we need at least one reference
107+ const references = new Referent ( refValue . identifier , file ) . findReferences ( { } ) // we need at least one reference
93108 // Has to be referenced in call, conditional or return statement;
94109 for ( let ref of references ) {
95110 const parent = parentOfType ( ref . node ,
@@ -99,7 +114,8 @@ export class UnusedImpureInspection extends UnusedInspection implements Inspecti
99114 "if_statement" ,
100115 "while_statement" ,
101116 "do_while_statement" ,
102- "repeat_statement"
117+ "repeat_statement" ,
118+ "return_statement"
103119 )
104120 if ( parent && parent . type !== "expression_statement" ) {
105121 return false ;
0 commit comments