-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2169 from marcelofabri/unavailable_function
Add unavailable_function opt-in rule
- Loading branch information
Showing
7 changed files
with
177 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 100 additions & 0 deletions
100
Source/SwiftLintFramework/Rules/UnavailableFunctionRule.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// | ||
// UnavailableFunctionRule.swift | ||
// SwiftLint | ||
// | ||
// Created by Marcelo Fabri on 04/09/18. | ||
// Copyright © 2018 Realm. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import SourceKittenFramework | ||
|
||
public struct UnavailableFunctionRule: ASTRule, ConfigurationProviderRule, OptInRule { | ||
public var configuration = SeverityConfiguration(.warning) | ||
|
||
public init() {} | ||
|
||
public static let description = RuleDescription( | ||
identifier: "unavailable_function", | ||
name: "Unavailable Function", | ||
description: "Unimplemented functions should be marked as unavailable.", | ||
kind: .idiomatic, | ||
minSwiftVersion: .fourDotOne, | ||
nonTriggeringExamples: [ | ||
""" | ||
class ViewController: UIViewController { | ||
@available(*, unavailable) | ||
public required init?(coder aDecoder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
} | ||
""", | ||
""" | ||
func jsonValue(_ jsonString: String) -> NSObject { | ||
let data = jsonString.data(using: .utf8)! | ||
let result = try! JSONSerialization.jsonObject(with: data, options: []) | ||
if let dict = (result as? [String: Any])?.bridge() { | ||
return dict | ||
} else if let array = (result as? [Any])?.bridge() { | ||
return array | ||
} | ||
fatalError() | ||
} | ||
""" | ||
], | ||
triggeringExamples: [ | ||
""" | ||
class ViewController: UIViewController { | ||
public required ↓init?(coder aDecoder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
} | ||
""", | ||
""" | ||
class ViewController: UIViewController { | ||
public required ↓init?(coder aDecoder: NSCoder) { | ||
let reason = "init(coder:) has not been implemented" | ||
fatalError(reason) | ||
} | ||
} | ||
""" | ||
] | ||
) | ||
|
||
public func validate(file: File, kind: SwiftDeclarationKind, | ||
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] { | ||
guard SwiftDeclarationKind.functionKinds.contains(kind) else { | ||
return [] | ||
} | ||
|
||
let containsFatalError = dictionary.substructure.contains { dict -> Bool in | ||
return dict.kind.flatMap(SwiftExpressionKind.init(rawValue:)) == .call && dict.name == "fatalError" | ||
} | ||
|
||
guard let offset = dictionary.offset, containsFatalError, | ||
!isFunctionUnavailable(file: file, dictionary: dictionary), | ||
let bodyOffset = dictionary.bodyOffset, let bodyLength = dictionary.bodyLength, | ||
let range = file.contents.bridge().byteRangeToNSRange(start: bodyOffset, length: bodyLength), | ||
file.match(pattern: "\\breturn\\b", with: [.keyword], range: range).isEmpty else { | ||
return [] | ||
} | ||
|
||
return [ | ||
StyleViolation(ruleDescription: type(of: self).description, | ||
severity: configuration.severity, | ||
location: Location(file: file, byteOffset: offset)) | ||
] | ||
} | ||
|
||
private func isFunctionUnavailable(file: File, dictionary: [String: SourceKitRepresentable]) -> Bool { | ||
return dictionary.swiftAttributes.contains { dict -> Bool in | ||
guard dict.attribute.flatMap(SwiftDeclarationAttributeKind.init(rawValue:)) == .available, | ||
let offset = dict.offset, let length = dict.length, | ||
let contents = file.contents.bridge().substringWithByteRange(start: offset, length: length) else { | ||
return false | ||
} | ||
|
||
return contents.contains("unavailable") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters