mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
157 lines
7.4 KiB
Swift
157 lines
7.4 KiB
Swift
//
|
|
// CompilerProtocolInitRule.swift
|
|
// SwiftLint
|
|
//
|
|
// Created by Marcelo Fabri on 12/31/16.
|
|
// Copyright © 2016 Realm. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import SourceKittenFramework
|
|
|
|
public struct CompilerProtocolInitRule: ASTRule, ConfigurationProviderRule {
|
|
public var configuration = SeverityConfiguration(.warning)
|
|
|
|
public init() {}
|
|
|
|
public static let description = RuleDescription(
|
|
identifier: "compiler_protocol_init",
|
|
name: "Compiler Protocol Init",
|
|
description: "The initializers declared in compiler protocols such as `ExpressibleByArrayLiteral` " +
|
|
"shouldn't be called directly.",
|
|
nonTriggeringExamples: [
|
|
"let set: Set<Int> = [1, 2]\n",
|
|
"let set = Set(array)\n"
|
|
],
|
|
triggeringExamples: [
|
|
"let set = ↓Set(arrayLiteral: 1, 2)\n",
|
|
"let set = ↓Set.init(arrayLiteral: 1, 2)\n"
|
|
]
|
|
)
|
|
|
|
public func validateFile(_ file: File, kind: SwiftExpressionKind,
|
|
dictionary: [String : SourceKitRepresentable]) -> [StyleViolation] {
|
|
return violationRangesInFile(file, kind: kind, dictionary: dictionary).map {
|
|
StyleViolation(ruleDescription: type(of: self).description,
|
|
severity: configuration.severity,
|
|
location: Location(file: file, characterOffset: $0.location))
|
|
}
|
|
}
|
|
|
|
private func violationRangesInFile(_ file: File,
|
|
kind: SwiftExpressionKind,
|
|
dictionary: [String: SourceKitRepresentable]) -> [NSRange] {
|
|
guard kind == .call,
|
|
let name = dictionary["key.name"] as? String else {
|
|
return []
|
|
}
|
|
|
|
for compilerProtocol in ExpressibleByCompiler.allProtocols {
|
|
guard compilerProtocol.initCallNames.contains(name),
|
|
case let arguments = dictionary.enclosedArguments.flatMap({ $0["key.name"] as? String }),
|
|
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
|
|
}
|
|
|
|
return [range]
|
|
}
|
|
|
|
return []
|
|
}
|
|
}
|
|
|
|
private struct ExpressibleByCompiler {
|
|
private let protocolName: String
|
|
let initCallNames: Set<String>
|
|
private let arguments: [[String]]
|
|
|
|
init(protocolName: String, types: Set<String>, arguments: [[String]]) {
|
|
self.protocolName = protocolName
|
|
self.arguments = arguments
|
|
|
|
initCallNames = Set(types.flatMap { [$0, "\($0).init"] })
|
|
}
|
|
|
|
static let allProtocols = [byArrayLiteral, byNilLiteral, byBooleanLiteral,
|
|
byFloatLiteral, byIntegerLiteral, byUnicodeScalarLiteral,
|
|
byExtendedGraphemeClusterLiteral, byStringLiteral,
|
|
byStringInterpolation, byDictionaryLiteral]
|
|
|
|
func matchArguments(_ arguments: [String]) -> Bool {
|
|
for item in self.arguments {
|
|
if item == arguments {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
private static let byArrayLiteral: ExpressibleByCompiler = {
|
|
let types: Set = [
|
|
"Array",
|
|
"ArraySlice",
|
|
"ContiguousArray",
|
|
"IndexPath",
|
|
"NSArray",
|
|
"NSCountedSet",
|
|
"NSMutableArray",
|
|
"NSMutableOrderedSet",
|
|
"NSMutableSet",
|
|
"NSOrderedSet",
|
|
"NSSet",
|
|
"SBElementArray",
|
|
"Set"
|
|
]
|
|
return ExpressibleByCompiler(protocolName: "ExpressibleByArrayLiteral",
|
|
types: types, arguments: [["arrayLiteral"]])
|
|
}()
|
|
|
|
private static let byNilLiteral = ExpressibleByCompiler(protocolName: "ExpressibleByNilLiteral",
|
|
types: ["ImplicitlyUnwrappedOptional", "Optional"],
|
|
arguments: [["nilLiteral"]])
|
|
|
|
private static let byBooleanLiteral = ExpressibleByCompiler(protocolName: "ExpressibleByBooleanLiteral",
|
|
types: ["Bool", "NSDecimalNumber",
|
|
"NSNumber", "ObjCBool"],
|
|
arguments: [["booleanLiteral"]])
|
|
|
|
private static let byFloatLiteral = ExpressibleByCompiler(protocolName: "ExpressibleByFloatLiteral",
|
|
types: ["Decimal", "NSDecimalNumber", "NSNumber"],
|
|
arguments: [["floatLiteral"]])
|
|
|
|
private static let byIntegerLiteral = ExpressibleByCompiler(protocolName: "ExpressibleByIntegerLiteral",
|
|
types: ["Decimal", "Double", "Float", "Float80",
|
|
"NSDecimalNumber", "NSNumber"],
|
|
arguments: [["integerLiteral"]])
|
|
|
|
private static let byUnicodeScalarLiteral = ExpressibleByCompiler(protocolName: "ExpressibleByUnicodeScalarLiteral",
|
|
types: ["StaticString", "String",
|
|
"UnicodeScalar"],
|
|
arguments: [["unicodeScalarLiteral"]])
|
|
|
|
private static let byExtendedGraphemeClusterLiteral =
|
|
ExpressibleByCompiler(protocolName: "ExpressibleByExtendedGraphemeClusterLiteral",
|
|
types: ["Character", "StaticString", "String"],
|
|
arguments: [["extendedGraphemeClusterLiteral"]])
|
|
|
|
private static let byStringLiteral = ExpressibleByCompiler(protocolName: "ExpressibleByStringLiteral",
|
|
types: ["CSLocalizedString", "NSMutableString",
|
|
"NSString", "Selector",
|
|
"StaticString", "String"],
|
|
arguments: [["stringLiteral"]])
|
|
|
|
private static let byStringInterpolation = ExpressibleByCompiler(protocolName: "ExpressibleByStringInterpolation",
|
|
types: ["String"],
|
|
arguments: [["stringInterpolation"],
|
|
["stringInterpolationSegment"]])
|
|
|
|
private static let byDictionaryLiteral = ExpressibleByCompiler(protocolName: "ExpressibleByDictionaryLiteral",
|
|
types: ["Dictionary", "DictionaryLiteral",
|
|
"NSDictionary", "NSMutableDictionary"],
|
|
arguments: [["dictionaryLiteral"]])
|
|
}
|