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

Add generic_type_name rule #1068

Merged
merged 8 commits into from
Jan 5, 2017
Merged

Add generic_type_name rule #1068

merged 8 commits into from
Jan 5, 2017

Conversation

marcelofabri
Copy link
Collaborator

Fixes #51

var ends = [Int]()
var currentOffset = 0
let components = characters.split { character in
currentOffset += 1
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm pretty sure this wouldn't work with unicode characters 😅

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've tried an alternative implementation but it's much slower 😱

fileprivate func split(separator: String) -> [(String, NSRange)] {
    let nsText = self.bridge()
    var index = 0
    let length = nsText.length
    var entries = [(String, NSRange)]()

    while true {
        let searchRange = NSRange(location: index, length: length - index)
        let separatorRange = nsText.range(of: separator, options: [], range: searchRange)
        if separatorRange.location == NSNotFound {
            break
        }

        let previousIndex = index
        index = NSMaxRange(separatorRange)

        let range = NSRange(location: previousIndex, length: separatorRange.location - previousIndex)
        let substring = nsText.substring(with: range)
        entries.append((substring, range))
    }

    let range = NSRange(location: index, length: length - index)
    let substring = nsText.substring(with: range)
    entries.append((substring, range))

    return entries
}


fileprivate func trimmingWhitespaces() -> (String, NSRange) {
let range = NSRange(location: 0, length: bridge().length)
guard let match = regex("^\\s*(\\S*)\\s*$").firstMatch(in: self, options: [], range: range),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I hope there's a better way to do this 🤔

@marcelofabri marcelofabri mentioned this pull request Dec 25, 2016
7 tasks
@codecov-io
Copy link

codecov-io commented Dec 25, 2016

Current coverage is 82.69% (diff: 93.33%)

Merging #1068 into master will increase coverage by 0.19%

@@             master      #1068   diff @@
==========================================
  Files           153        154     +1   
  Lines          7443       7578   +135   
  Methods           0          0          
  Messages          0          0          
  Branches          0          0          
==========================================
+ Hits           6141       6267   +126   
- Misses         1302       1311     +9   
  Partials          0          0          

Powered by Codecov. Last update 9539cac...9970246

)

private let genericTypePattern = "<(\\s*\\w.*?)>"
private var genericTypeRegex: NSRegularExpression {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why isn't this private var genericTypeRegex = regex(genericTypePattern)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Because I wanted to make sure it's read-only and regex caches the objects anyway

Copy link
Collaborator

Choose a reason for hiding this comment

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

sorry, I meant private let

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oh, because you can't initialize a variable with the contents with another one ¯_(ツ)_/¯

private func validateGenericTypeAliases(_ file: File) -> [StyleViolation] {
let pattern = "typealias\\s+.+?" + genericTypePattern + "\\s*="
return file.matchPattern(pattern).flatMap { (range, tokens) -> [(String, Int)] in
guard tokens.count > 1, tokens.first == .keyword,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the tokens.count > 1 check is superfluous and covered by the subsequent two checks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

good catch 👍

}

private func minParameterOffset(parameters: [[String: SourceKitRepresentable]], file: File) -> Int {
let offsets = parameters.flatMap {
Copy link
Collaborator

Choose a reason for hiding this comment

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

these two flatMaps can be combined.

file.contents.bridge().byteRangeToNSRange(start: $0, length: 0)?.location
}

return offsets.min() ?? Int.max
Copy link
Collaborator

Choose a reason for hiding this comment

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

?? .max

}

extension String {
fileprivate func split(separator: Character) -> [(String, NSRange)] {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why did you make this function? Couldn't you use String.components(separatedBy:) instead? You should be able to get the ranges out of that too, since you know the length of the argument.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'd probably still have a helper function to calculate the ranges and return a [(String, NSRange)]. I could reimplement this function to use String.components(separatedBy:) instead. Do you think it would be better?

Copy link
Collaborator

Choose a reason for hiding this comment

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

you'd probably avoid the multi-byte character issue by doing so.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🙌 I'll reimplement that

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done in 9c6d900


var previousEndOffset = 0
let separatorLength = separator.bridge().length
return components.map { component -> (String, NSRange) in
Copy link
Collaborator

Choose a reason for hiding this comment

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

map with mutation in defer? 😱

let separatorLength = separator.bridge().length
var previousEndOffset = 0
var result = [(String, NSRange)]()

for component in components(separatedBy: separator) {
    let length = component.bridge().length
    let range = NSRange(location: previousEndOffset, length: length)
    result.append((component, range))
    previousEndOffset += length + separatorLength
}
return result

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

😅

@jpsim
Copy link
Collaborator

jpsim commented Jan 5, 2017

This rule is crashing when linting test/Parse/utf8_bom.swift from the Swift source.

Backtrace
fatal error: unexpectedly found nil while unwrapping an Optional value
Linting 'else.swift' (1963/8955)
2017-01-04 17:09:26.563636 swiftlint[63628:6305806] fatal error: unexpectedly found nil while unwrapping an Optional value
Current stack trace:
Linting 'elseif.swift' (1964/8955)
0    libswiftCore.dylib                 0x00000001009cece0 swift_reportError + 132
1    libswiftCore.dylib                 0x00000001009ec090 _swift_stdlib_reportFatalError + 61
2    libswiftCore.dylib                 0x00000001007e20c0 specialized specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) -> A) -> A + 355
3    libswiftCore.dylib                 0x000000010095e230 partial apply for (_fatalErrorMessage(StaticString, StaticString, StaticString, UInt, flags : UInt32) -> Never).(closure #2) + 109
4    libswiftCore.dylib                 0x00000001007e20c0 specialized specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) -> A) -> A + 355
Linting 'for.swift' (1965/8955)
5    libswiftCore.dylib                 0x00000001009163f0 specialized _fatalErrorMessage(StaticString, StaticString, StaticString, UInt, flags : UInt32) -> Never + 96
Linting 'func_decls.swift' (1966/8955)
6    SourceKittenFramework              0x000000010060b030 NSString.CacheContainer.location(fromByteOffset : Int) -> Int + 2890
7    SourceKittenFramework              0x000000010060fb40 NSString.byteRangeToNSRange(start : Int, length : Int) -> _NSRange? + 249
8    SwiftLintFramework                 0x00000001001f0f70 GenericTypeNameRule.genericTypesForType(File, kind : SwiftDeclarationKind, dictionary : [String : SourceKitRepresentable]) -> [(String, Int)] + 2175
9    SwiftLintFramework                 0x00000001001f0930 GenericTypeNameRule.validateFile(File, kind : SwiftDeclarationKind, dictionary : [String : SourceKitRepresentable]) -> [StyleViolation] + 503
10   SwiftLintFramework                 0x00000001001f83f0 protocol witness for ASTRule.validateFile(File, kind : A.KindType, dictionary : [String : SourceKitRepresentable]) -> [StyleViolation] in conformance GenericTypeNameRule + 408
11   SwiftLintFramework                 0x00000001002f92e0 ASTRule<A where ...>.(validateFile(File, dictionary : [String : SourceKitRepresentable]) -> [StyleViolation]).(closure #1) + 1432
12   SwiftLintFramework                 0x00000001002f9ae0 thunk + 53
13   SwiftLintFramework                 0x00000001002f9bb0 partial apply for thunk + 124
14   libswiftCore.dylib                 0x00000001008958a0 Sequence.flatMap<A where ...> ((A.Iterator.Element) throws -> A1) throws -> [A1.Iterator.Element] + 639
15   SwiftLintFramework                 0x00000001002f90d0 ASTRule<A where ...>.validateFile(File, dictionary : [String : SourceKitRepresentable]) -> [StyleViolation] + 375
16   SwiftLintFramework                 0x00000001001eff10 GenericTypeNameRule.validateFile(File) -> [StyleViolation] + 383
17   SwiftLintFramework                 0x00000001001f8ac0 protocol witness for Rule.validateFile(File) -> [StyleViolation] in conformance GenericTypeNameRule + 364
18   SwiftLintFramework                 0x00000001001abed0 Rule.performLint(file : File, regions : [Region], benchmark : Bool) -> LintResult? + 1404
19   SwiftLintFramework                 0x00000001001b3360 Linter.(getStyleViolations(Bool) -> ([StyleViolation], [(id : String, time : Double)])).(closure #1) + 253
20   SwiftLintFramework                 0x00000001001b5a70 partial apply for Linter.(getStyleViolations(Bool) -> ([StyleViolation], [(id : String, time : Double)])).(closure #1) + 147
21   SwiftLintFramework                 0x00000001001b3560 thunk + 39
22   SwiftLintFramework                 0x00000001001b5b50 partial apply for thunk + 81
23   SwiftLintFramework                 0x0000000100319100 Array.(parallelMap<A> (transform : (A) -> A1) -> [A1]).(closure #1) + 375
24   SwiftLintFramework                 0x0000000100319430 partial apply for Array.(parallelMap<A> (transform : (A) -> A1) -> [A1]).(closure #1) + 182
25   libdispatch.dylib                  0x0000000101031967 _dispatch_client_callout2 + 8
26   libdispatch.dylib                  0x000000010103609b _dispatch_apply_serial + 108
27   libdispatch.dylib                  0x0000000101023f54 _dispatch_client_callout + 8
28   libdispatch.dylib                  0x000000010103b1da _dispatch_sync_function_invoke + 288
29   libdispatch.dylib                  0x00000001010310dc dispatch_apply_f + 459
30   libswiftCore.dylib                 0x00000001009ed010 _swift_dispatch_apply_current + 72
31   libswiftDispatch.dylib             0x0000000100cf8380 static DispatchQueue.concurrentPerform(iterations : Int, execute : (Int) -> ()) -> () + 116
32   SwiftLintFramework                 0x00000001003183f0 Array.parallelMap<A> (transform : (A) -> A1) -> [A1] + 1152
33   SwiftLintFramework                 0x0000000100318e50 Array.parallelFlatMap<A> (transform : (A) -> A1?) -> [A1] + 104
34   SwiftLintFramework                 0x00000001001b2360 Linter.getStyleViolations(Bool) -> ([StyleViolation], [(id : String, time : Double)]) + 511
35   SwiftLintFramework                 0x00000001001b2310 Linter.styleViolations.getter + 45
36   swiftlint                          0x000000010000f0b0 LintCommand.(run(LintOptions) -> Result<(), CommandantError<()>>).(closure #1) + 999
37   swiftlint                          0x000000010000f710 partial apply for LintCommand.(run(LintOptions) -> Result<(), CommandantError<()>>).(closure #1) + 603
38   swiftlint                          0x0000000100006f20 Configuration.(visitLintableFiles(String, action : String, useSTDIN : Bool, quiet : Bool, useScriptInputFiles : Bool, parallel : Bool, visitorBlock : (Linter) -> ()) -> Result<[File], CommandantError<()>>).(closure #2).(closure #1).(closure #2) + 976
39   swiftlint                          0x0000000100008890 partial apply for Configuration.(visitLintableFiles(String, action : String, useSTDIN : Bool, quiet : Bool, useScriptInputFiles : Bool, parallel : Bool, visitorBlock : (Linter) -> ()) -> Result<[File], CommandantError<()>>).(closure #2).(closure #1).(closure #2) + 672
40   swiftlint                          0x0000000100007380 thunk + 32
41   swiftlint                          0x0000000100008b90 partial apply for thunk + 81
42   libswiftObjectiveC.dylib           0x0000000100f2c4a0 autoreleasepool<A> (invoking : () throws -> A) throws -> A + 54
43   swiftlint                          0x00000001000063c0 Configuration.(visitLintableFiles(String, action : String, useSTDIN : Bool, quiet : Bool, useScriptInputFiles : Bool, parallel : Bool, visitorBlock : (Linter) -> ()) -> Result<[File], CommandantError<()>>).(closure #2).(closure #1) + 1742
44   swiftlint                          0x0000000100008290 partial apply for Configuration.(visitLintableFiles(String, action : String, useSTDIN : Bool, quiet : Bool, useScriptInputFiles : Bool, parallel : Bool, visitorBlock : (Linter) -> ()) -> Result<[File], CommandantError<()>>).(closure #2).(closure #1) + 893
45   swiftlint                          0x00000001000073f0 Configuration.(visitLintableFiles(String, action : String, useSTDIN : Bool, quiet : Bool, useScriptInputFiles : Bool, parallel : Bool, visitorBlock : (Linter) -> ()) -> Result<[File], CommandantError<()>>).(closure #2).(closure #2) + 95
46   libdispatch.dylib                  0x0000000101031967 _dispatch_client_callout2 + 8
47   libdispatch.dylib                  0x0000000101031721 _dispatch_apply_invoke + 147
48   libdispatch.dylib                  0x0000000101023f54 _dispatch_client_callout + 8
49   libdispatch.dylib                  0x0000000101025f38 _dispatch_root_queue_drain + 1364
50   libdispatch.dylib                  0x0000000101025e76 _dispatch_worker_thread3 + 114
51   libsystem_pthread.dylib            0x000000010109b387 _pthread_wqthread + 1299
52   libsystem_pthread.dylib            0x000000010109b368 start_wqthread + 13

@marcelofabri
Copy link
Collaborator Author

From the backtrace it seems the same issue as #1006, doesn't it?

@jpsim
Copy link
Collaborator

jpsim commented Jan 5, 2017

Yes, though none of the other rules hit it with that particular file.

@marcelofabri
Copy link
Collaborator Author

marcelofabri commented Jan 5, 2017

Is the file that you've linked the right one? Because the log says Linting 'elseif.swift' (1964/8955)

@jpsim
Copy link
Collaborator

jpsim commented Jan 5, 2017

Yes, it's the right one. The log includes other files because we now lint multiple files in parallel.

@jpsim
Copy link
Collaborator

jpsim commented Jan 5, 2017

To be clear, I don't consider that issue to be a blocker for this PR, just sharing as an interesting discovery.

severity: severity,
location: Location(file: file, byteOffset: offset),
reason: "Generic type name should be between \(configuration.minLengthThreshold) and " +
"\(configuration.maxLengthThreshold) characters long: '\(name)'")
Copy link
Collaborator

Choose a reason for hiding this comment

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

indentation

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this was actually how Xcode formatted it 😱
Should I just align the quotes?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Tes

}

fileprivate func trimmingWhitespaces() -> (String, NSRange) {
let range = NSRange(location: 0, length: bridge().length)
Copy link
Collaborator

Choose a reason for hiding this comment

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

since you call bridge() twice in this function, could you instead just save it to a local let bridged = bridge() variable?

@marcelofabri marcelofabri merged commit f4e7768 into realm:master Jan 5, 2017
@marcelofabri marcelofabri deleted the generic-rule branch January 5, 2017 03:16
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