mirror of
https://github.com/scratchfoundation/golangci-lint.git
synced 2025-08-28 22:28:43 -04:00
dev: rewrite linters Manager (#4419)
This commit is contained in:
parent
26f8088b38
commit
b14d05cdb4
27 changed files with 1749 additions and 1825 deletions
131
pkg/lint/lintersdb/builder_plugin.go
Normal file
131
pkg/lint/lintersdb/builder_plugin.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
package lintersdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"plugin"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
|
||||
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||
"github.com/golangci/golangci-lint/pkg/logutils"
|
||||
)
|
||||
|
||||
type AnalyzerPlugin interface {
|
||||
GetAnalyzers() []*analysis.Analyzer
|
||||
}
|
||||
|
||||
// PluginBuilder builds the custom linters (plugins) based on the configuration.
|
||||
type PluginBuilder struct {
|
||||
log logutils.Log
|
||||
}
|
||||
|
||||
// NewPluginBuilder creates new PluginBuilder.
|
||||
func NewPluginBuilder(log logutils.Log) *PluginBuilder {
|
||||
return &PluginBuilder{log: log}
|
||||
}
|
||||
|
||||
// Build loads custom linters that are specified in the golangci-lint config file.
|
||||
func (b *PluginBuilder) Build(cfg *config.Config) []*linter.Config {
|
||||
if cfg == nil || b.log == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var linters []*linter.Config
|
||||
|
||||
for name, settings := range cfg.LintersSettings.Custom {
|
||||
lc, err := b.loadConfig(cfg, name, settings)
|
||||
if err != nil {
|
||||
b.log.Errorf("Unable to load custom analyzer %s:%s, %v", name, settings.Path, err)
|
||||
} else {
|
||||
linters = append(linters, lc)
|
||||
}
|
||||
}
|
||||
|
||||
return linters
|
||||
}
|
||||
|
||||
// loadConfig loads the configuration of private linters.
|
||||
// Private linters are dynamically loaded from .so plugin files.
|
||||
func (b *PluginBuilder) loadConfig(cfg *config.Config, name string, settings config.CustomLinterSettings) (*linter.Config, error) {
|
||||
analyzers, err := b.getAnalyzerPlugin(cfg, settings.Path, settings.Settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.log.Infof("Loaded %s: %s", settings.Path, name)
|
||||
|
||||
customLinter := goanalysis.NewLinter(name, settings.Description, analyzers, nil).
|
||||
WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
|
||||
linterConfig := linter.NewConfig(customLinter).
|
||||
WithEnabledByDefault().
|
||||
WithLoadForGoAnalysis().
|
||||
WithURL(settings.OriginalURL)
|
||||
|
||||
return linterConfig, nil
|
||||
}
|
||||
|
||||
// getAnalyzerPlugin loads a private linter as specified in the config file,
|
||||
// loads the plugin from a .so file,
|
||||
// and returns the 'AnalyzerPlugin' interface implemented by the private plugin.
|
||||
// An error is returned if the private linter cannot be loaded
|
||||
// or the linter does not implement the AnalyzerPlugin interface.
|
||||
func (b *PluginBuilder) getAnalyzerPlugin(cfg *config.Config, path string, settings any) ([]*analysis.Analyzer, error) {
|
||||
if !filepath.IsAbs(path) {
|
||||
// resolve non-absolute paths relative to config file's directory
|
||||
path = filepath.Join(cfg.GetConfigDir(), path)
|
||||
}
|
||||
|
||||
plug, err := plugin.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
analyzers, err := b.lookupPlugin(plug, settings)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("lookup plugin %s: %w", path, err)
|
||||
}
|
||||
|
||||
return analyzers, nil
|
||||
}
|
||||
|
||||
func (b *PluginBuilder) lookupPlugin(plug *plugin.Plugin, settings any) ([]*analysis.Analyzer, error) {
|
||||
symbol, err := plug.Lookup("New")
|
||||
if err != nil {
|
||||
analyzers, errP := b.lookupAnalyzerPlugin(plug)
|
||||
if errP != nil {
|
||||
return nil, errors.Join(err, errP)
|
||||
}
|
||||
|
||||
return analyzers, nil
|
||||
}
|
||||
|
||||
// The type func cannot be used here, must be the explicit signature.
|
||||
constructor, ok := symbol.(func(any) ([]*analysis.Analyzer, error))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("plugin does not abide by 'New' function: %T", symbol)
|
||||
}
|
||||
|
||||
return constructor(settings)
|
||||
}
|
||||
|
||||
func (b *PluginBuilder) lookupAnalyzerPlugin(plug *plugin.Plugin) ([]*analysis.Analyzer, error) {
|
||||
symbol, err := plug.Lookup("AnalyzerPlugin")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.log.Warnf("plugin: 'AnalyzerPlugin' plugins are deprecated, please use the new plugin signature: " +
|
||||
"https://golangci-lint.run/contributing/new-linters/#create-a-plugin")
|
||||
|
||||
analyzerPlugin, ok := symbol.(AnalyzerPlugin)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("plugin does not abide by 'AnalyzerPlugin' interface: %T", symbol)
|
||||
}
|
||||
|
||||
return analyzerPlugin.GetAnalyzers(), nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue