Skip to content

Commit 8bd8c2f

Browse files
committed
Update documentation on adding new linters
1 parent ca2705f commit 8bd8c2f

File tree

1 file changed

+53
-41
lines changed

1 file changed

+53
-41
lines changed

docs/new-linter.md

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,70 +24,82 @@ Once you are within the `inspect.Preorder`, you can then implement the business
2424
The registry in the analysis package co-ordinates the initialization of all linters.
2525
Where linters have configuration, or are enabled/disabled by higher level configuration, the registry takes on making sure the linters are initialized correctly.
2626

27-
To enable the registry, each linter package must create an `Initializer` function that returns an `analysis.AnalyzerInitializer` interface (from `pkg/analysis`).
27+
To enable the registry, each linter package must create an `Initializer` function that returns an `initializer.AnalyzerInitializer` interface (from `pkg/analysis/initializer`).
2828

2929
It is expected that each linter package contain a file `initializer.go`, the content of this file should be as follows:
3030

3131
```go
32+
func init() {
33+
// Register the linter with the registry when the package is imported.
34+
kalanalysis.DefaultRegistry().RegisterLinter(Initializer())
35+
}
36+
3237
// Initializer returns the AnalyzerInitializer for this
3338
// Analyzer so that it can be added to the registry.
34-
func Initializer() initializer {
35-
return initializer{}
39+
func Initializer() initializer.AnalyzerInitializer {
40+
return initializer.NewInitializer(
41+
name, // A constant containing the name of the linter. This should be lowercase.
42+
initAnalyzer, // A function that returns the initialized Analyzer.
43+
true, // Whether the linter is enabled by default.
44+
)
3645
}
3746

38-
// intializer implements the AnalyzerInitializer interface.
39-
type initializer struct{}
40-
41-
// Name returns the name of the Analyzer.
42-
func (initializer) Name() string {
43-
return name
47+
// initAnalyzer returns the intialized Analyzer.
48+
func initAnalyzer(_ any) (*analysis.Analyzer, error) {
49+
return Analyzer, nil
4450
}
51+
```
4552

46-
// Init returns the intialized Analyzer.
47-
func (initializer) Init(cfg config.LintersConfig) (*analysis.Analyzer, error) {
48-
return newAnalyzer(cfg.MyLinterConfig)
49-
}
53+
This pattern allows the linter to be registered with the KAL registry, and allows the linter to be initialized.
5054

51-
// Default determines whether this Analyzer is on by default, or not.
52-
func (initializer) Default() bool {
53-
return true // or false
54-
}
55-
```
55+
Once you have created the `initializer.go` file, you will need to import the linter package in the `pkg/analysis/registration/registration.go` file.
5656

57-
This pattern allows the linter to be registered with the KAL registry, and allows the linter to be initialized with configuration.
57+
Once imported, the analyzer will be included in the linter builds.
5858

59-
Once you have created the `initializer.go` file, you will need to add the linter to the `pkg/analysis/registry.go` file.
59+
## Configuration
6060

61-
Add the initializer to the `NewRegistry` function, and it will then be included in the linter builds.
61+
Where the linter requires configuration, a slightly different pattern is used in `initializer.go`.
62+
This time, use `NewConfigurableInitializer` instead of `NewInitializer` and pass in a function to validate the linter configuration.
63+
You will also need to pass a function to initialize a new pointer to the configuration struct for the linter.
6264

6365
```go
64-
func NewRegistry() []*analysis.Analyzer {
65-
return []*analysis.Analyzer{
66-
// Add the new linter here
67-
mynewlinter.Initializer(),
68-
}
69-
}
70-
```
7166

72-
## Configuration
73-
74-
If the linter requires configuration, the configuration should be added to the `config` package.
67+
func init() {
68+
kalanalysis.DefaultRegistry().RegisterLinter(Initializer())
69+
}
7570

76-
Add a new structure (or structures) to the `linters_config.go` file.
77-
Include a new field for the top level configuration to the `LintersConfig` struct, using the name of the linter, in camel case for the `json` tag.
71+
// Initializer returns the AnalyzerInitializer for this
72+
// Analyzer so that it can be added to the registry.
73+
func Initializer() initializer.AnalyzerInitializer {
74+
return initializer.NewConfigurableInitializer(
75+
name, // A constant containing the name of the linter. This should be lowercase.
76+
initAnalyzer, // A function that returns the initialized Analyzer.
77+
true, // Whether the linter is enabled by default.
78+
func() any { return &Config{} }, // A function that returns a new pointer to the configuration struct for the linter.
79+
validateLintersConfig, // A function that validates the linter configuration.
80+
)
81+
}
7882

79-
Any options for the linter, should also be validated.
80-
Validation lives in the `validation` package.
83+
// initAnalyzer returns the intialized Analyzer.
84+
func initAnalyzer(cfg any) (*analysis.Analyzer, error) {
85+
cc, ok := cfg.(*ConditionsConfig)
86+
if !ok {
87+
return nil, fmt.Errorf("failed to initialize conditions analyzer: %w", initializer.NewIncorrectTypeError(cfg))
88+
}
8189

82-
Within the `ValidateLintersConfig` function, in the `linters_config.go` file, you will need to add
83-
a line as below, to include any configuration validation for the new linter.
84-
There are already examples of this in the file.
90+
return newAnalyzer(), nil
91+
}
8592

86-
```go
87-
fieldErrors = append(fieldErrors, validateMyNewLint(lc.MyNewLinter, fldPath.Child("myNewLinter"))...)
93+
// validateLintersConfig validates the linter configuration.
94+
func validateLintersConfig(cfg any, fldPath *field.Path) field.ErrorList {
95+
... // Validate the linter configuration.
96+
}
8897
```
8998

90-
The validations should use the `field.Error` pattern to provide consistent error messages.
99+
The configuration struct should be defined within the analyzer package alongside the code.
100+
101+
Validation should be implemented in the `validateLintersConfig` function to ensure that the configuration is valid.
102+
This validation function will be called before the `initAnalyzer` function is called.
91103

92104
## Helpers
93105

0 commit comments

Comments
 (0)