Skip to content

Commit

Permalink
Add fix option for messagefmt and complete importalias fix option
Browse files Browse the repository at this point in the history
Resolves: projectcontour#6

Signed-off-by: Shayegan Hooshyari <[email protected]>
  • Loading branch information
Glyphack committed Apr 25, 2021
1 parent 00ba86c commit 9566aff
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 28 deletions.
70 changes: 50 additions & 20 deletions pkg/analysis/importalias/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ package importalias
import (
"fmt"
"go/ast"
"go/types"

"regexp"
"strconv"
"strings"

"golang.org/x/tools/go/analysis"
Expand Down Expand Up @@ -50,12 +53,16 @@ func run(pass *analysis.Pass) (interface{}, error) {
return
}

if strings.HasPrefix(alias, "_") {
return // Used by go test and for auto-includes, not a conflict.
}

aliasSlice := strings.Split(alias, "_")
path := strings.ReplaceAll(importStmt.Path.Value, "\"", "")

originalImportPath, _ := strconv.Unquote(importStmt.Path.Value)
// replace all separators with `/` for normalization
path = strings.ReplaceAll(path, "_", "/")
path = strings.ReplaceAll(path, ".", "/")
path = strings.ReplaceAll(path, "-", "")
replacer := strings.NewReplacer("_", "/", ".", "/", "-", "")
path := replacer.Replace(originalImportPath)
// omit the domain name in path
pathSlice := strings.Split(path, "/")[1:]

Expand All @@ -64,15 +71,11 @@ func run(pass *analysis.Pass) (interface{}, error) {
_, versionIndex := packageVersion(pathSlice)
pass.Report(analysis.Diagnostic{
Pos: node.Pos(),
Message: fmt.Sprintf("version %q not specified in alias %q for import path %q",
pathSlice[versionIndex], alias, path),
Message: fmt.Sprintf("version %q not specified in alias %q for import path %q may replace %q with %q",
pathSlice[versionIndex], alias, path, alias, applicableAlias),
SuggestedFixes: []analysis.SuggestedFix{{
Message: fmt.Sprintf("should replace %q with %q", alias, applicableAlias),
TextEdits: []analysis.TextEdit{{
Pos: importStmt.Pos(),
End: importStmt.Name.End(),
NewText: []byte(applicableAlias),
}},
Message: fmt.Sprintf("may replace %q with %q", alias, applicableAlias),
TextEdits: findEdits(node, pass.TypesInfo.Uses, originalImportPath, alias, applicableAlias),
}},
})

Expand All @@ -83,14 +86,10 @@ func run(pass *analysis.Pass) (interface{}, error) {
applicableAlias := getAliasFix(pathSlice)
pass.Report(analysis.Diagnostic{
Pos: node.Pos(),
Message: err.Error(),
Message: fmt.Sprintf("%q may replace %q with %q", err.Error(), alias, applicableAlias),
SuggestedFixes: []analysis.SuggestedFix{{
Message: fmt.Sprintf("should replace %q with %q", alias, applicableAlias),
TextEdits: []analysis.TextEdit{{
Pos: importStmt.Pos(),
End: importStmt.Name.End(),
NewText: []byte(applicableAlias),
}},
Message: fmt.Sprintf("may replace %q with %q", alias, applicableAlias),
TextEdits: findEdits(node, pass.TypesInfo.Uses, originalImportPath, alias, applicableAlias),
}},
})

Expand Down Expand Up @@ -171,11 +170,42 @@ func packageVersion(pathSlice []string) (bool, int) {

func searchString(slice []string, word string) int {
for pos, value := range slice {
r, _ := regexp.Compile(word + "(s)?")
r, _ := regexp.Compile("^" + word + "(s)?$")
if r.MatchString(value) {
return pos
}
}

return len(slice)
}

func findEdits(node ast.Node, uses map[*ast.Ident]types.Object, importPath, original, required string) []analysis.TextEdit {
// Edit the actual import line.
result := []analysis.TextEdit{{
Pos: node.Pos(),
End: node.End(),
NewText: []byte(required + " " + strconv.Quote(importPath)),
}}

// Edit all the uses of the alias in the code.
for use, pkg := range uses {
pkgName, ok := pkg.(*types.PkgName)
if !ok {
// skip identifiers that aren't pointing at a PkgName.
continue
}

if pkgName.Pos() != node.Pos() {
// skip identifiers pointing to a different import statement.
continue
}
if original == pkgName.Name() {
result = append(result, analysis.TextEdit{
Pos: use.Pos(),
End: use.End(),
NewText: []byte(required),
})
}
}
return result
}
68 changes: 60 additions & 8 deletions pkg/analysis/messagefmt/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package messagefmt

import (
"fmt"
"go/ast"
"go/token"
"go/types"
Expand Down Expand Up @@ -122,8 +123,8 @@ func checkInitialLower(pass *analysis.Pass, lit *ast.BasicLit) {
if lit == nil {
return
}

words := strings.Fields(strings.Trim(lit.Value, "\"`"))
value := strings.Trim(lit.Value, "\"`")
words := strings.Fields(value)
first := words[0]

// If the first word is all uppercase, it's an
Expand All @@ -137,7 +138,21 @@ func checkInitialLower(pass *analysis.Pass, lit *ast.BasicLit) {
// `lit.Value` will be the string literal quote.
firstRune, _ := utf8.DecodeRuneInString(first)
if unicode.IsUpper(firstRune) && !isException(first) {
pass.Reportf(lit.Pos(), "message starts with uppercase: %s", lit.Value)
validMessage := []rune(value)
validMessage[0] = unicode.ToLower(validMessage[0])
fix := string(validMessage)
pass.Report(analysis.Diagnostic{
Pos: lit.Pos(),
Message: fmt.Sprintf("message starts with uppercase: %s", lit.Value),
SuggestedFixes: []analysis.SuggestedFix{{
Message: fmt.Sprintf("message starts with uppercase: %s", lit.Value),
TextEdits: []analysis.TextEdit{{
Pos: lit.Pos()+1,
End: lit.End()-1,
NewText: []byte(fix),
}},
}},
})
}
}

Expand All @@ -146,7 +161,8 @@ func checkInitialUpper(pass *analysis.Pass, lit *ast.BasicLit) {
return
}

words := strings.Fields(strings.Trim(lit.Value, "\"`"))
value := strings.Trim(lit.Value, "\"`")
words := strings.Fields(value)
first := words[0]

// If the first word is all uppercase, it's an
Expand All @@ -160,7 +176,21 @@ func checkInitialUpper(pass *analysis.Pass, lit *ast.BasicLit) {
// `lit.Value` will be the string literal quote.
firstRune, _ := utf8.DecodeRuneInString(first)
if unicode.IsLower(firstRune) && !isException(first) {
pass.Reportf(lit.Pos(), "message starts with lowercase: %s", lit.Value)
validMessage := []rune(value)
validMessage[0] = unicode.ToUpper(validMessage[0])
fix := string(validMessage)
pass.Report(analysis.Diagnostic{
Pos: lit.Pos(),
Message: fmt.Sprintf("message starts with lowercase: %s", lit.Value),
SuggestedFixes: []analysis.SuggestedFix{{
Message: fmt.Sprintf("message starts with lowercase: %s", lit.Value),
TextEdits: []analysis.TextEdit{{
Pos: lit.Pos()+1,
End: lit.End()-1,
NewText: []byte(fix),
}},
}},
})
}
}

Expand All @@ -172,7 +202,18 @@ func checkEndsWithoutPeriod(pass *analysis.Pass, lit *ast.BasicLit) {
value := strings.Trim(lit.Value, "\"`")

if len(value) > 0 && value[len(value)-1] == '.' {
pass.Reportf(lit.Pos(), "message must not end with a period: %s", lit.Value)
pass.Report(analysis.Diagnostic{
Pos: lit.Pos(),
Message: fmt.Sprintf("message must not end with a period: %s", lit.Value),
SuggestedFixes: []analysis.SuggestedFix{{
Message: fmt.Sprintf("message must not end with a period: %s", lit.Value),
TextEdits: []analysis.TextEdit{{
Pos: lit.Pos()+1,
End: lit.End()-1,
NewText: []byte(value[:len(value)-1]),
}},
}},
})
}
}

Expand All @@ -183,8 +224,19 @@ func checkEndsWithPeriod(pass *analysis.Pass, lit *ast.BasicLit) {

value := strings.Trim(lit.Value, "\"`")

if len(value) == 0 || value[len(value)-1] != '.' {
pass.Reportf(lit.Pos(), "message must end with a period: %s", lit.Value)
if len(value) > 0 && value[len(value)-1] != '.' {
pass.Report(analysis.Diagnostic{
Pos: lit.Pos(),
Message: fmt.Sprintf("message must end with a period: %s", lit.Value),
SuggestedFixes: []analysis.SuggestedFix{{
Message: fmt.Sprintf("message must end with a period: %s", lit.Value),
TextEdits: []analysis.TextEdit{{
Pos: lit.Pos()+1,
End: lit.End()-1,
NewText: []byte(value + "."),
}},
}},
})
}
}

Expand Down

0 comments on commit 9566aff

Please sign in to comment.