Add disallow_default_parameter opt-in rule#6506
Add disallow_default_parameter opt-in rule#6506William-Laverty wants to merge 3 commits intorealm:mainfrom
disallow_default_parameter opt-in rule#6506Conversation
Generated by 🚫 Danger |
Adds a new opt-in rule that flags default parameter values on functions with certain access levels. By default, it disallows defaults on private and internal functions, encouraging explicit parameter passing at non-public API boundaries. Configurable via `disallowed_access_levels` to target any combination of `private`, `fileprivate`, `internal`, and `package`. Closes realm#6488
26e530a to
0081fe6
Compare
SimplyDanny
left a comment
There was a problem hiding this comment.
Thanks for the contribution!
There are a few remarks I'd like to have addressed before merging.
| name: "Disallow Default Parameter", | ||
| description: "Default parameter values should not be used in functions with certain access levels", | ||
| kind: .idiomatic, | ||
| nonTriggeringExamples: DisallowDefaultParameterRuleExamples.nonTriggeringExamples, |
There was a problem hiding this comment.
If there aren't too many examples, it's okay to inline them here.
| static let description = RuleDescription( | ||
| identifier: "disallow_default_parameter", | ||
| name: "Disallow Default Parameter", | ||
| description: "Default parameter values should not be used in functions with certain access levels", |
There was a problem hiding this comment.
| description: "Default parameter values should not be used in functions with certain access levels", | |
| description: "Default parameter values should not be used in functions with certain access levels.", |
and use a shorter reason with every violation mentioning the access level like
"Default parameter values should not be used in '<visibility>' functions"| identifier: "disallow_default_parameter", | ||
| name: "Disallow Default Parameter", | ||
| description: "Default parameter values should not be used in functions with certain access levels", | ||
| kind: .idiomatic, |
There was a problem hiding this comment.
Fits better:
| kind: .idiomatic, | |
| kind: .lint, |
|
|
||
| private func collectViolations( | ||
| modifiers: DeclModifierListSyntax, | ||
| signature: FunctionSignatureSyntax |
There was a problem hiding this comment.
If you use ParameterClauseSyntax here, you can implement the case for SubscriptDeclSyntax exactly like the other two.
| ) { | ||
| guard matchesDisallowedAccessLevel(modifiers) else { return } | ||
| for param in signature.parameterClause.parameters where param.defaultValue != nil { | ||
| violations.append(param.defaultValue!.positionAfterSkippingLeadingTrivia) |
There was a problem hiding this comment.
I'd prefer to have the default parameter unwrapped in an optional binding instead of relying on the where implicitly.
| key: "disallowed_access_levels", | ||
| postprocessor: { $0 = Set($0.sorted()) } | ||
| ) | ||
| private(set) var disallowedAccessLevels: Set<AccessLevel> = [.private, .internal] |
There was a problem hiding this comment.
Why do you suggest these two as defaults?
| private(set) var severityConfiguration = SeverityConfiguration<Parent>(.warning) | ||
| @ConfigurationElement( | ||
| key: "disallowed_access_levels", | ||
| postprocessor: { $0 = Set($0.sorted()) } |
There was a problem hiding this comment.
Postprocessor can be removed. Neither order nor uniqueness are important.
Incidentally, with Set($0.sorted()) sorting would have no effect. What you want is Set($0).sorted(), but then disallowedAccessLevels would need to be a list.
- Change kind from .idiomatic to .lint - Use per-violation reason with access level name - Unify subscript handling via ParameterClauseSyntax - Use optional binding instead of where + force unwrap - Remove postprocessor from configuration - Default to [.internal] only (most common implicit level) - Inline examples into rule description - Add CHANGELOG entry
New Rule
Adds a new opt-in rule
disallow_default_parameterthat flags default parameter values on functions with certain access levels.Motivation
As described in #6488, default parameter values on non-public functions can mask bugs when parameters aren't properly forwarded. This rule enforces explicit parameterization at internal API boundaries while allowing defaults on public APIs where consumers benefit from convenience.
Configuration
disallowed_access_levels: Array of access levels to flag. Default:["private", "internal"]private,fileprivate,internal,packageExamples
Triggering (default config):
Non-triggering:
Closes #6488