Files
SwiftLint/Source/SwiftLintFramework/Extensions/Dictionary+SwiftLint.swift
T
Timofey Solonin 7800220ff7 #2737 - Fix unused_imports false positive when only operators from the module are used (#2840)
As was discussed #2737, I utilized the `indexsource` request to look up the operators and fetch the cursorInfo per operator. In the implementation I refrained from using `usr` to look up the operator because SourceKit [doesn't support](https://github.com/apple/swift/blob/5add16804272b4df917da15c46eb6f28d826d656/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp#L1799) fetching cursorInfo by `usr` when it comes from C (but it can still be fetched by offset).

I also made [an alternative implementation](https://github.com/realm/SwiftLint/compare/master...biboran:fix-unused-imports-for-operators-alternative) which uses the `indexsource` request instead of the `editor.open`. It seems to work with the unit tests but I am not 100% sure it doesn't introduce a regression since there is no oss-check for analyzer rules.

I also updated [the example](https://github.com/biboran/synthax-bug-example) to better reflect the original issue in case you want to test the changes manually
2019-08-23 16:19:57 -07:00

138 lines
4.7 KiB
Swift

import SourceKittenFramework
extension Dictionary where Key: ExpressibleByStringLiteral {
/// Accessibility.
var accessibility: String? {
return self["key.accessibility"] as? String
}
/// Body length.
var bodyLength: Int? {
return (self["key.bodylength"] as? Int64).flatMap({ Int($0) })
}
/// Body offset.
var bodyOffset: Int? {
return (self["key.bodyoffset"] as? Int64).flatMap({ Int($0) })
}
/// Kind.
var kind: String? {
return self["key.kind"] as? String
}
/// Length.
var length: Int? {
return (self["key.length"] as? Int64).flatMap({ Int($0) })
}
/// Name.
var name: String? {
return self["key.name"] as? String
}
/// Name length.
var nameLength: Int? {
return (self["key.namelength"] as? Int64).flatMap({ Int($0) })
}
/// Name offset.
var nameOffset: Int? {
return (self["key.nameoffset"] as? Int64).flatMap({ Int($0) })
}
/// Offset.
var offset: Int? {
return (self["key.offset"] as? Int64).flatMap({ Int($0) })
}
/// Setter accessibility.
var setterAccessibility: String? {
return self["key.setter_accessibility"] as? String
}
/// Type name.
var typeName: String? {
return self["key.typename"] as? String
}
/// Documentation length.
var docLength: Int? {
return (self["key.doclength"] as? Int64).flatMap({ Int($0) })
}
var attribute: String? {
return self["key.attribute"] as? String
}
var enclosedSwiftAttributes: [SwiftDeclarationAttributeKind] {
return swiftAttributes.compactMap { $0.attribute }
.compactMap(SwiftDeclarationAttributeKind.init(rawValue:))
}
var swiftAttributes: [[String: SourceKitRepresentable]] {
let array = self["key.attributes"] as? [SourceKitRepresentable] ?? []
let dictionaries = array.compactMap { ($0 as? [String: SourceKitRepresentable]) }
return dictionaries
}
var substructure: [[String: SourceKitRepresentable]] {
let substructure = self["key.substructure"] as? [SourceKitRepresentable] ?? []
return substructure.compactMap { $0 as? [String: SourceKitRepresentable] }
}
var elements: [[String: SourceKitRepresentable]] {
let elements = self["key.elements"] as? [SourceKitRepresentable] ?? []
return elements.compactMap { $0 as? [String: SourceKitRepresentable] }
}
var entities: [[String: SourceKitRepresentable]] {
let entities = self["key.entities"] as? [SourceKitRepresentable] ?? []
return entities.compactMap { $0 as? [String: SourceKitRepresentable] }
}
var enclosedVarParameters: [[String: SourceKitRepresentable]] {
return substructure.flatMap { subDict -> [[String: SourceKitRepresentable]] in
guard let kindString = subDict.kind else {
return []
}
if SwiftDeclarationKind(rawValue: kindString) == .varParameter {
return [subDict]
} else if SwiftExpressionKind(rawValue: kindString) == .argument ||
SwiftExpressionKind(rawValue: kindString) == .closure {
return subDict.enclosedVarParameters
}
return []
}
}
var enclosedArguments: [[String: SourceKitRepresentable]] {
return substructure.flatMap { subDict -> [[String: SourceKitRepresentable]] in
guard let kindString = subDict.kind,
SwiftExpressionKind(rawValue: kindString) == .argument else {
return []
}
return [subDict]
}
}
var inheritedTypes: [String] {
let array = self["key.inheritedtypes"] as? [SourceKitRepresentable] ?? []
return array.compactMap { ($0 as? [String: String])?.name }
}
internal func extractCallsToSuper(methodName: String) -> [String] {
guard let methodNameWithoutArguments = methodName.split(separator: "(").first else {
return []
}
let superCall = "super.\(methodNameWithoutArguments)"
return substructure.flatMap { elems -> [String] in
guard let type = elems.kind.flatMap(SwiftExpressionKind.init),
let name = elems.name,
type == .call && superCall == name else {
return elems.extractCallsToSuper(methodName: methodName)
}
return [name]
}
}
}
extension Dictionary where Key == String {
/// Returns a dictionary with SwiftLint violation markers (↓) removed from keys.
func removingViolationMarkers() -> [Key: Value] {
return Dictionary(uniqueKeysWithValues: map { ($0.replacingOccurrences(of: "↓", with: ""), $1) })
}
}