@@ -2,6 +2,7 @@ package analysis
2
2
3
3
import (
4
4
"math/big"
5
+ "math/rand"
5
6
"slices"
6
7
"strings"
7
8
@@ -87,6 +88,7 @@ var Builtins = map[string]FnCallResolution{
87
88
type Diagnostic struct {
88
89
Range parser.Range
89
90
Kind DiagnosticKind
91
+ Id int32
90
92
}
91
93
92
94
type CheckResult struct {
@@ -149,30 +151,30 @@ func newCheckResult(program parser.Program) CheckResult {
149
151
}
150
152
151
153
func (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
+ }
156
159
157
- if varDecl .Name != nil {
158
- res .checkDuplicateVars (* varDecl .Name , varDecl )
159
- }
160
+ if varDecl .Name != nil {
161
+ res .checkDuplicateVars (* varDecl .Name , varDecl )
162
+ }
160
163
161
- if varDecl .Origin != nil {
162
- res .checkVarOrigin (* varDecl .Origin , varDecl )
164
+ if varDecl .Origin != nil {
165
+ res .checkVarOrigin (* varDecl .Origin , varDecl )
166
+ }
163
167
}
164
168
}
169
+
165
170
for _ , statement := range res .Program .Statements {
166
171
res .unboundedAccountInSend = nil
167
172
res .checkStatement (statement )
168
173
}
169
174
170
175
// after static AST traversal is complete, check for unused vars
171
176
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 })
176
178
}
177
179
}
178
180
@@ -214,19 +216,12 @@ func CheckSource(source string) CheckResult {
214
216
result := parser .Parse (source )
215
217
res := newCheckResult (result .Value )
216
218
for _ , parserError := range result .Errors {
217
- res .Diagnostics = append ( res . Diagnostics , parsingErrorToDiagnostic ( parserError ) )
219
+ res .pushDiagnostic ( parserError . Range , Parsing { Description : parserError . Msg } )
218
220
}
219
221
res .check ()
220
222
return res
221
223
}
222
224
223
- func parsingErrorToDiagnostic (parserError parser.ParserError ) Diagnostic {
224
- return Diagnostic {
225
- Range : parserError .Range ,
226
- Kind : & Parsing {Description : parserError .Msg },
227
- }
228
- }
229
-
230
225
func (res * CheckResult ) checkFnCallArity (fnCall * parser.FnCall ) {
231
226
resolution , resolved := res .fnCallResolution [fnCall .Caller ]
232
227
@@ -244,12 +239,9 @@ func (res *CheckResult) checkFnCallArity(fnCall *parser.FnCall) {
244
239
245
240
if actualArgs < expectedArgs {
246
241
// 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 ,
253
245
})
254
246
} else if actualArgs > expectedArgs {
255
247
// Too many args
@@ -262,12 +254,9 @@ func (res *CheckResult) checkFnCallArity(fnCall *parser.FnCall) {
262
254
End : lastIllegalArg .GetRange ().End ,
263
255
}
264
256
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 ,
271
260
})
272
261
}
273
262
@@ -287,11 +276,8 @@ func (res *CheckResult) checkFnCallArity(fnCall *parser.FnCall) {
287
276
res .checkExpression (arg , TypeAny )
288
277
}
289
278
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 ,
295
281
})
296
282
}
297
283
}
@@ -302,20 +288,14 @@ func isTypeAllowed(typeName string) bool {
302
288
303
289
func (res * CheckResult ) checkVarType (typeDecl parser.TypeDecl ) {
304
290
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 })
309
292
}
310
293
}
311
294
312
295
func (res * CheckResult ) checkDuplicateVars (variableName parser.Variable , decl parser.VarDeclaration ) {
313
296
// check there aren't duplicate variables
314
297
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 })
319
299
} else {
320
300
res .declaredVars [variableName .Name ] = decl
321
301
res .unusedVars [variableName .Name ] = variableName .Range
@@ -337,20 +317,17 @@ func (res *CheckResult) checkVarOrigin(fnCall parser.FnCall, decl parser.VarDecl
337
317
}
338
318
339
319
func (res * CheckResult ) checkExpression (lit parser.ValueExpr , requiredType string ) {
340
- actualType := res .checkTypeOf (lit )
320
+ actualType := res .checkTypeOf (lit , requiredType )
341
321
res .assertHasType (lit , requiredType , actualType )
342
322
}
343
323
344
- func (res * CheckResult ) checkTypeOf (lit parser.ValueExpr ) string {
324
+ func (res * CheckResult ) checkTypeOf (lit parser.ValueExpr , typeHint string ) string {
345
325
switch lit := lit .(type ) {
346
326
case * parser.Variable :
347
327
if varDeclaration , ok := res .declaredVars [lit .Name ]; ok {
348
328
res .varResolution [lit ] = varDeclaration
349
329
} 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 })
354
331
}
355
332
delete (res .unusedVars , lit .Name )
356
333
@@ -408,19 +385,16 @@ func (res *CheckResult) checkTypeOf(lit parser.ValueExpr) string {
408
385
}
409
386
410
387
func (res * CheckResult ) checkInfixOverload (bin * parser.BinaryInfix , allowed []string ) string {
411
- leftType := res .checkTypeOf (bin .Left )
388
+ leftType := res .checkTypeOf (bin .Left , allowed [ 0 ] )
412
389
413
390
if leftType == TypeAny || slices .Contains (allowed , leftType ) {
414
391
res .checkExpression (bin .Right , leftType )
415
392
return leftType
416
393
}
417
394
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 ,
424
398
})
425
399
return TypeAny
426
400
}
@@ -430,14 +404,10 @@ func (res *CheckResult) assertHasType(lit parser.ValueExpr, requiredType string,
430
404
return
431
405
}
432
406
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 ,
439
410
})
440
-
441
411
}
442
412
443
413
func (res * CheckResult ) checkSentValue (sentValue parser.SentValue ) {
@@ -455,30 +425,21 @@ func (res *CheckResult) checkSource(source parser.Source) {
455
425
}
456
426
457
427
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 {})
462
429
}
463
430
464
431
switch source := source .(type ) {
465
432
case * parser.SourceAccount :
466
433
res .checkExpression (source .ValueExpr , TypeAccount )
467
434
if account , ok := source .ValueExpr .(* parser.AccountInterpLiteral ); ok {
468
435
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 {})
473
437
} else if account .IsWorld () {
474
438
res .unboundedAccountInSend = account
475
439
}
476
440
477
441
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 ()})
482
443
}
483
444
484
445
res .emptiedAccount [account .String ()] = struct {}{}
@@ -497,10 +458,7 @@ func (res *CheckResult) checkSource(source parser.Source) {
497
458
}
498
459
499
460
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 {})
504
462
}
505
463
506
464
res .checkExpression (source .Address , TypeAccount )
@@ -528,10 +486,7 @@ func (res *CheckResult) checkSource(source parser.Source) {
528
486
529
487
case * parser.SourceAllotment :
530
488
if res .unboundedSend {
531
- res .Diagnostics = append (res .Diagnostics , Diagnostic {
532
- Kind : & NoAllotmentInSendAll {},
533
- Range : source .Range ,
534
- })
489
+ res .pushDiagnostic (source .Range , NoAllotmentInSendAll {})
535
490
}
536
491
537
492
var remainingAllotment * parser.RemainingAllotment = nil
@@ -555,10 +510,7 @@ func (res *CheckResult) checkSource(source parser.Source) {
555
510
if isLast {
556
511
remainingAllotment = allotment
557
512
} else {
558
- res .Diagnostics = append (res .Diagnostics , Diagnostic {
559
- Range : source .Range ,
560
- Kind : & RemainingIsNotLast {},
561
- })
513
+ res .pushDiagnostic (source .Range , RemainingIsNotLast {})
562
514
}
563
515
}
564
516
@@ -637,10 +589,7 @@ func (res *CheckResult) tryEvaluatingPortionExpr(expr parser.ValueExpr) *big.Rat
637
589
}
638
590
639
591
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 {})
644
593
return nil
645
594
}
646
595
@@ -708,10 +657,7 @@ func (res *CheckResult) checkDestination(destination parser.Destination) {
708
657
if isLast {
709
658
remainingAllotment = allotment
710
659
} else {
711
- res .Diagnostics = append (res .Diagnostics , Diagnostic {
712
- Range : destination .Range ,
713
- Kind : & RemainingIsNotLast {},
714
- })
660
+ res .pushDiagnostic (destination .Range , RemainingIsNotLast {})
715
661
}
716
662
}
717
663
@@ -746,36 +692,22 @@ func (res *CheckResult) checkHasBadAllotmentSum(
746
692
747
693
if cmp == - 1 && len (variableLiterals ) == 1 {
748
694
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 ),
754
697
})
755
698
} 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 })
762
700
}
763
701
764
702
// sum == 1
765
703
case 0 :
766
704
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 ),
772
707
})
773
708
}
774
709
if remaining != nil {
775
- res .Diagnostics = append (res .Diagnostics , Diagnostic {
776
- Range : remaining .Range ,
777
- Kind : & RedundantRemaining {},
778
- })
710
+ res .pushDiagnostic (remaining .Range , RedundantRemaining {})
779
711
}
780
712
}
781
713
}
@@ -820,3 +752,11 @@ func (res *CheckResult) enterCappedSource() func() {
820
752
exitCloneUnboundedSend ()
821
753
}
822
754
}
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