Skip to content

Commit 17bc613

Browse files
authored
Add WithRelatedCheckConfigs option to check additional check configs for unused plugins warning (#3550)
1 parent 463dc8e commit 17bc613

File tree

7 files changed

+109
-38
lines changed

7 files changed

+109
-38
lines changed

private/buf/cmd/buf/command/breaking/breaking.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/bufbuild/buf/private/buf/buffetch"
2525
"github.com/bufbuild/buf/private/bufpkg/bufanalysis"
2626
"github.com/bufbuild/buf/private/bufpkg/bufcheck"
27+
"github.com/bufbuild/buf/private/bufpkg/bufconfig"
2728
"github.com/bufbuild/buf/private/bufpkg/bufimage"
2829
"github.com/bufbuild/buf/private/pkg/app/appcmd"
2930
"github.com/bufbuild/buf/private/pkg/app/appext"
@@ -218,10 +219,19 @@ func run(
218219
len(againstImageWithConfigs),
219220
)
220221
}
222+
// We add all check configs (both lint and breaking) as related configs to check if plugins
223+
// have rules configured.
224+
// We allocated twice the size of imageWithConfigs for both lint and breaking configs.
225+
allCheckConfigs := make([]bufconfig.CheckConfig, 0, len(imageWithConfigs)*2)
226+
for _, imageWithConfig := range imageWithConfigs {
227+
allCheckConfigs = append(allCheckConfigs, imageWithConfig.LintConfig())
228+
allCheckConfigs = append(allCheckConfigs, imageWithConfig.BreakingConfig())
229+
}
221230
var allFileAnnotations []bufanalysis.FileAnnotation
222231
for i, imageWithConfig := range imageWithConfigs {
223232
breakingOptions := []bufcheck.BreakingOption{
224233
bufcheck.WithPluginConfigs(imageWithConfig.PluginConfigs()...),
234+
bufcheck.WithRelatedCheckConfigs(allCheckConfigs...),
225235
}
226236
if flags.ExcludeImports {
227237
breakingOptions = append(breakingOptions, bufcheck.BreakingWithExcludeImports())

private/buf/cmd/buf/command/config/internal/internal.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,14 @@ func lsRun(
243243
return syserror.Newf("unknown FileVersion: %v", fileVersion)
244244
}
245245
var checkConfig bufconfig.CheckConfig
246+
// We add all check configs (both lint and breaking) as related configs to check if plugins
247+
// have rules configured.
248+
// We allocated twice the size of moduleConfigs for both lint and breaking configs.
249+
allCheckConfigs := make([]bufconfig.CheckConfig, 0, len(moduleConfigs)*2)
250+
for _, moduleConfig := range moduleConfigs {
251+
allCheckConfigs = append(allCheckConfigs, moduleConfig.LintConfig())
252+
allCheckConfigs = append(allCheckConfigs, moduleConfig.BreakingConfig())
253+
}
246254
switch ruleType {
247255
case check.RuleTypeLint:
248256
checkConfig = moduleConfig.LintConfig()
@@ -253,6 +261,7 @@ func lsRun(
253261
}
254262
configuredRuleOptions := []bufcheck.ConfiguredRulesOption{
255263
bufcheck.WithPluginConfigs(bufYAMLFile.PluginConfigs()...),
264+
bufcheck.WithRelatedCheckConfigs(allCheckConfigs...),
256265
}
257266
rules, err = checkClient.ConfiguredRules(
258267
ctx,

private/buf/cmd/buf/command/lint/lint.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/bufbuild/buf/private/buf/bufctl"
2424
"github.com/bufbuild/buf/private/bufpkg/bufanalysis"
2525
"github.com/bufbuild/buf/private/bufpkg/bufcheck"
26+
"github.com/bufbuild/buf/private/bufpkg/bufconfig"
2627
"github.com/bufbuild/buf/private/pkg/app/appcmd"
2728
"github.com/bufbuild/buf/private/pkg/app/appext"
2829
"github.com/bufbuild/buf/private/pkg/stringutil"
@@ -143,9 +144,18 @@ func run(
143144
return err
144145
}
145146
var allFileAnnotations []bufanalysis.FileAnnotation
147+
// We add all check configs (both lint and breaking) as related configs to check if plugins
148+
// have rules configured.
149+
// We allocated twice the size of imageWithConfigs for both lint and breaking configs.
150+
allCheckConfigs := make([]bufconfig.CheckConfig, 0, len(imageWithConfigs)*2)
151+
for _, imageWithConfig := range imageWithConfigs {
152+
allCheckConfigs = append(allCheckConfigs, imageWithConfig.LintConfig())
153+
allCheckConfigs = append(allCheckConfigs, imageWithConfig.BreakingConfig())
154+
}
146155
for _, imageWithConfig := range imageWithConfigs {
147156
lintOptions := []bufcheck.LintOption{
148157
bufcheck.WithPluginConfigs(imageWithConfig.PluginConfigs()...),
158+
bufcheck.WithRelatedCheckConfigs(allCheckConfigs...),
149159
}
150160
if err := checkClient.Lint(
151161
ctx,

private/bufpkg/bufcheck/bufcheck.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,25 @@ type ConfiguredRulesOption interface {
127127
applyToConfiguredRules(*configuredRulesOptions)
128128
}
129129

130+
// LintBreakingAndConfiguredRulesOption is an option for Lint, Breaking, and ConfiguredRules.
131+
type LintBreakingAndConfiguredRulesOption interface {
132+
LintOption
133+
BreakingOption
134+
ConfiguredRulesOption
135+
}
136+
137+
// WithRelatedCheckConfigs returns a new LintBreakingAndConfiguredRulesOption that allows
138+
// the caller to provide additional related check configs. This allows the client to check
139+
// for unused plugins across related check configs and provide users with a warning if the
140+
// plugin is unused in all check configs.
141+
//
142+
// The default is to only check the configs provided to the client for Lint, Breaking, or ConfiguredRules.
143+
func WithRelatedCheckConfigs(relatedCheckConfigs ...bufconfig.CheckConfig) LintBreakingAndConfiguredRulesOption {
144+
return &relatedCheckConfigsOption{
145+
relatedCheckConfigs: relatedCheckConfigs,
146+
}
147+
}
148+
130149
// AllRulesOption is an option for AllRules.
131150
type AllRulesOption interface {
132151
applyToAllRules(*allRulesOptions)

private/bufpkg/bufcheck/client.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func (c *client) Lint(
104104
if err != nil {
105105
return err
106106
}
107-
config, err := configForLintConfig(lintConfig, allRules, allCategories)
107+
config, err := configForLintConfig(lintConfig, allRules, allCategories, lintOptions.relatedCheckConfigs)
108108
if err != nil {
109109
return err
110110
}
@@ -168,6 +168,7 @@ func (c *client) Breaking(
168168
allRules,
169169
allCategories,
170170
breakingOptions.excludeImports,
171+
breakingOptions.relatedCheckConfigs,
171172
)
172173
if err != nil {
173174
return err
@@ -229,7 +230,7 @@ func (c *client) ConfiguredRules(
229230
if err != nil {
230231
return nil, err
231232
}
232-
rulesConfig, err := rulesConfigForCheckConfig(checkConfig, allRules, allCategories, ruleType)
233+
rulesConfig, err := rulesConfigForCheckConfig(checkConfig, allRules, allCategories, ruleType, configuredRulesOptions.relatedCheckConfigs)
233234
if err != nil {
234235
return nil, err
235236
}
@@ -476,24 +477,27 @@ func checkCommentLineForCheckIgnore(
476477
}
477478

478479
type lintOptions struct {
479-
pluginConfigs []bufconfig.PluginConfig
480+
pluginConfigs []bufconfig.PluginConfig
481+
relatedCheckConfigs []bufconfig.CheckConfig
480482
}
481483

482484
func newLintOptions() *lintOptions {
483485
return &lintOptions{}
484486
}
485487

486488
type breakingOptions struct {
487-
pluginConfigs []bufconfig.PluginConfig
488-
excludeImports bool
489+
pluginConfigs []bufconfig.PluginConfig
490+
excludeImports bool
491+
relatedCheckConfigs []bufconfig.CheckConfig
489492
}
490493

491494
func newBreakingOptions() *breakingOptions {
492495
return &breakingOptions{}
493496
}
494497

495498
type configuredRulesOptions struct {
496-
pluginConfigs []bufconfig.PluginConfig
499+
pluginConfigs []bufconfig.PluginConfig
500+
relatedCheckConfigs []bufconfig.CheckConfig
497501
}
498502

499503
func newConfiguredRulesOptions() *configuredRulesOptions {
@@ -553,3 +557,19 @@ func (p *pluginConfigsOption) applyToAllRules(allRulesOptions *allRulesOptions)
553557
func (p *pluginConfigsOption) applyToAllCategories(allCategoriesOptions *allCategoriesOptions) {
554558
allCategoriesOptions.pluginConfigs = append(allCategoriesOptions.pluginConfigs, p.pluginConfigs...)
555559
}
560+
561+
type relatedCheckConfigsOption struct {
562+
relatedCheckConfigs []bufconfig.CheckConfig
563+
}
564+
565+
func (r *relatedCheckConfigsOption) applyToLint(lintOptions *lintOptions) {
566+
lintOptions.relatedCheckConfigs = append(lintOptions.relatedCheckConfigs, r.relatedCheckConfigs...)
567+
}
568+
569+
func (r *relatedCheckConfigsOption) applyToBreaking(breakingOptions *breakingOptions) {
570+
breakingOptions.relatedCheckConfigs = append(breakingOptions.relatedCheckConfigs, r.relatedCheckConfigs...)
571+
}
572+
573+
func (r *relatedCheckConfigsOption) applyToConfiguredRules(configuredRulesOptions *configuredRulesOptions) {
574+
configuredRulesOptions.relatedCheckConfigs = append(configuredRulesOptions.relatedCheckConfigs, r.relatedCheckConfigs...)
575+
}

private/bufpkg/bufcheck/config.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ func configForLintConfig(
2828
lintConfig bufconfig.LintConfig,
2929
allRules []Rule,
3030
allCategories []Category,
31+
relatedCheckConfigs []bufconfig.CheckConfig,
3132
) (*config, error) {
32-
rulesConfig, err := rulesConfigForCheckConfig(lintConfig, allRules, allCategories, check.RuleTypeLint)
33+
rulesConfig, err := rulesConfigForCheckConfig(lintConfig, allRules, allCategories, check.RuleTypeLint, relatedCheckConfigs)
3334
if err != nil {
3435
return nil, err
3536
}
@@ -48,8 +49,9 @@ func configForBreakingConfig(
4849
allRules []Rule,
4950
allCategories []Category,
5051
excludeImports bool,
52+
relatedCheckConfigs []bufconfig.CheckConfig,
5153
) (*config, error) {
52-
rulesConfig, err := rulesConfigForCheckConfig(breakingConfig, allRules, allCategories, check.RuleTypeBreaking)
54+
rulesConfig, err := rulesConfigForCheckConfig(breakingConfig, allRules, allCategories, check.RuleTypeBreaking, relatedCheckConfigs)
5355
if err != nil {
5456
return nil, err
5557
}

private/bufpkg/bufcheck/rules_config.go

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func rulesConfigForCheckConfig(
3333
allRules []Rule,
3434
allCategories []Category,
3535
ruleType check.RuleType,
36+
relatedCheckConfigs []bufconfig.CheckConfig,
3637
) (*rulesConfig, error) {
3738
return newRulesConfig(
3839
checkConfig.UseIDsAndCategories(),
@@ -42,6 +43,7 @@ func rulesConfigForCheckConfig(
4243
allRules,
4344
allCategories,
4445
ruleType,
46+
relatedCheckConfigs,
4547
)
4648
}
4749

@@ -94,13 +96,9 @@ type rulesConfig struct {
9496
//
9597
// A plugin is unused if no rules from the plugin are configured.
9698
//
97-
// This map will *not* contain plugins that have Rules with RuleTypes other than the given
98-
// RuleType. We need to account for this to properly print warnings. It is possible that
99-
// a plugin is not used in the lint section, for example, but does have breaking rules configured.
100-
// In client.Lint and client.Breaking, we only have the Lint or Breaking config, and we don't know
101-
// the state of the other config. If a plugin is unused for lint, but has both lint and breaking
102-
// Rules, we don't warn for this plugin, as it may have had rules configured in breaking that
103-
// we haven't accounted for.
99+
// The caller can provide additional related check configs to check if the plugin has rules
100+
// configured. If the plugin has rules configured in any of the additional check configs
101+
// provided, then we don't warn.
104102
//
105103
// The Rule IDs will be sorted.
106104
// This will only contain RuleIDs of the given RuleType.
@@ -124,6 +122,7 @@ func newRulesConfig(
124122
allRules []Rule,
125123
allCategories []Category,
126124
ruleType check.RuleType,
125+
relatedCheckConfigs []bufconfig.CheckConfig,
127126
) (*rulesConfig, error) {
128127
allRulesForType := rulesForType(allRules, ruleType)
129128
if len(allRulesForType) == 0 {
@@ -292,7 +291,6 @@ func newRulesConfig(
292291
return nil, err
293292
}
294293

295-
pluginNameToOtherRuleTypes := getPluginNameToOtherRuleTypes(allRules, ruleType)
296294
// This map initially contains a map from plugin name to ALL Rule IDs, but we will
297295
// then delete the used plugin names, and then delete the plugins with other rule types.
298296
//
@@ -305,11 +303,31 @@ func newRulesConfig(
305303
delete(unusedPluginNameToRuleIDs, pluginName)
306304
}
307305
}
308-
for pluginName := range unusedPluginNameToRuleIDs {
309-
// If the rule had other plugin types (see the comment on UnusedPluginNameToRuleIDs),
310-
// delete the plugin name from the map
311-
if _, ok := pluginNameToOtherRuleTypes[pluginName]; ok {
312-
delete(unusedPluginNameToRuleIDs, pluginName)
306+
// We check additional check configs. If rules are set in the related check configs, then
307+
// the plugin is not considered unused. We check against all rules for all rule types.
308+
allRuleIDToRule, err := getIDToRuleOrCategory(allRules)
309+
if err != nil {
310+
return nil, err
311+
}
312+
allRuleIDToCategoryIDs, err := getRuleIDToCategoryIDs(allRules)
313+
if err != nil {
314+
return nil, err
315+
}
316+
allCategoryIDToRuleIDs := getCategoryIDToRuleIDs(allRuleIDToCategoryIDs)
317+
for _, checkConfig := range relatedCheckConfigs {
318+
checkConfigUseRuleIDs, err := transformRuleOrCategoryIDsToRuleIDs(
319+
checkConfig.UseIDsAndCategories(),
320+
allRuleIDToCategoryIDs,
321+
allCategoryIDToRuleIDs,
322+
)
323+
if err != nil {
324+
return nil, err
325+
}
326+
for _, checkConfigRuleID := range checkConfigUseRuleIDs {
327+
// If a rule from a non-builtin plugin is found, then we remove it from the unused plugins.
328+
if rule, ok := allRuleIDToRule[checkConfigRuleID]; rule.PluginName() != "" && ok {
329+
delete(unusedPluginNameToRuleIDs, rule.PluginName())
330+
}
313331
}
314332
}
315333

@@ -422,23 +440,6 @@ func getIDToRuleOrCategory[R RuleOrCategory](ruleOrCategories []R) (map[string]R
422440
return m, nil
423441
}
424442

425-
func getPluginNameToOtherRuleTypes(allRules []Rule, ruleType check.RuleType) map[string]map[check.RuleType]struct{} {
426-
m := make(map[string]map[check.RuleType]struct{})
427-
for _, rule := range allRules {
428-
if pluginName := rule.PluginName(); pluginName != "" {
429-
if rule.Type() != ruleType {
430-
otherRuleTypes, ok := m[pluginName]
431-
if !ok {
432-
otherRuleTypes = make(map[check.RuleType]struct{})
433-
m[pluginName] = otherRuleTypes
434-
}
435-
otherRuleTypes[rule.Type()] = struct{}{}
436-
}
437-
}
438-
}
439-
return m
440-
}
441-
442443
func getPluginNameToRuleOrCategoryIDs[R RuleOrCategory](ruleOrCategories []R) map[string][]string {
443444
m := make(map[string][]string)
444445
for _, ruleOrCategory := range ruleOrCategories {

0 commit comments

Comments
 (0)