github.com/daixiang0/gci@v0.13.4/pkg/analyzer/analyzer.go (about)

     1  package analyzer
     2  
     3  import (
     4  	"fmt"
     5  	"go/token"
     6  	"strings"
     7  
     8  	"github.com/golangci/modinfo"
     9  	"golang.org/x/tools/go/analysis"
    10  	"golang.org/x/tools/go/analysis/passes/inspect"
    11  
    12  	"github.com/daixiang0/gci/pkg/config"
    13  	"github.com/daixiang0/gci/pkg/gci"
    14  	"github.com/daixiang0/gci/pkg/io"
    15  	"github.com/daixiang0/gci/pkg/log"
    16  )
    17  
    18  const (
    19  	NoInlineCommentsFlag  = "noInlineComments"
    20  	NoPrefixCommentsFlag  = "noPrefixComments"
    21  	SkipGeneratedFlag     = "skipGenerated"
    22  	SectionsFlag          = "Sections"
    23  	SectionSeparatorsFlag = "SectionSeparators"
    24  	SectionDelimiter      = ","
    25  )
    26  
    27  var (
    28  	noInlineComments     bool
    29  	noPrefixComments     bool
    30  	skipGenerated        bool
    31  	sectionsStr          string
    32  	sectionSeparatorsStr string
    33  )
    34  
    35  func init() {
    36  	Analyzer.Flags.BoolVar(&noInlineComments, NoInlineCommentsFlag, false, "If comments in the same line as the input should be present")
    37  	Analyzer.Flags.BoolVar(&noPrefixComments, NoPrefixCommentsFlag, false, "If comments above an input should be present")
    38  	Analyzer.Flags.BoolVar(&skipGenerated, SkipGeneratedFlag, false, "Skip generated files")
    39  	Analyzer.Flags.StringVar(&sectionsStr, SectionsFlag, "", "Specify the Sections format that should be used to check the file formatting")
    40  	Analyzer.Flags.StringVar(&sectionSeparatorsStr, SectionSeparatorsFlag, "", "Specify the Sections that are inserted as Separators between Sections")
    41  
    42  	log.InitLogger()
    43  	defer log.L().Sync()
    44  }
    45  
    46  var Analyzer = &analysis.Analyzer{
    47  	Name: "gci",
    48  	Doc:  "A tool that control Go package import order and make it always deterministic.",
    49  	Run:  runAnalysis,
    50  	Requires: []*analysis.Analyzer{
    51  		inspect.Analyzer,
    52  		modinfo.Analyzer,
    53  	},
    54  }
    55  
    56  func runAnalysis(pass *analysis.Pass) (interface{}, error) {
    57  	var fileReferences []*token.File
    58  	// extract file references for all files in the analyzer pass
    59  	for _, pkgFile := range pass.Files {
    60  		fileForPos := pass.Fset.File(pkgFile.Package)
    61  		if fileForPos != nil {
    62  			fileReferences = append(fileReferences, fileForPos)
    63  		}
    64  	}
    65  	expectedNumFiles := len(pass.Files)
    66  	foundNumFiles := len(fileReferences)
    67  	if expectedNumFiles != foundNumFiles {
    68  		return nil, InvalidNumberOfFilesInAnalysis{expectedNumFiles, foundNumFiles}
    69  	}
    70  
    71  	file, err := modinfo.FindModuleFromPass(pass)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	gciCfg, err := generateGciConfiguration(file.Path).Parse()
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	for _, file := range fileReferences {
    82  		filePath := file.Name()
    83  		unmodifiedFile, formattedFile, err := gci.LoadFormatGoFile(io.File{FilePath: filePath}, *gciCfg)
    84  		if err != nil {
    85  			return nil, err
    86  		}
    87  		fix, err := GetSuggestedFix(file, unmodifiedFile, formattedFile)
    88  		if err != nil {
    89  			return nil, err
    90  		}
    91  		if fix == nil {
    92  			// no difference
    93  			continue
    94  		}
    95  		pass.Report(analysis.Diagnostic{
    96  			Pos:            fix.TextEdits[0].Pos,
    97  			Message:        fmt.Sprintf("fix by `%s %s`", generateCmdLine(*gciCfg), filePath),
    98  			SuggestedFixes: []analysis.SuggestedFix{*fix},
    99  		})
   100  	}
   101  	return nil, nil
   102  }
   103  
   104  func generateGciConfiguration(modPath string) *config.YamlConfig {
   105  	fmtCfg := config.BoolConfig{
   106  		NoInlineComments: noInlineComments,
   107  		NoPrefixComments: noPrefixComments,
   108  		Debug:            false,
   109  		SkipGenerated:    skipGenerated,
   110  	}
   111  
   112  	var sectionStrings []string
   113  	if sectionsStr != "" {
   114  		sectionStrings = strings.Split(sectionsStr, SectionDelimiter)
   115  	}
   116  
   117  	var sectionSeparatorStrings []string
   118  	if sectionSeparatorsStr != "" {
   119  		sectionSeparatorStrings = strings.Split(sectionSeparatorsStr, SectionDelimiter)
   120  		fmt.Println(sectionSeparatorsStr)
   121  	}
   122  
   123  	return &config.YamlConfig{Cfg: fmtCfg, SectionStrings: sectionStrings, SectionSeparatorStrings: sectionSeparatorStrings, ModPath: modPath}
   124  }
   125  
   126  func generateCmdLine(cfg config.Config) string {
   127  	result := "gci write"
   128  
   129  	if cfg.BoolConfig.NoInlineComments {
   130  		result += " --NoInlineComments "
   131  	}
   132  
   133  	if cfg.BoolConfig.NoPrefixComments {
   134  		result += " --NoPrefixComments "
   135  	}
   136  
   137  	if cfg.BoolConfig.SkipGenerated {
   138  		result += " --skip-generated "
   139  	}
   140  
   141  	if cfg.BoolConfig.CustomOrder {
   142  		result += " --custom-order "
   143  	}
   144  
   145  	for _, s := range cfg.Sections.String() {
   146  		result += fmt.Sprintf(" --Section \"%s\" ", s)
   147  	}
   148  	for _, s := range cfg.SectionSeparators.String() {
   149  		result += fmt.Sprintf(" --SectionSeparator %s ", s)
   150  	}
   151  	return result
   152  }