Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@ jobs:
key: windows-${{ matrix.windows-version }}-spm-${{ env.SWIFT_VERSION }}-${{ env.SWIFT_BUILD }}-${{ hashFiles('Package.resolved', 'Package.swift') }}
restore-keys: windows-${{ matrix.windows-version }}-spm-${{ env.SWIFT_VERSION }}-${{ env.SWIFT_BUILD }}-
path: .build
- name: Build all targets
run: swift build --build-tests
- name: Run selected tests
run: swift test --skip IntegrationTests --skip FileSystemAccessTests --skip FrameworkTests --skip BuiltInRulesTests
# To be extended with test execution and linting ...
- name: Run tests
run: swift test --parallel
- name: Lint codebase
run: swift run swiftlint lint --strict
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@

### Experimental

* None.
* SwiftLint can now be built and run on Windows. It is expected to work in the same way as
on other platforms. The only restrictions are missing support for `?[]` glob patterns in
include/exclude patterns and the requirement for `\n` as line ending in all linted files.
[compnerd](https://github.com/compnerd)
[roman-bcny](https://github.com/roman-bcny)
[SimplyDanny](https://github.com/SimplyDanny)
[#6351](https://github.com/realm/SwiftLint/issues/6351)
[#6352](https://github.com/realm/SwiftLint/issues/6352)

### Enhancements

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct FileNameNoSpaceRule: OptInRule, SourceKitFreeRule {

func validate(file: SwiftLintFile) -> [StyleViolation] {
guard let filePath = file.path,
case let fileName = filePath.bridge().lastPathComponent,
case let fileName = filePath.lastPathComponent,
!configuration.excluded.contains(fileName),
fileName.rangeOfCharacter(from: .whitespaces) != nil else {
return []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct FileNameRule: OptInRule, SourceKitFreeRule {
let prefixRegex = regex("\\A(?:\(configuration.prefixPattern))")
let suffixRegex = regex("(?:\(configuration.suffixPattern))\\z")

let fileName = filePath.bridge().lastPathComponent
let fileName = filePath.lastPathComponent
var typeInFileName = fileName.bridge().deletingPathExtension

// Process prefix
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,6 @@ private extension Command {
location = line.distance(from: line.startIndex, to: ruleIdentifierIndex) + 1
}
}
return Location(file: file.file.path, line: line, character: location)
return Location(file: file.path, line: line, character: location)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ private extension SwiftLintFile {
func index(compilerArguments: [String]) -> SourceKittenDictionary? {
guard
let path,
let response = try? Request.index(file: path, arguments: compilerArguments).sendIfNotDisabled()
let response = try? Request.index(file: path.filepath, arguments: compilerArguments).sendIfNotDisabled()
else {
Issue.indexingError(path: path, ruleID: CaptureVariableRule.identifier).print()
return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ struct TypesafeArrayInitRule: AnalyzerRule {
return false
}
let cursorInfo = Request.cursorInfoWithoutSymbolGraph(
file: filePath, offset: offset, arguments: compilerArguments
file: filePath.filepath, offset: offset, arguments: compilerArguments
)
guard let request = try? cursorInfo.sendIfNotDisabled() else {
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ private extension SwiftLintFile {
func index(compilerArguments: [String]) -> SourceKittenDictionary? {
path
.flatMap { path in
try? Request.index(file: path, arguments: compilerArguments).send()
try? Request.index(file: path.filepath, arguments: compilerArguments).send()
}
.map(SourceKittenDictionary.init)
}
Expand Down Expand Up @@ -203,7 +203,7 @@ private extension SwiftLintFile {

func cursorInfo(at byteOffset: ByteCount, compilerArguments: [String]) -> SourceKittenDictionary? {
let request = Request.cursorInfoWithoutSymbolGraph(
file: path!, offset: byteOffset, arguments: compilerArguments
file: path!.filepath, offset: byteOffset, arguments: compilerArguments
)
return (try? request.sendIfNotDisabled()).map(SourceKittenDictionary.init)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ private extension SwiftLintFile {
continue
}
let cursorInfoRequest = Request.cursorInfoWithoutSymbolGraph(
file: path!, offset: token.offset, arguments: compilerArguments
file: path!.filepath, offset: token.offset, arguments: compilerArguments
)
guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled()).map(SourceKittenDictionary.init) else {
Issue.missingCursorInfo(path: path, ruleID: UnusedImportRule.identifier).print()
Expand Down Expand Up @@ -211,7 +211,7 @@ private extension SwiftLintFile {

// Operators are omitted in the editor.open request and thus have to be looked up by the indexsource request
func operatorImports(arguments: [String], processedTokenOffsets: Set<ByteCount>) -> Set<String> {
guard let index = (try? Request.index(file: path!, arguments: arguments).sendIfNotDisabled())
guard let index = (try? Request.index(file: path!.filepath, arguments: arguments).sendIfNotDisabled())
.map(SourceKittenDictionary.init) else {
Issue.indexingError(path: path, ruleID: UnusedImportRule.identifier).print()
return []
Expand All @@ -231,7 +231,7 @@ private extension SwiftLintFile {
guard !processedTokenOffsets.contains(ByteCount(offset)) else { continue }

let cursorInfoRequest = Request.cursorInfoWithoutSymbolGraph(
file: path!, offset: ByteCount(offset), arguments: arguments
file: path!.filepath, offset: ByteCount(offset), arguments: arguments
)
guard let cursorInfo = (try? cursorInfoRequest.sendIfNotDisabled())
.map(SourceKittenDictionary.init) else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ struct FileHeaderConfiguration: SeverityBasedRuleConfiguration {
escapeFileName: Bool) -> NSRegularExpression? {
// Recompile the regex for this file...
let replacedPattern = file.path.map { path in
let fileName = path.bridge().lastPathComponent
let fileName = path.lastPathComponent

// Replace SWIFTLINT_CURRENT_FILENAME with the filename.
let escapedName = escapeFileName ? NSRegularExpression.escapedPattern(for: fileName) : fileName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,12 @@ struct FileNameConfiguration: SeverityBasedRuleConfiguration {
}

extension FileNameConfiguration {
func shouldExclude(filePath: String) -> Bool {
let fileName = filePath.bridge().lastPathComponent
if excluded.contains(fileName) {
func shouldExclude(filePath: URL) -> Bool {
if excluded.contains(filePath.lastPathComponent) {
return true
}
return excludedPaths.contains {
$0.regex.firstMatch(in: filePath, range: filePath.fullNSRange) != nil
$0.regex.firstMatch(in: filePath.path, range: filePath.path.fullNSRange) != nil
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private extension SwiftLintFile {
try byteOffsets.compactMap { offset in
if isExplicitAccess(at: offset) { return nil }
let cursorInfoRequest = Request.cursorInfoWithoutSymbolGraph(
file: self.path!, offset: offset, arguments: compilerArguments
file: path!.filepath, offset: offset, arguments: compilerArguments
)
var cursorInfo = try cursorInfoRequest.sendIfNotDisabled()

Expand Down Expand Up @@ -135,8 +135,7 @@ private extension StringView {
}

private func binaryOffsets(file: SwiftLintFile, compilerArguments: [String]) throws -> [ByteCount] {
let absoluteFile = file.path!.bridge().absolutePathRepresentation()
let index = try Request.index(file: absoluteFile, arguments: compilerArguments).sendIfNotDisabled()
let index = try Request.index(file: file.path!.filepath, arguments: compilerArguments).sendIfNotDisabled()
let binaryOffsets = file.stringView.recursiveByteOffsets(index)
return binaryOffsets.sorted()
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ struct FileTypesOrderRule: OptInRule {
return mainTypeSubstructure(in: dict)
}

let fileName = URL(fileURLWithPath: filePath, isDirectory: false)
.lastPathComponent.replacingOccurrences(of: ".swift", with: "")
let fileName = filePath.lastPathComponent.replacingOccurrences(of: ".swift", with: "")
guard let mainTypeSubstructure = dict.substructure.first(where: { $0.name == fileName }) else {
return mainTypeSubstructure(in: file.structureDictionary)
}
Expand Down
21 changes: 0 additions & 21 deletions Source/SwiftLintCore/Extensions/String+SwiftLint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,6 @@ public extension String {
NSRange(location: 0, length: utf16.count)
}

/// Returns a new string, converting the path to a canonical absolute path.
///
/// > Important: This method might use an incorrect working directory internally. This can cause test failures
/// in Bazel builds but does not seem to cause trouble in production.
///
/// - returns: A new `String`.
func absolutePathStandardized() -> String {
URL(fileURLWithPath: bridge().standardizingPath.absolutePathRepresentation()).filepath
}

var isFile: Bool {
if isEmpty {
return false
}
var isDirectoryObjC: ObjCBool = false
if FileManager.default.fileExists(atPath: self, isDirectory: &isDirectoryObjC) {
return !isDirectoryObjC.boolValue
}
return false
}

/// Count the number of occurrences of the given character in `self`
/// - Parameter character: Character to count
/// - Returns: Number of times `character` occurs in `self`
Expand Down
2 changes: 1 addition & 1 deletion Source/SwiftLintCore/Extensions/SwiftLintFile+Cache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ private let foldedSyntaxTreeCache = Cache { file -> SourceFileSyntax? in
.as(SourceFileSyntax.self)
}
private let locationConverterCache = Cache { file -> SourceLocationConverter in
SourceLocationConverter(fileName: file.path ?? "<nopath>", tree: file.syntaxTree)
SourceLocationConverter(fileName: file.path?.filepath ?? "<nopath>", tree: file.syntaxTree)
}
private let commandsCache = Cache { file -> [Command] in
guard file.contents.contains("swiftlint:") else {
Expand Down
4 changes: 2 additions & 2 deletions Source/SwiftLintCore/Extensions/SwiftLintFile+Regex.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ extension SwiftLintFile {
guard let stringData = string.data(using: .utf8) else {
queuedFatalError("can't encode '\(string)' with UTF8")
}
guard let path, let fileHandle = FileHandle(forWritingAtPath: path) else {
guard let path, let fileHandle = FileHandle(forWritingAtPath: path.filepath) else {
queuedFatalError("can't write to path '\(String(describing: path))'")
}
_ = fileHandle.seekToEndOfFile()
Expand All @@ -166,7 +166,7 @@ extension SwiftLintFile {
queuedFatalError("can't encode '\(string)' with UTF8")
}
do {
try stringData.write(to: URL(fileURLWithPath: path, isDirectory: false), options: .atomic)
try stringData.write(to: path, options: .atomic)
} catch {
queuedFatalError("can't write file to \(path)")
}
Expand Down
84 changes: 73 additions & 11 deletions Source/SwiftLintCore/Extensions/URL+SwiftLint.swift
Original file line number Diff line number Diff line change
@@ -1,23 +1,85 @@
import Foundation

public extension URL {
static var cwd: URL {
FileManager.default.currentDirectoryPath.url(directoryHint: .isDirectory)
}

var filepath: String {
withUnsafeFileSystemRepresentation { String(cString: $0!) }
}

var filepathGuarded: String? {
withUnsafeFileSystemRepresentation { ptr in
guard let ptr else {
Issue.genericError(
"File with URL '\(self)' cannot be represented as a file system path; skipping it"
).print()
return nil
}
return String(cString: ptr)
var isSwiftFile: Bool {
isFile && pathExtension == "swift"
}

var isFile: Bool {
var isDirectoryObjC: ObjCBool = false
if FileManager.default.fileExists(atPath: filepath, isDirectory: &isDirectoryObjC) {
return !isDirectoryObjC.boolValue
}
return false
}

var isSwiftFile: Bool {
filepath.isFile && pathExtension == "swift"
var isDirectory: Bool {
var isDirectoryObjC: ObjCBool = false
if FileManager.default.fileExists(atPath: filepath, isDirectory: &isDirectoryObjC) {
return isDirectoryObjC.boolValue
}
return false
}

/// Path relative to the current working directory.
///
/// > Warning: Use this representation only for displaying file paths to users. It is not
/// suitable for file operations.
var relativeDisplayPath: String {
let path = path.replacing(Self.cwd.path, with: "")
if path.starts(with: "/") {
return String(path.dropFirst())
}
return path
}

var exists: Bool {
isFileURL && FileManager.default.fileExists(atPath: filepath)
}

func relative(to base: URL) -> URL {
guard base.isFileURL, isFileURL else {
return self
}

let baseComponents = base.standardizedFileURL.pathComponents
let selfComponents = standardizedFileURL.pathComponents

var index = 0
while index < baseComponents.count, index < selfComponents.count,
baseComponents[index] == selfComponents[index] {
index += 1
}

var newPath = base
for _ in index..<baseComponents.count {
newPath.deleteLastPathComponent()
}
for component in selfComponents[index...] {
newPath.append(path: component)
}
return newPath
}
}

public extension String {
func url(relativeTo base: URL? = nil, directoryHint: URL.DirectoryHint = .inferFromPath) -> URL {
guard var base else {
return URL(filePath: self, directoryHint: directoryHint).standardizedFileURL
}
if base.isDirectory {
let lastComponent = base.lastPathComponent
base.deleteLastPathComponent()
base.append(path: lastComponent, directoryHint: .isDirectory)
}
return URL(filePath: self, directoryHint: directoryHint, relativeTo: base).standardizedFileURL
}
}
Loading
Loading