-
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
Adds AttributeRule #847
Adds AttributeRule #847
Conversation
} | ||
|
||
private func validateTestableImport(file: File) -> [StyleViolation] { | ||
let pattern = "@testable[\n]+\\s*import*" |
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's no info in AST about imports:
$ sourcekitten structure --text "@testable import Module"
{
"key.offset" : 0,
"key.diagnostic_stage" : "source.diagnostic.stage.swift.parse",
"key.length" : 23
}
let previousAttributes = try attributesFromPreviousLines(lineNumber - 1, file: file) | ||
|
||
let alwaysInSameLineAttributes = ["@IBAction", "@NSManaged"] | ||
let alwaysInNewLineAttributes = ["@available"] |
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.
maybe these should be configured?
] | ||
} | ||
|
||
private func attributesFromPreviousLines(lineNumber: Int, file: File) throws -> [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.
I don't particularly like this, but it was the only way I figured out to get the texts for the attributes, as there aren't exposed on AST: #846 (comment)
When running the tests on |
The failing cases are: "@GKInspectable\n var maxSpeed: Float"
"@discardableResult func a() -> Int"
"@objc\n @discardableResult func a() -> Int"
"@objc\n\n @discardableResult\n func a() -> Int" I think they're failing because they were only introduced on Swift 3 (at least |
|
Thanks for explaining it, now it makes sense! Should we wait on this until we support Swift 3.0 officially then? |
IMO, we can use |
Current coverage is 82.00% (diff: 94.78%)@@ master #847 diff @@
==========================================
Files 123 127 +4
Lines 5853 6263 +410
Methods 0 0
Messages 0 0
Branches 0 0
==========================================
+ Hits 4749 5136 +387
- Misses 1104 1127 +23
Partials 0 0
|
@marcelofabri thanks for your work on this. What's the next step? |
I need to detect and handle attributes with parameters (I think only Also, it'd be nice to have some feedback in the rules applied, as their reflect mostly my opinion + what I found in the docs examples. |
|
|
@jpsim This is ready for review. It got waaaay more complex than I thought it would be, so feel free to throw any ideas or suggestions to make it better 💯 |
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.
Great stuff! I have lots of comments, but I'm generally very happy to see how you did this.
@@ -43,6 +43,23 @@ | |||
[Marcelo Fabri](https://github.com/marcelofabri) | |||
[#883](https://github.com/realm/SwiftLint/issues/883) | |||
|
|||
* Add `AttributesRule` which validates if an attribute (`@objc`, `@IBOutlet`, | |||
`@discardableResult`, etc) is on the right position: |
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.
"in the right position"
- if it's applied to a type or function, it should be on the line above | ||
- if it's applied to an import (the only option is `@testable import`), | ||
it should be on the same line. | ||
You can also configure what attributes should be always in a new line or in |
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.
"on a new line or on the same line"
- if it's applied to an import (the only option is `@testable import`), | ||
it should be on the same line. | ||
You can also configure what attributes should be always in a new line or in | ||
the same line as the declaration with the `always_in_same_line` and |
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.
always_on_same_line
it should be on the same line. | ||
You can also configure what attributes should be always in a new line or in | ||
the same line as the declaration with the `always_in_same_line` and | ||
`always_in_new_line` keys. |
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.
always_on_line_above
?
|
||
private func validateTestableImport(file: File) -> [StyleViolation] { | ||
let pattern = "@testable[\n]+\\s*import" | ||
let excludingKinds = SyntaxKind.commentAndStringKinds() |
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.
@testable
is always a source.lang.swift.syntaxtype.attribute.builtin
and import
is always a source.lang.swift.syntaxtype.keyword
, so you should use those rather than excluding comments and strings.
} else { | ||
|
||
// ignore whitelisted attributes | ||
let operation: Set<String> -> Set<String> -> 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.
operation
is a very generic name for this. Can you find a better name?
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 couldn't find a better name 😅
Maybe we should drop this clever way and just have an if with two paths?
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.
maybe call this let excludeWhitelistedAttributes
?
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 know, the more I think the more I realize this is too clever. Are you ok with splitting it in two paths?
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.
sure
StyleViolation(ruleDescription: self.dynamicType.description, | ||
severity: configuration.severityConfiguration.severity, | ||
location: location | ||
) |
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.
open parens hugs the parameters, so should the closing one.
|
||
#if swift(>=3.0) | ||
let swift3Only = [ | ||
"@GKInspectable var maxSpeed: Float", |
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.
indentation
|
||
#if swift(>=3.0) | ||
let swift3Only = [ | ||
"@GKInspectable\n ↓var maxSpeed: Float", |
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.
indentation
@@ -40,7 +40,7 @@ public struct CyclomaticComplexityRule: ASTRule, ConfigurationProviderRule { | |||
|
|||
public func validateFile(file: File, kind: SwiftDeclarationKind, | |||
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { | |||
if !functionKinds.contains(kind) { | |||
if !SwiftDeclarationKind.functionKinds().contains(kind) { |
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.
sweet refactoring!
previousAttributes.intersect(alwaysOnNewLineAttributes) | ||
) | ||
)(attributesTokens.intersect(alwaysOnSameLineAttributes)) | ||
let attributesAfterWhitelist: 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.
Yeah, this is definitely clearer!
@jpsim I'll take a look at this, thanks for letting me know! |
@jpsim I was able to reduce the violations to 10:
which are all I'll commit it shortly. |
Can you please rebase this so we can confirm it builds and passes tests on Linux? |
The tests are failing on Linux because of guard let config = makeConfig(ruleConfiguration, ruleDescription.identifier) else {
XCTFail()
return
} Any ideas? 😬 UPDATE: I was able to fix in fd09fde by using |
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 good, but I want to run on Realm again.
// AttributesRule.swift | ||
// SwiftLint | ||
// | ||
// Created by Marcelo Fabri on 10/15/16. |
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.
12/12/16?
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.
this is right actually 😅
// AttributesRulesExamples.swift | ||
// SwiftLint | ||
// | ||
// Created by Marcelo Fabri on 09/12/16. |
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.
12/12/16?
// AttributesConfiguration.swift | ||
// SwiftLint | ||
// | ||
// Created by Marcelo Fabri on 26/11/16. |
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.
12/12/16?
Personally, I'd keep attributes_rule:
always_on_same_line:
- '@objc'
- '@nonobjc' am I missing something? |
@jpsim I've checked why violations are still being reported and almost all of them seems to be because the attribute had a parameter. Another one was because there was two properties without blank lines between them, so the algorithm thought that the attributes from both of them belonged to the last one:
I'll try to fix these soon. |
return false | ||
} | ||
|
||
return ["func", "var", "let"].contains(keyword) |
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.
this probably could be a bit safer if I used AST, but I thought it would be more complex and slower, since I already had the tokens.
@jpsim OK, so I had another look on this and fixed a bug in e4a556d. The remaining violations of this rule on Realm with the updated @objc(RealmSwiftObjectUtil)
public class ObjectUtil We could update the rule implementation to prioritize the "attributes with parameters should be on their own line" over the In reality, there're so many cases that this rule is probably the most complex one in SwiftLint, so I understand if you feel we shouldn't merge this or make it opt-in. |
I'm thinking opt-in might make sense for those reasons. |
I've updated it to be opt-in and also renamed the identifier to |
@@ -26,7 +26,7 @@ public struct AttributesRule: ASTRule, ConfigurationProviderRule { | |||
public init() {} | |||
|
|||
public static let description = RuleDescription( | |||
identifier: "attributes_rule", | |||
identifier: "attributes", |
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 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.
👍 this looks good to me now. @marcelofabri I'll let you merge this when you're done working on it.
Let's just wait for CI to be sure 😅 |
🎉 congrats @marcelofabri, I know this was a massive undertaking. |
In short, here's the logic I've implemented:
@IBAction
or@NSManaged
, it should always be onthe same line as the declaration
the declaration
@testable import
),it should be on the same line.
Check #846 for more details on the idea.
TODO:
@objc