@@ -489,7 +489,9 @@ func LintErrorTypeAssertions(fset *token.FileSet, info *TypesInfoExt) []analysis
489
489
// For assignment statements like: targetErr, ok := err.(*SomeError)
490
490
if assign , ok := parent .(* ast.AssignStmt ); ok && len (assign .Lhs ) == 2 {
491
491
if id , ok := assign .Lhs [0 ].(* ast.Ident ); ok {
492
- varName := id .Name
492
+ // Generate a suitable variable name, handling underscore case
493
+ // Example: _, ok := err.(*MyError) -> myError := &MyError{}; ok := errors.As(err, &myError)
494
+ varName := generateErrorVarName (id .Name , baseType )
493
495
494
496
// If this is part of an if statement initialization
495
497
ifParent , isIfInit := info .NodeParent [assign ].(* ast.IfStmt )
@@ -532,9 +534,20 @@ func LintErrorTypeAssertions(fset *token.FileSet, info *TypesInfoExt) []analysis
532
534
} else {
533
535
varDecl = fmt .Sprintf ("var %s %s" , varName , baseType )
534
536
}
537
+
538
+ // Preserve the original name of the "ok" variable
539
+ // Example: myErr, wasFound := err.(*MyError)
540
+ // Should use "wasFound" in the transformed code, not just "ok"
541
+ okName := "ok" // Default
542
+ if len (assign .Lhs ) > 1 {
543
+ if okIdent , okOk := assign .Lhs [1 ].(* ast.Ident ); okOk && okIdent .Name != "_" {
544
+ okName = okIdent .Name
545
+ }
546
+ }
547
+
535
548
// Align with golden file format
536
- replacement := fmt .Sprintf ("%s\n ok := errors.As(%s, &%s)" ,
537
- varDecl , errExpr , varName )
549
+ replacement := fmt .Sprintf ("%s\n %s := errors.As(%s, &%s)" ,
550
+ varDecl , okName , errExpr , varName )
538
551
539
552
diagnostic .SuggestedFixes = []analysis.SuggestedFix {{
540
553
Message : "Use errors.As() for type assertions on errors" ,
@@ -551,15 +564,15 @@ func LintErrorTypeAssertions(fset *token.FileSet, info *TypesInfoExt) []analysis
551
564
552
565
if _ , ok := parent .(* ast.IfStmt ); ok {
553
566
// For if statements without initialization but with direct type assertion in condition
554
- // This case is less common but could happen
567
+ varName := generateErrorVarName ( "target" , baseType )
555
568
var varDecl string
556
569
if isPointerType {
557
- varDecl = fmt .Sprintf ("target := &%s{}" , baseType )
570
+ varDecl = fmt .Sprintf ("%s := &%s{}" , varName , baseType )
558
571
} else {
559
- varDecl = fmt .Sprintf ("var target %s" , baseType )
572
+ varDecl = fmt .Sprintf ("var %s %s" , varName , baseType )
560
573
}
561
- replacement := fmt .Sprintf ("%s\n if errors.As(%s, &target )" ,
562
- varDecl , errExpr )
574
+ replacement := fmt .Sprintf ("%s\n if errors.As(%s, &%s )" ,
575
+ varDecl , errExpr , varName )
563
576
564
577
diagnostic .SuggestedFixes = []analysis.SuggestedFix {{
565
578
Message : "Use errors.As() for type assertions on errors" ,
@@ -573,17 +586,19 @@ func LintErrorTypeAssertions(fset *token.FileSet, info *TypesInfoExt) []analysis
573
586
continue
574
587
}
575
588
576
- // For standalone type assertions: err.(*SomeError) or err.(SomeError).
577
- // Create an anonymous function that handles the errors.As call.
589
+ // Handle standalone type assertions without assignment
590
+ // Example: _ = err.(*MyError)
591
+ // Transforms to: _ = func() *MyError { var target *MyError; _ = errors.As(err, &target); return target }()
592
+ varName := generateErrorVarName ("target" , baseType )
578
593
var targetDecl string
579
594
if isPointerType {
580
- targetDecl = fmt .Sprintf ("target := &%s{}" , baseType )
595
+ targetDecl = fmt .Sprintf ("%s := &%s{}" , varName , baseType )
581
596
} else {
582
- targetDecl = fmt .Sprintf ("var target %s" , baseType )
597
+ targetDecl = fmt .Sprintf ("var %s %s" , varName , baseType )
583
598
}
584
599
585
- replacement := fmt .Sprintf ("func() %s {\n \t %s\n \t _ = errors.As(%s, &target )\n \t return target \n }()" ,
586
- targetType , targetDecl , errExpr )
600
+ replacement := fmt .Sprintf ("func() %s {\n \t %s\n \t _ = errors.As(%s, &%s )\n \t return %s \n }()" ,
601
+ targetType , targetDecl , errExpr , varName , varName )
587
602
588
603
diagnostic .SuggestedFixes = []analysis.SuggestedFix {{
589
604
Message : "Use errors.As() for type assertions on errors" ,
@@ -830,3 +845,40 @@ func implementsError(t types.Type) bool {
830
845
831
846
return false
832
847
}
848
+
849
+ // generateErrorVarName creates an appropriate variable name for error type assertions.
850
+ // If originalName is "_" or a generic placeholder, it generates a more meaningful name
851
+ // based on the error type, following Go naming conventions:
852
+ //
853
+ // Examples:
854
+ // - originalName="_", typeName="MyError" → "myError" (camelCase conversion)
855
+ // - originalName="_", typeName="pkg.CustomError" → "customError" (package prefix removed)
856
+ // - originalName="existingName" → "existingName" (original name preserved)
857
+ // - originalName="_", typeName="" → "myErr" (fallback for unknown types)
858
+ //
859
+ // This helps ensure code readability when converting type assertions to errors.As calls,
860
+ // particularly when dealing with underscore identifiers that can't be referenced.
861
+ func generateErrorVarName (originalName , typeName string ) string {
862
+ // If the original name is not an underscore, use it
863
+ if originalName != "_" {
864
+ return originalName
865
+ }
866
+
867
+ // Handle underscore case by generating a name based on the type
868
+ // Strip any package prefix like "pkg."
869
+ if lastDot := strings .LastIndex (typeName , "." ); lastDot >= 0 {
870
+ typeName = typeName [lastDot + 1 :]
871
+ }
872
+
873
+ // Convert first letter to lowercase for camelCase
874
+ if len (typeName ) > 0 {
875
+ firstChar := strings .ToLower (typeName [:1 ])
876
+ if len (typeName ) > 1 {
877
+ return firstChar + typeName [1 :]
878
+ }
879
+ return firstChar
880
+ }
881
+
882
+ // If we couldn't determine a good name, use default.
883
+ return "anErr"
884
+ }
0 commit comments