Skip to content
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

Rebased to nn-swift-4.1 #2

Closed
wants to merge 21 commits into from

Conversation

dirtydanee
Copy link

@dirtydanee dirtydanee commented Mar 5, 2018

As we discussed, i have rebased to nn-swift-4.1.

I would have a couple of questions regarding your implementation. I thought this PR could be a good place to discuss it.

First question: Shall i rather rebase you original branch attibute_and_modifier_order to nn-swift-4.1, than open this PR? 😜

masters3d and others added 16 commits July 4, 2017 12:09
```
opt_in_rules:
  - modifiers_order

modifiers_order:
  before_acl: ["override"]
  after_acl: ["class"]
```
> warning: initializer 'init(configuration:)' nearly matches defaulted requirement 'init(configuration:)' of protocol 'Rule'
> note: candidate has non-matching type '(configuration: SeverityLevelsConfiguration)'
> note: move 'init(configuration:)' to an extension to silence this warning
> SwiftLintFramework.Rule:5:12: note: requirement 'init(configuration:)' declared here
>    public init(configuration: Any) throws
```
opt_in_rules:
  - modifiers_order

modifiers_order:
  before_acl: ["override"]
  after_acl: ["class"]
```
@masters3d
Copy link
Owner

Yeah, lets rebase. If the rebase goes well, go ahead a force push this to my branch. I made a back up on github. Then we can see the changes on the swiftlints PR.

@masters3d
Copy link
Owner

We can also point the PR on swift lint to the 4.1 PR

@dirtydanee
Copy link
Author

I have rebased + fixed tests. Force pushed to origin. Could you retarget the PR on SwiftLint?

@masters3d
Copy link
Owner

retargeted

@dirtydanee
Copy link
Author

dirtydanee commented Mar 6, 2018

My basic idea on how to solve this rule would be something like the following using this example:

@objc \n override public private(set) weak var foo: Bar?

Will produce this:

["key.diagnostic_stage": "source.diagnostic.stage.swift.parse", 
"key.substructure": [
	["key.attributes": [
			["key.attribute": "source.decl.attribute.weak"], 
                         ["key.attribute": "source.decl.attribute.override"],
			["key.attribute": "source.decl.attribute.objc"]
		], 
	"key.nameoffset": 37, 
	"key.accessibility": "source.lang.swift.accessibility.public", 
	"key.length": 26, 
	"key.typename": "Bar?", 
	"key.name": "foo", 
	"key.kind": "source.lang.swift.decl.var.global", 
	"key.offset": 33, 
	"key.namelength": 8, 
	"key.setter_accessibility": "source.lang.swift.accessibility.private"]
], 
"key.offset": 0, 
"key.length": 59]

We have:

@objc -> "key.attribute": "source.decl.attribute.objc"
public ->  "key.accessibility": "source.lang.swift.accessibility.public"
override -> "key.attribute": "source.decl.attribute.override"
private(set) ->  "key.setter_accessibility": "source.lang.swift.accessibility.private"
weak ->  "key.attribute": "source.decl.attribute.weak"
var ->  "key.kind": "source.lang.swift.decl.var.global"

Using the info above and switching to ASTRule observing SwiftDeclarationKind, we could get the line with the range containing key.offset from the file. Than break down the line into an array of String or something, and take a look if the order is as it is in ModifiersOrderConfiguration.

In ModifiersOrderConfiguration, we should create groups like it was mentioned:

  • ACLs
  • ACLs for setter
  • override ( ? )
  • final
  • mutating/nonmutating
  • static/class
  • weak/unowned

If i understand you approach correctly, you are doing something similar. What i can not understand, what does the extractContinuousSections function do.

  • Do you think my approach sounds okay, or would you prefer to somehow improve your initial idea?
  • You have an idea, why did the reviewer of the original PR thought of the original solution as a bit fragile?
  • What should i be paying extra attention to or what could be the pitfalls when writing this rule?
  • What could i do with the failing tests on the original PR? I am not sure how could i trigger them locally.

for token in tokens {
// static & class are not builtin attibutes so we check every keyword for static || class
if token.type == keyword,
token.length == "static".bridge().length || token.length == "class".bridge().length,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this part here is fragile because “static” is hard coded.

case "static":
tokenSection.append(token)
// filter out `class` when used as a declaration
case "class" :
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hard coded here too which makes it fragile.

]
)

