-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Add compiler_protocol_init rule #1101
Add compiler_protocol_init rule #1101
Conversation
Current coverage is 82.50% (diff: 97.43%)@@ master #1101 diff @@
==========================================
Files 152 153 +1
Lines 7406 7443 +37
Methods 0 0
Messages 0 0
Branches 0 0
==========================================
+ Hits 6103 6141 +38
+ Misses 1303 1302 -1
Partials 0 0
|
return [range] | ||
} | ||
|
||
private let initCallNames: Set<String> = { |
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.
Should this be made static
as well? It doesn't look like this will ever produce a different Set
.
There are a handful of other literal syntax protocols that may need to be handled in the same way:
|
Done in 7d6600a |
Is there a reason this rule shouldn't be called Also, I didn't actually confirm that the protocols listed in #1101 (comment) have the same remark about their initializers. Did you confirm that it was the case? |
private func violationRangesInFile(_ file: File, | ||
kind: SwiftExpressionKind, | ||
dictionary: [String: SourceKitRepresentable]) -> [NSRange] { | ||
guard kind == .call, |
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 are a few things that could be improved in this function IMO. Especially since many variables are computed in every loop iteration even though they can't change.
Consider incorporating some of these changes:
guard kind == .call,
- let name = dictionary["key.name"] as? String else {
- return []
+ let name = dictionary["key.name"] as? String,
+ let offset = (dictionary["key.offset"] as? Int64).flatMap({ Int($0) }),
+ let length = (dictionary["key.length"] as? Int64).flatMap({ Int($0) }) else {
+ return []
}
+ let arguments = dictionary.enclosedArguments.flatMap({ $0["key.name"] as? String })
+
for compilerProtocol in ExpressibleByCompiler.allProtocols {
- guard compilerProtocol.initCallNames.contains(name),
- case let arguments = dictionary.enclosedArguments.flatMap({ $0["key.name"] as? String }),
+ if compilerProtocol.initCallNames.contains(name),
compilerProtocol.matchArguments(arguments),
- let offset = (dictionary["key.offset"] as? Int64).flatMap({ Int($0) }),
- let length = (dictionary["key.length"] as? Int64).flatMap({ Int($0) }),
- let range = file.contents.bridge().byteRangeToNSRange(start: offset, length: length) else {
- continue
+ let range = file.contents.bridge().byteRangeToNSRange(start: offset, length: length) {
+ return [range]
}
-
- return [range]
}
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.
The only one that would actually be computed in every loop would be arguments
(since the other ones shouldn't really return nil
). However, computing it earlier actually makes the rule a bit slower because most of times a .call
won't be one of the forbidden ones, so we'd be computing arguments
unnecessarily.
} | ||
|
||
private struct ExpressibleByCompiler { | ||
let protocolName: String |
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.
private
?
byStringInterpolation, byDictionaryLiteral] | ||
|
||
func matchArguments(_ arguments: [String]) -> Bool { | ||
for item in self.arguments { |
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 feel strongly about this one, so ignore if you don't like:
return self.arguments.first(where: { $0 == arguments }) != nil
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 feel like the procedural style is more readable in this case 😅
Mainly because
I've checked and some of them contain the note, some don't. IMO that is probably a documentation omission and I don't see why one would call these methods directly. |
I had proposed that rule name based on Swift Evolution proposal SE-0115, which refers to these protocols as "Literal Syntax Protocols". |
@@ -70,6 +70,13 @@ | |||
[JP Simard](https://github.com/jpsim) | |||
[#1077](https://github.com/realm/SwiftLint/issues/1077) | |||
|
|||
* Add `compiler_protocol_init` rule that flags usage of initializers | |||
declared in protocols used by compiler such as `ExpressibleByArrayLiteral` |
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.
used by the compiler
Fixes #1096