mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 03:18:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			103 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			103 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package rule
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"go/ast"
 | |
| 	"go/token"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/mgechev/revive/lint"
 | |
| )
 | |
| 
 | |
| // ImportShadowingRule lints given else constructs.
 | |
| type ImportShadowingRule struct{}
 | |
| 
 | |
| // Apply applies the rule to given file.
 | |
| func (r *ImportShadowingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
 | |
| 	var failures []lint.Failure
 | |
| 
 | |
| 	importNames := map[string]struct{}{}
 | |
| 	for _, imp := range file.AST.Imports {
 | |
| 		importNames[getName(imp)] = struct{}{}
 | |
| 	}
 | |
| 
 | |
| 	fileAst := file.AST
 | |
| 	walker := importShadowing{
 | |
| 		importNames: importNames,
 | |
| 		onFailure: func(failure lint.Failure) {
 | |
| 			failures = append(failures, failure)
 | |
| 		},
 | |
| 		alreadySeen: map[*ast.Object]struct{}{},
 | |
| 	}
 | |
| 
 | |
| 	ast.Walk(walker, fileAst)
 | |
| 
 | |
| 	return failures
 | |
| }
 | |
| 
 | |
| // Name returns the rule name.
 | |
| func (r *ImportShadowingRule) Name() string {
 | |
| 	return "import-shadowing"
 | |
| }
 | |
| 
 | |
| func getName(imp *ast.ImportSpec) string {
 | |
| 	const pathSep = "/"
 | |
| 	const strDelim = `"`
 | |
| 	if imp.Name != nil {
 | |
| 		return imp.Name.Name
 | |
| 	}
 | |
| 
 | |
| 	path := imp.Path.Value
 | |
| 	i := strings.LastIndex(path, pathSep)
 | |
| 	if i == -1 {
 | |
| 		return strings.Trim(path, strDelim)
 | |
| 	}
 | |
| 
 | |
| 	return strings.Trim(path[i+1:], strDelim)
 | |
| }
 | |
| 
 | |
| type importShadowing struct {
 | |
| 	importNames map[string]struct{}
 | |
| 	onFailure   func(lint.Failure)
 | |
| 	alreadySeen map[*ast.Object]struct{}
 | |
| }
 | |
| 
 | |
| // Visit visits AST nodes and checks if id nodes (ast.Ident) shadow an import name
 | |
| func (w importShadowing) Visit(n ast.Node) ast.Visitor {
 | |
| 	switch n := n.(type) {
 | |
| 	case *ast.AssignStmt:
 | |
| 		if n.Tok == token.DEFINE {
 | |
| 			return w // analyze variable declarations of the form id := expr
 | |
| 		}
 | |
| 
 | |
| 		return nil // skip assigns of the form id = expr (not an id declaration)
 | |
| 	case *ast.CallExpr, // skip call expressions (not an id declaration)
 | |
| 		*ast.ImportSpec,   // skip import section subtree because we already have the list of imports
 | |
| 		*ast.KeyValueExpr, // skip analysis of key-val expressions ({key:value}): ids of such expressions, even the same of an import name, do not shadow the import name
 | |
| 		*ast.ReturnStmt,   // skip skipping analysis of returns, ids in expression were already analyzed
 | |
| 		*ast.SelectorExpr, // skip analysis of selector expressions (anId.otherId): because if anId shadows an import name, it was already detected, and otherId does not shadows the import name
 | |
| 		*ast.StructType:   // skip analysis of struct type because struct fields can not shadow an import name
 | |
| 		return nil
 | |
| 	case *ast.Ident:
 | |
| 		id := n.Name
 | |
| 		if id == "_" {
 | |
| 			return w // skip _ id
 | |
| 		}
 | |
| 
 | |
| 		_, isImportName := w.importNames[id]
 | |
| 		_, alreadySeen := w.alreadySeen[n.Obj]
 | |
| 		if isImportName && !alreadySeen {
 | |
| 			w.onFailure(lint.Failure{
 | |
| 				Confidence: 1,
 | |
| 				Node:       n,
 | |
| 				Category:   "namming",
 | |
| 				Failure:    fmt.Sprintf("The name '%s' shadows an import name", id),
 | |
| 			})
 | |
| 
 | |
| 			w.alreadySeen[n.Obj] = struct{}{}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return w
 | |
| }
 |