-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Add opt-in unneeded_throws_rethrows
rule
#6069
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Generated by 🚫 Danger |
Source/SwiftLintBuiltInRules/Rules/Lint/UnneededThrowsRule.swift
Outdated
Show resolved
Hide resolved
@SimplyDanny Any thoughts on this? :) |
@tonyskansf It seems that some conflicts have arisen, would you mind resolving them? Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks very good already! You thought about many special cases already. The rule also makes sense. But adding it as opt-in is important as it can cause false positives in the context of protocol implementations (and maybe more occasions).
I've added a few comments. Please also check the findings reported by the OSS scan.
@@ -12,7 +12,9 @@ | |||
|
|||
### Enhancements | |||
|
|||
* None. | |||
* Add opt-in `unneeded_throws_rethrows` rules that triggers when declarations | |||
marked `throws`/`rethrows` never actually throw or call any throwing code. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please append two spaces to the end of the description. They are required to enforce a line break in the rendered Markdown.
static let description = RuleDescription( | ||
identifier: "unneeded_throws_rethrows", | ||
name: "Unneeded (re)throws keyword", | ||
description: "Non-throwing functions/variables should not be marked as `throws` or `rethrows`", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
description: "Non-throwing functions/variables should not be marked as `throws` or `rethrows`", | |
description: "Non-throwing functions/properties/closures should not be marked as `throws` or `rethrows`.", |
violations.append( | ||
ReasonedRuleViolation( | ||
position: throwsToken.positionAfterSkippingLeadingTrivia, | ||
reason: reason, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reason: reason, | |
reason: "Superfluous 'throws'; " + reason, |
if let closedScope = scopes.closeScope() { | ||
validateScope( | ||
closedScope, | ||
reason: "The closure type does not throw any error" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To keep it short:
reason: "The closure type does not throw any error" | |
reason: "closure type does not throw any error" |
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> { | ||
private var scopes = Stack<Scope>() | ||
|
||
override func visit(_: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a shortcut, you can use skippableDeclarations
to ignore declarations you don't need to consider.
catch is SomeError {} | ||
catch {} | ||
} | ||
"""), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should also be an example (especially a correction) making use of "typed throws".
children(viewMode: .sourceAccurate).contains { child in | ||
child.is(InitializerClauseSyntax.self) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
children(viewMode: .sourceAccurate).contains { child in | |
child.is(InitializerClauseSyntax.self) | |
} | |
initializer != nil |
Is that not possible?
|
||
var children = Set(typeAnnotation.children(viewMode: .sourceAccurate)) | ||
|
||
while let child = children.popFirst() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's dangerous. Consider this example:
let s: S<() throws -> Void> = S()
I think, you should be explicit about the syntax structure to match instead of iterating through all children.
reason: reason, | ||
correction: ReasonedRuleViolation.ViolationCorrection( | ||
start: throwsToken.positionAfterSkippingLeadingTrivia, | ||
end: throwsToken.endPosition, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to use endPositionBeforeTrailingTrivia
here to avoid removing trailing trivia.
} | ||
|
||
override func visitPost(_ node: FunctionCallExprSyntax) { | ||
if node.containsTaskDeclaration { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this exclusion of tasks to be honest. Could you explain?
You need to rebase onto |
Add a new opt-in rule that detects unnecessary
throws
, helping avoid misleading definitions and requirements for the caller to write unnecessary error handling.