private func extractContinuousSections(from tokens: [SyntaxToken], in file: File) -> [[SyntaxToken]] {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I can remember right this will give you have arrays where builtins, keywords happen. As in weak override perhaps is one continuous sub array of same type of keywords.

private func extractContinuousSections(from tokens: [SyntaxToken], in file: File) -> [[SyntaxToken]] {
var builtInTokenSections = [[SyntaxToken]]()
var tokenSection = [SyntaxToken]()
let builtIn = "source.lang.swift.syntaxtype.attribute.builtin"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are facilities to get this information from source kit. Pretty much I think everything hard coded like this needs to go.

return builtInTokenSections.filter({ $0.count > 1 })
}

private func isAccessControlLabel(_ input: String) -> Bool {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another part that may be fragile. I could not find the facilities to get this information from the source kit.

@masters3d
Copy link
Owner

sounds good. I am not sure about the groupings. Naming is hard. I opted for the config to pivot around ACL (including setters) so you would just need to specify which “keywords” should go before or after ACL. If you only specify before_acl then everything else is assumed it goes after the ACL.

Not sure if that is the best way though. Perhaps if you want to tackle getting it to depend on the new 4.1 functionality first? Then we can ask the community upstream what would be best.

@dirtydanee
Copy link
Author

dirtydanee commented Mar 6, 2018

I see your point with the grouping, yeah, it could be trouble. Lets ask then the community, what do they think, or how to approach that part.

Looking at the PRs from Marcelo, ACL is added to the key.attributes and the offset should be there too. Using this we could simply remove the part of getting the line and tralala, just compare offSets. It should a lot more simple than my first idea.

However, i do not get those updates, but I have:

  • Xcode 9.3 Beta 4
  • I have switched SwiftLint, SwiftLintFramework and SwiftLintFrameworkTests to use Swift 4.1
  • The package file contains a dependency of SourceKitten from nn-swift-4.1.

What am i missing to really use 4.1 ? -> command line tools..

🎉I get this structure with 4.1 🎉

[
	"key.attributes": [
		[
		"key.attribute": "source.decl.attribute.weak", 
		"key.offset": 37, 
		"key.length": 4
		], 
		[
		"key.attribute": "source.decl.attribute.setter_access.private", 
		"key.offset": 24, 
		"key.length": 12
		], 
		["key.attribute": "source.decl.attribute.public", 
		"key.offset": 17, 
		"key.length": 6
		], 
		[
		"key.attribute": "source.decl.attribute.override", 
		"key.offset": 8, 
		"key.length": 8], 
		[
		"key.attribute": "source.decl.attribute.objc", 
		"key.offset": 0, 
		"key.length": 5
		]
	], 
	"key.nameoffset": 46, 
	"key.accessibility": "source.lang.swift.accessibility.public", 
	"key.length": 13, 
	"key.typename": "Bar?", 
	"key.name": "foo", 
	"key.kind": "source.lang.swift.decl.var.global", 
	"key.offset": 42, 
	"key.namelength": 3, 
	"key.setter_accessibility": "source.lang.swift.accessibility.private"
]

This means, we can drop the idea of having to search for the line, just simply look at the offset. Looks much simpler, right?

@dirtydanee
Copy link
Author

Added an initial version of the modified modifiers rule.

I am seeing the need to introduce some enum for the declaration kinds (SwiftDeclarationAttributeKind), plus somehow they should be grep-ed from SourceKitService to have the latest for 4.1.

Somehow working with the groups seems very natural, but i can stick with beforeACL and afterACL, lets see how that goes.

Let me know what do you think about the approach.

@masters3d
Copy link
Owner

The implementation detail can use groups. I think this is great. The part that I am not sure is exposing that as a configuration? Perhaps you are able to expose them with some sort of different naming? The exposed “api” is what I am thinking about.

case `override` = "source.decl.attribute.override"
case `weak` = "source.decl.attribute.weak"
case `testable` = "source.decl.attribute.testable"
case privateSetter = "source.decl.attribute.setter_access.private"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there more options like public and internal open etc

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There must be, but yesterday i was not able to grep the values from SwiftDeclarationAttribute . I only have the 9.3 beta, and i can not install it as my default, i still need 9.2 for work. And as far i have seen SourceKitService framework is only available with your default Xcode. I will try to figure something out...

import Foundation
import SourceKittenFramework

// TODO: Get somehow `SwiftDeclarationAttribute` for Swift 4.1
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class and static? class can be an attribute or a keyword

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added TODO

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This issue is painful. I will push an update in the following days, but static and class is not recognised as attributes by SourceKitten

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can You file a radar?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a workaround in my latest commit, but its an ugly hack. Please let me know what do you think, or we should wait until a further release.

I will open a radar meanwhile.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, now that i am just about to open the radar, i can already see, add them as source.lang.swift.decl.var.static or source.lang.swift.decl.function.method.class. They might just answer it is not a declaration attribute, but a declaration. Maybe i could work out something based on that, rather than this regex search i am doing now. However, somehow i should still fake them i am affraid..

For example:

class Foo { 
   static public let bar = 3
}

will give

{
"key.diagnostic_stage": "source.diagnostic.stage.swift.parse", 
"key.substructure": [
  { 
    "key.bodylength": 31, 
    "key.nameoffset": 6, 
     "key.accessibility": "source.lang.swift.accessibility.internal", 
     "key.length": 43, 
     "key.substructure": [
          {
               "key.nameoffset": 32, 
               "key.accessibility": "source.lang.swift.accessibility.public", 
               "key.length": 25, 
               "key.name": "bar", 
               "key.kind": "source.lang.swift.decl.var.static", 
               "key.namelength": 3, 
               "key.offset": 14, 
               "key.attributes": [
                    { 
                         "key.attribute": "source.decl.attribute.public", 
                         "key.offset": 21, 
                         "key.length": 6}]
                    }
     ], 
     "key.runtime_name": "_TtC8__main__3Foo", 
     "key.name": "Foo", 
     "key.kind": "source.lang.swift.decl.class", 
     "key.namelength": 3, 
     "key.offset": 0, 
     "key.bodyoffset": 11
     }
],
"key.offset": 0, 
"key.length": 43
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meanwhile, i decided to raise the issue, lets see what they say.
https://bugs.swift.org/browse/SR-7252

case `required` = "source.decl.attribute.required"
case `optional` = "source.decl.attribute.optional"
case noreturn = "source.decl.attribute.noreturn"
case `NSManaged` = "source.decl.attribute.NSManaged"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These start with @ should we make the distinction?

@dirtydanee dirtydanee force-pushed the dm/attribute_and_modifier_order branch 2 times, most recently from 83f292d to 1b09fd7 Compare March 7, 2018 18:28
case `iboutlet` = "source.decl.attribute.iboutlet"
case `ibdesignable` = "source.decl.attribute.ibdesignable"
case `ibinspectable` = "source.decl.attribute.ibinspectable"
case `objc` = "source.decl.attribute.objc" // TODO: This always has to be first. How to handle this case?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can relay on the compiler to make sure that’s happens. We don’t have to worry about it. If somebody gives the wrong configuration we are not able to check it.

]
)

private let observedDeclarationKinds: [SwiftDeclarationKind] = [.class,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to specify this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking this might speeds up things, since we skip a few cases. It might makes no diff at all.

@masters3d
Copy link
Owner

Is there a way to know from source kit if an attribute is a modifier? As in it starts with @? Then all you have worry about is modifiers, ACL, other.

@dirtydanee
Copy link
Author

Maybe we could filter out all these @objc, @objcmembers, @IBAction` etc... ?
There is no diff from the point of view of source kit, but i could filter these out from the dictionary. Right now i add these as hard coded first elements. Would you rather prefer the filtering?

@dirtydanee
Copy link
Author

@masters3d I have pushed a much simpler solution, no fake cases, using SwiftDeclarationKind and SwiftDeclarationAttributeKind.

I think i am getting close to the final solution, i will start expending the possible test cases. If you would have time for a review, maybe suggest a few test cases, it would be great!

.nonmutating]
case .override:
return [.override]
case .memoryReference:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

owned

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed.

.public,
.open]
case .setterACL:
return [.privateSetter,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is open setter a thing?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fileprivate?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

open set is a thing 🤯
screen shot 2018-03-28 at 21 36 15

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added these cases too.

Copy link
Owner

@masters3d masters3d left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would be hard to keep in sync with new "keywords" being added. I believe there is already some code to deal with ACL and attributes that start with @ .

@dirtydanee
Copy link
Author

dirtydanee commented Mar 28, 2018

There is SwiftDeclarationKind+SwiftLint and AttributesRule are the ones i think you refer to. I can refactor the groups to be similar to SwiftDeclarationKind+SwiftLint. Do you have an other idea how could i approach the grouping?

Also, SwiftDeclarationAttributeKind should ideally be going to SourceKitten. I have opened an issue, let see what they think.

The attributes of the @ modifiers are just hardcoded strings in AttributeRule. I am not sure if they could be reused.

@masters3d
Copy link
Owner

I dont have more ideas on how to do the spitting of the groups other than what I already said.

The compiler already enforces attributes that start with @ to go at the beginning of the declaration. I think swift lint even has a rule to make sure these attributes go on a separate line.

@dirtydanee
Copy link
Author

Declaration attribute has been merged to SourceKitten, and increased the Swift version to 4.1 🎉. Things starting to get together for this PR.

I will be keeping the groups approach and extending it with further tests, and try to see how to utilise on the @ attributes enforced by the compiler.

@masters3d
Copy link
Owner

I retargeted the other branch to go back to master. Let me know if you need anything from me.

@dirtydanee dirtydanee force-pushed the dm/attribute_and_modifier_order branch from 89ad548 to 0e1c782 Compare April 10, 2018 19:53
@dirtydanee dirtydanee force-pushed the dm/attribute_and_modifier_order branch from 0e1c782 to 664ce7b Compare April 10, 2018 19:58
@dirtydanee dirtydanee changed the base branch from attibute_and_modifier_order to master April 10, 2018 20:03
@dirtydanee dirtydanee changed the base branch from master to upstream_master April 10, 2018 20:08
# Conflicts:
#	Cartfile.private
#	Carthage/Checkouts/SourceKitten
#	Rules.md
#	Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift
#	SwiftLint.xcodeproj/project.pbxproj
@dirtydanee
Copy link
Author

so it was merged. Thanks for the cooperation!

@dirtydanee dirtydanee closed this May 8, 2018
@masters3d
Copy link
Owner

Thank you for pushing this forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants