@@ -2,6 +2,7 @@ package analysis
22
33import (
44 "math/big"
5+ "math/rand"
56 "slices"
67 "strings"
78
@@ -87,6 +88,7 @@ var Builtins = map[string]FnCallResolution{
8788type Diagnostic struct {
8889 Range parser.Range
8990 Kind DiagnosticKind
91+ Id int32
9092}
9193
9294type CheckResult struct {
@@ -149,30 +151,30 @@ func newCheckResult(program parser.Program) CheckResult {
149151}
150152
151153func (res * CheckResult ) check () {
152- for _ , varDecl := range res .Program .Vars {
153- if varDecl .Type != nil {
154- res .checkVarType (* varDecl .Type )
155- }
154+ if res .Program .Vars != nil {
155+ for _ , varDecl := range res .Program .Vars .Declarations {
156+ if varDecl .Type != nil {
157+ res .checkVarType (* varDecl .Type )
158+ }
156159
157- if varDecl .Name != nil {
158- res .checkDuplicateVars (* varDecl .Name , varDecl )
159- }
160+ if varDecl .Name != nil {
161+ res .checkDuplicateVars (* varDecl .Name , varDecl )
162+ }
160163
161- if varDecl .Origin != nil {
162- res .checkVarOrigin (* varDecl .Origin , varDecl )
164+ if varDecl .Origin != nil {
165+ res .checkVarOrigin (* varDecl .Origin , varDecl )
166+ }
163167 }
164168 }
169+
165170 for _ , statement := range res .Program .Statements {
166171 res .unboundedAccountInSend = nil
167172 res .checkStatement (statement )
168173 }
169174
170175 // after static AST traversal is complete, check for unused vars
171176 for name , rng := range res .unusedVars {
172- res .Diagnostics = append (res .Diagnostics , Diagnostic {
173- Range : rng ,
174- Kind : & UnusedVar {Name : name },
175- })
177+ res .pushDiagnostic (rng , UnusedVar {Name : name })
176178 }
177179}
178180
@@ -214,19 +216,12 @@ func CheckSource(source string) CheckResult {
214216 result := parser .Parse (source )
215217 res := newCheckResult (result .Value )
216218 for _ , parserError := range result .Errors {
217- res .Diagnostics = append ( res . Diagnostics , parsingErrorToDiagnostic ( parserError ) )
219+ res .pushDiagnostic ( parserError . Range , Parsing { Description : parserError . Msg } )
218220 }
219221 res .check ()
220222 return res
221223}
222224
223- func parsingErrorToDiagnostic (parserError parser.ParserError ) Diagnostic {
224- return Diagnostic {
225- Range : parserError .Range ,
226- Kind : & Parsing {Description : parserError .Msg },
227- }
228- }
229-
230225func (res * CheckResult ) checkFnCallArity (fnCall * parser.FnCall ) {
231226 resolution , resolved := res .fnCallResolution [fnCall .Caller ]
232227
@@ -244,12 +239,9 @@ func (res *CheckResult) checkFnCallArity(fnCall *parser.FnCall) {
244239
245240 if actualArgs < expectedArgs {
246241 // Too few args
247- res .Diagnostics = append (res .Diagnostics , Diagnostic {
248- Range : fnCall .Range ,
249- Kind : & BadArity {
250- Expected : expectedArgs ,
251- Actual : actualArgs ,
252- },
242+ res .pushDiagnostic (fnCall .Range , BadArity {
243+ Expected : expectedArgs ,
244+ Actual : actualArgs ,
253245 })
254246 } else if actualArgs > expectedArgs {
255247 // Too many args
@@ -262,12 +254,9 @@ func (res *CheckResult) checkFnCallArity(fnCall *parser.FnCall) {
262254 End : lastIllegalArg .GetRange ().End ,
263255 }
264256
265- res .Diagnostics = append (res .Diagnostics , Diagnostic {
266- Range : rng ,
267- Kind : & BadArity {
268- Expected : expectedArgs ,
269- Actual : actualArgs ,
270- },
257+ res .pushDiagnostic (rng , BadArity {
258+ Expected : expectedArgs ,
259+ Actual : actualArgs ,
271260 })
272261 }
273262
@@ -287,11 +276,8 @@ func (res *CheckResult) checkFnCallArity(fnCall *parser.FnCall) {
287276 res .checkExpression (arg , TypeAny )
288277 }
289278
290- res .Diagnostics = append (res .Diagnostics , Diagnostic {
291- Range : fnCall .Caller .Range ,
292- Kind : & UnknownFunction {
293- Name : fnCall .Caller .Name ,
294- },
279+ res .pushDiagnostic (fnCall .Caller .Range , UnknownFunction {
280+ Name : fnCall .Caller .Name ,
295281 })
296282 }
297283}
@@ -302,20 +288,14 @@ func isTypeAllowed(typeName string) bool {
302288
303289func (res * CheckResult ) checkVarType (typeDecl parser.TypeDecl ) {
304290 if ! isTypeAllowed (typeDecl .Name ) {
305- res .Diagnostics = append (res .Diagnostics , Diagnostic {
306- Range : typeDecl .Range ,
307- Kind : & InvalidType {Name : typeDecl .Name },
308- })
291+ res .pushDiagnostic (typeDecl .Range , InvalidType {Name : typeDecl .Name })
309292 }
310293}
311294
312295func (res * CheckResult ) checkDuplicateVars (variableName parser.Variable , decl parser.VarDeclaration ) {
313296 // check there aren't duplicate variables
314297 if _ , ok := res .declaredVars [variableName .Name ]; ok {
315- res .Diagnostics = append (res .Diagnostics , Diagnostic {
316- Range : variableName .Range ,
317- Kind : & DuplicateVariable {Name : variableName .Name },
318- })
298+ res .pushDiagnostic (variableName .Range , DuplicateVariable {Name : variableName .Name })
319299 } else {
320300 res .declaredVars [variableName .Name ] = decl
321301 res .unusedVars [variableName .Name ] = variableName .Range
@@ -337,20 +317,17 @@ func (res *CheckResult) checkVarOrigin(fnCall parser.FnCall, decl parser.VarDecl
337317}
338318
339319func (res * CheckResult ) checkExpression (lit parser.ValueExpr , requiredType string ) {
340- actualType := res .checkTypeOf (lit )
320+ actualType := res .checkTypeOf (lit , requiredType )
341321 res .assertHasType (lit , requiredType , actualType )
342322}
343323
344- func (res * CheckResult ) checkTypeOf (lit parser.ValueExpr ) string {
324+ func (res * CheckResult ) checkTypeOf (lit parser.ValueExpr , typeHint string ) string {
345325 switch lit := lit .(type ) {
346326 case * parser.Variable :
347327 if varDeclaration , ok := res .declaredVars [lit .Name ]; ok {
348328 res .varResolution [lit ] = varDeclaration
349329 } else {
350- res .Diagnostics = append (res .Diagnostics , Diagnostic {
351- Range : lit .Range ,
352- Kind : & UnboundVariable {Name : lit .Name },
353- })
330+ res .pushDiagnostic (lit .Range , UnboundVariable {Name : lit .Name , Type : typeHint })
354331 }
355332 delete (res .unusedVars , lit .Name )
356333
@@ -408,19 +385,16 @@ func (res *CheckResult) checkTypeOf(lit parser.ValueExpr) string {
408385}
409386
410387func (res * CheckResult ) checkInfixOverload (bin * parser.BinaryInfix , allowed []string ) string {
411- leftType := res .checkTypeOf (bin .Left )
388+ leftType := res .checkTypeOf (bin .Left , allowed [ 0 ] )
412389
413390 if leftType == TypeAny || slices .Contains (allowed , leftType ) {
414391 res .checkExpression (bin .Right , leftType )
415392 return leftType
416393 }
417394
418- res .Diagnostics = append (res .Diagnostics , Diagnostic {
419- Range : bin .Left .GetRange (),
420- Kind : & TypeMismatch {
421- Expected : strings .Join (allowed , "|" ),
422- Got : leftType ,
423- },
395+ res .pushDiagnostic (bin .Left .GetRange (), TypeMismatch {
396+ Expected : strings .Join (allowed , "|" ),
397+ Got : leftType ,
424398 })
425399 return TypeAny
426400}
@@ -430,14 +404,10 @@ func (res *CheckResult) assertHasType(lit parser.ValueExpr, requiredType string,
430404 return
431405 }
432406
433- res .Diagnostics = append (res .Diagnostics , Diagnostic {
434- Range : lit .GetRange (),
435- Kind : & TypeMismatch {
436- Expected : requiredType ,
437- Got : actualType ,
438- },
407+ res .pushDiagnostic (lit .GetRange (), TypeMismatch {
408+ Expected : requiredType ,
409+ Got : actualType ,
439410 })
440-
441411}
442412
443413func (res * CheckResult ) checkSentValue (sentValue parser.SentValue ) {
@@ -455,30 +425,21 @@ func (res *CheckResult) checkSource(source parser.Source) {
455425 }
456426
457427 if res .unboundedAccountInSend != nil {
458- res .Diagnostics = append (res .Diagnostics , Diagnostic {
459- Range : source .GetRange (),
460- Kind : & UnboundedAccountIsNotLast {},
461- })
428+ res .pushDiagnostic (source .GetRange (), UnboundedAccountIsNotLast {})
462429 }
463430
464431 switch source := source .(type ) {
465432 case * parser.SourceAccount :
466433 res .checkExpression (source .ValueExpr , TypeAccount )
467434 if account , ok := source .ValueExpr .(* parser.AccountInterpLiteral ); ok {
468435 if account .IsWorld () && res .unboundedSend {
469- res .Diagnostics = append (res .Diagnostics , Diagnostic {
470- Range : source .GetRange (),
471- Kind : & InvalidUnboundedAccount {},
472- })
436+ res .pushDiagnostic (source .GetRange (), InvalidUnboundedAccount {})
473437 } else if account .IsWorld () {
474438 res .unboundedAccountInSend = account
475439 }
476440
477441 if _ , emptied := res .emptiedAccount [account .String ()]; emptied && ! account .IsWorld () {
478- res .Diagnostics = append (res .Diagnostics , Diagnostic {
479- Kind : & EmptiedAccount {Name : account .String ()},
480- Range : account .Range ,
481- })
442+ res .pushDiagnostic (account .Range , EmptiedAccount {Name : account .String ()})
482443 }
483444
484445 res .emptiedAccount [account .String ()] = struct {}{}
@@ -497,10 +458,7 @@ func (res *CheckResult) checkSource(source parser.Source) {
497458 }
498459
499460 if res .unboundedSend {
500- res .Diagnostics = append (res .Diagnostics , Diagnostic {
501- Range : source .Address .GetRange (),
502- Kind : & InvalidUnboundedAccount {},
503- })
461+ res .pushDiagnostic (source .Address .GetRange (), InvalidUnboundedAccount {})
504462 }
505463
506464 res .checkExpression (source .Address , TypeAccount )
@@ -528,10 +486,7 @@ func (res *CheckResult) checkSource(source parser.Source) {
528486
529487 case * parser.SourceAllotment :
530488 if res .unboundedSend {
531- res .Diagnostics = append (res .Diagnostics , Diagnostic {
532- Kind : & NoAllotmentInSendAll {},
533- Range : source .Range ,
534- })
489+ res .pushDiagnostic (source .Range , NoAllotmentInSendAll {})
535490 }
536491
537492 var remainingAllotment * parser.RemainingAllotment = nil
@@ -555,10 +510,7 @@ func (res *CheckResult) checkSource(source parser.Source) {
555510 if isLast {
556511 remainingAllotment = allotment
557512 } else {
558- res .Diagnostics = append (res .Diagnostics , Diagnostic {
559- Range : source .Range ,
560- Kind : & RemainingIsNotLast {},
561- })
513+ res .pushDiagnostic (source .Range , RemainingIsNotLast {})
562514 }
563515 }
564516
@@ -637,10 +589,7 @@ func (res *CheckResult) tryEvaluatingPortionExpr(expr parser.ValueExpr) *big.Rat
637589 }
638590
639591 if right .Cmp (big .NewInt (0 )) == 0 {
640- res .Diagnostics = append (res .Diagnostics , Diagnostic {
641- Kind : & DivByZero {},
642- Range : expr .Range ,
643- })
592+ res .pushDiagnostic (expr .Range , DivByZero {})
644593 return nil
645594 }
646595
@@ -708,10 +657,7 @@ func (res *CheckResult) checkDestination(destination parser.Destination) {
708657 if isLast {
709658 remainingAllotment = allotment
710659 } else {
711- res .Diagnostics = append (res .Diagnostics , Diagnostic {
712- Range : destination .Range ,
713- Kind : & RemainingIsNotLast {},
714- })
660+ res .pushDiagnostic (destination .Range , RemainingIsNotLast {})
715661 }
716662 }
717663
@@ -746,36 +692,22 @@ func (res *CheckResult) checkHasBadAllotmentSum(
746692
747693 if cmp == - 1 && len (variableLiterals ) == 1 {
748694 var value big.Rat
749- res .Diagnostics = append (res .Diagnostics , Diagnostic {
750- Range : variableLiterals [0 ].GetRange (),
751- Kind : & FixedPortionVariable {
752- Value : * value .Sub (big .NewRat (1 , 1 ), & sum ),
753- },
695+ res .pushDiagnostic (variableLiterals [0 ].GetRange (), FixedPortionVariable {
696+ Value : * value .Sub (big .NewRat (1 , 1 ), & sum ),
754697 })
755698 } else {
756- res .Diagnostics = append (res .Diagnostics , Diagnostic {
757- Range : rng ,
758- Kind : & BadAllotmentSum {
759- Sum : sum ,
760- },
761- })
699+ res .pushDiagnostic (rng , BadAllotmentSum {Sum : sum })
762700 }
763701
764702 // sum == 1
765703 case 0 :
766704 for _ , varLit := range variableLiterals {
767- res .Diagnostics = append (res .Diagnostics , Diagnostic {
768- Range : varLit .GetRange (),
769- Kind : & FixedPortionVariable {
770- Value : * big .NewRat (0 , 1 ),
771- },
705+ res .pushDiagnostic (varLit .GetRange (), FixedPortionVariable {
706+ Value : * big .NewRat (0 , 1 ),
772707 })
773708 }
774709 if remaining != nil {
775- res .Diagnostics = append (res .Diagnostics , Diagnostic {
776- Range : remaining .Range ,
777- Kind : & RedundantRemaining {},
778- })
710+ res .pushDiagnostic (remaining .Range , RedundantRemaining {})
779711 }
780712 }
781713}
@@ -820,3 +752,11 @@ func (res *CheckResult) enterCappedSource() func() {
820752 exitCloneUnboundedSend ()
821753 }
822754}
755+
756+ func (res * CheckResult ) pushDiagnostic (rng parser.Range , kind DiagnosticKind ) {
757+ res .Diagnostics = append (res .Diagnostics , Diagnostic {
758+ Range : rng ,
759+ Kind : kind ,
760+ Id : rand .Int31 (),
761+ })
762+ }
0 commit comments