diff --git a/Sources/Build/BuildOperation.swift b/Sources/Build/BuildOperation.swift index 1335120784d..4bbcf637d9f 100644 --- a/Sources/Build/BuildOperation.swift +++ b/Sources/Build/BuildOperation.swift @@ -24,7 +24,6 @@ import Foundation import class TSCBasic.DiagnosticsEngine import protocol TSCBasic.OutputByteStream import class Basics.AsyncProcess -import struct TSCBasic.RegEx import enum TSCUtility.Diagnostics @@ -834,7 +833,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS guard let _ = self._buildPlan?.targets.first(where: { $0.module.name == target }) else { return nil } // Check for cases involving modules that cannot be found. - if let importedModule = try? RegEx(pattern: "no such module '(.+)'").matchGroups(in: message).first?.first { + if let importedModule = message.firstMatch(of: #/no such module '(?.+)'/#)?.module { // A target is importing a module that can't be found. We take a look at the build plan and see if can offer any advice. // Look for a target with the same module name as the one that's being imported. diff --git a/Sources/Build/LLBuildProgressTracker.swift b/Sources/Build/LLBuildProgressTracker.swift index 8b0c8c3571b..d21ce2d1813 100644 --- a/Sources/Build/LLBuildProgressTracker.swift +++ b/Sources/Build/LLBuildProgressTracker.swift @@ -20,7 +20,6 @@ import SPMBuildCore import SPMLLBuild import protocol TSCBasic.OutputByteStream -import struct TSCBasic.RegEx import class TSCBasic.ThreadSafeOutputByteStream import class TSCUtility.IndexStoreAPI @@ -448,11 +447,13 @@ final class LLBuildProgressTracker: LLBuildBuildSystemDelegate, SwiftCompilerOut // next we want to try and scoop out any errors from the output (if reasonable size, otherwise this // will be very slow), so they can later be passed to the advice provider in case of failure. if output.utf8.count < 1024 * 10 { - let regex = try! RegEx(pattern: #".*(error:[^\n]*)\n.*"#, options: .dotMatchesLineSeparators) - for match in regex.matchGroups(in: output) { - self.errorMessagesByTarget[parser.targetName] = ( - self.errorMessagesByTarget[parser.targetName] ?? [] - ) + [match[0]] + let regex = #/.*(?error:[^\n]*)\n.*/#.dotMatchesNewlines() + for match in output.matches(of: regex) { + self + .errorMessagesByTarget[parser.targetName] = ( + self + .errorMessagesByTarget[parser.targetName] ?? [] + ) + [String(match.error)] } } } diff --git a/Sources/PackageCollections/Providers/GitHubPackageMetadataProvider.swift b/Sources/PackageCollections/Providers/GitHubPackageMetadataProvider.swift index 9aee9b67155..e079c507254 100644 --- a/Sources/PackageCollections/Providers/GitHubPackageMetadataProvider.swift +++ b/Sources/PackageCollections/Providers/GitHubPackageMetadataProvider.swift @@ -234,23 +234,11 @@ struct GitHubPackageMetadataProvider: PackageMetadataProvider, Closable { // FIXME: use URL instead of string internal static func apiURL(_ url: String) -> URL? { - do { - let regex = try NSRegularExpression(pattern: #"([^/@]+)[:/]([^:/]+)/([^/.]+)(\.git)?$"#, options: .caseInsensitive) - if let match = regex.firstMatch(in: url, options: [], range: NSRange(location: 0, length: url.count)) { - if let hostRange = Range(match.range(at: 1), in: url), - let ownerRange = Range(match.range(at: 2), in: url), - let repoRange = Range(match.range(at: 3), in: url) { - let host = String(url[hostRange]) - let owner = String(url[ownerRange]) - let repo = String(url[repoRange]) - - return URL(string: "https://\(Self.apiHostPrefix)\(host)/repos/\(owner)/\(repo)") - } - } - return nil - } catch { + let regex = #/(?[^/@]+)[:/](?[^:/]+)/(?[^/.]+)(\.git)?$/#.ignoresCase() + guard let match = url.firstMatch(of: regex) else { return nil } + return URL(string: "https://\(Self.apiHostPrefix)\(match.host)/repos/\(match.owner)/\(match.repo)") } private func makeRequestOptions(validResponseCodes: [Int]) -> LegacyHTTPClientRequest.Options { diff --git a/Sources/PackageLoading/ManifestJSONParser.swift b/Sources/PackageLoading/ManifestJSONParser.swift index c65d298ddc3..13e300d11f2 100644 --- a/Sources/PackageLoading/ManifestJSONParser.swift +++ b/Sources/PackageLoading/ManifestJSONParser.swift @@ -20,7 +20,6 @@ import struct Basics.InternalError import struct Basics.RelativePath import enum TSCBasic.PathValidationError -import struct TSCBasic.RegEx import struct TSCBasic.StringError import struct TSCUtility.Version @@ -254,7 +253,7 @@ enum ManifestJSONParser { } /// Looks for Xcode-style build setting macros "$()". - fileprivate static let invalidValueRegex = try! RegEx(pattern: #"(\$\(.*?\))"#) + fileprivate static let invalidValueRegex = #/(\$\(.*?\))/# } extension SystemPackageProviderDescription { @@ -488,7 +487,7 @@ extension TargetBuildSettingDescription.Kind { static func from(_ name: String, values: [String]) throws -> Self { // Diagnose invalid values. for item in values { - let groups = ManifestJSONParser.invalidValueRegex.matchGroups(in: item).flatMap{ $0 } + let groups = item.matches(of: ManifestJSONParser.invalidValueRegex).map { $0.1 } if !groups.isEmpty { let error = "the build setting '\(name)' contains invalid component(s): \(groups.joined(separator: " "))" throw ManifestParseError.runtimeManifestErrors([error]) diff --git a/Sources/PackageLoading/Target+PkgConfig.swift b/Sources/PackageLoading/Target+PkgConfig.swift index 2eae23dd168..89c64e8aea2 100644 --- a/Sources/PackageLoading/Target+PkgConfig.swift +++ b/Sources/PackageLoading/Target+PkgConfig.swift @@ -14,7 +14,6 @@ import Basics import PackageModel import class Basics.AsyncProcess -import struct TSCBasic.RegEx import enum TSCUtility.Platform @@ -318,14 +317,14 @@ public func removeDefaultFlags(cFlags: [String], libs: [String]) throws -> ([Str /// /// See https://github.com/swiftlang/swift-package-manager/issues/6439 for details. public func patchSDKPaths(in flags: [String], to sdkRootPath: AbsolutePath) throws -> [String] { - let sdkRegex = try! RegEx(pattern: #"^.*\.sdk(\/.*|$)"#) + let sdkRegex = #/^.*\.sdk(\/.*|$)/# return try ["-I", "-L"].reduce(flags) { (flags, flag) in try patch(flag: flag, in: flags) { value in - guard let groups = sdkRegex.matchGroups(in: value).first else { + guard let match = value.firstMatch(of: sdkRegex) else { return value } - return sdkRootPath.pathString + groups[0] + return sdkRootPath.pathString + match.1 } } } diff --git a/Sources/PackageLoading/ToolsVersionParser.swift b/Sources/PackageLoading/ToolsVersionParser.swift index f38e2e4eb07..af127f0fa3a 100644 --- a/Sources/PackageLoading/ToolsVersionParser.swift +++ b/Sources/PackageLoading/ToolsVersionParser.swift @@ -15,7 +15,6 @@ import Foundation import PackageModel import struct TSCBasic.ByteString -import struct TSCBasic.RegEx import struct TSCUtility.Version @@ -621,19 +620,27 @@ extension ManifestLoader { do { contents = try fileSystem.getDirectoryContents(packagePath) } catch { throw ToolsVersionParser.Error.inaccessiblePackage(path: packagePath, reason: String(describing: error)) } - let regex = try! RegEx(pattern: #"^Package@swift-(\d+)(?:\.(\d+))?(?:\.(\d+))?.swift$"#) - + let regex = #/^Package@swift-(?\d+)(?:\.(?\d+))?(?:\.(?\d+))?.swift$/# // Collect all version-specific manifests at the given package path. let versionSpecificManifests = Dictionary(contents.compactMap{ file -> (ToolsVersion, String)? in - let parsedVersion = regex.matchGroups(in: file) - guard parsedVersion.count == 1, parsedVersion[0].count == 3 else { + let parsedVersionMatches = file.matches(of: regex) + guard parsedVersionMatches.count == 1, + let parsedVersion = parsedVersionMatches.first else { return nil } - - let major = Int(parsedVersion[0][0])! - let minor = parsedVersion[0][1].isEmpty ? 0 : Int(parsedVersion[0][1])! - let patch = parsedVersion[0][2].isEmpty ? 0 : Int(parsedVersion[0][2])! - + let major = Int(parsedVersion.major) ?? 0 + let minor: Int + if let minorString = parsedVersion.minor { + minor = Int(minorString) ?? 0 + } else { + minor = 0 + } + let patch: Int + if let patchString = parsedVersion.patch { + patch = Int(patchString) ?? 0 + } else { + patch = 0 + } return (ToolsVersion(version: Version(major, minor, patch)), file) }, uniquingKeysWith: { $1 }) diff --git a/Sources/PackageModel/SwiftLanguageVersion.swift b/Sources/PackageModel/SwiftLanguageVersion.swift index e7a1870c3a6..3be9107b9a5 100644 --- a/Sources/PackageModel/SwiftLanguageVersion.swift +++ b/Sources/PackageModel/SwiftLanguageVersion.swift @@ -12,8 +12,6 @@ import Foundation -import struct TSCBasic.RegEx - import struct TSCUtility.Version /// Represents a Swift language version. @@ -63,21 +61,31 @@ public struct SwiftLanguageVersion: Hashable, Sendable { } /// Regex for parsing the Swift language version. - private static let regex = try! RegEx(pattern: #"^(\d+)(?:\.(\d+))?(?:\.(\d+))?$"#) + private static let regex = #/^(?\d+)(?:\.(?\d+))?(?:\.(?\d+))?$/# /// Create an instance of Swift language version from the given string. /// // The Swift language version is not officially fixed but we require it to // be a valid SemVer-like string. public init?(string: String) { - let parsedVersion = SwiftLanguageVersion.regex.matchGroups(in: string) - guard parsedVersion.count == 1, parsedVersion[0].count == 3 else { + let parsedVersions = string.matches(of: SwiftLanguageVersion.regex) + guard parsedVersions.count == 1 else { return nil } - let major = Int(parsedVersion[0][0])! - let minor = parsedVersion[0][1].isEmpty ? 0 : Int(parsedVersion[0][1])! - let patch = parsedVersion[0][2].isEmpty ? 0 : Int(parsedVersion[0][2])! - + let parsedVersion = parsedVersions[0] + let major = Int(parsedVersion.major) ?? 0 + let minor: Int + if let minorString = parsedVersion.minor { + minor = Int(minorString) ?? 0 + } else { + minor = 0 + } + let patch: Int + if let patchString = parsedVersion.patch { + patch = Int(patchString) ?? 0 + } else { + patch = 0 + } self.rawValue = string self._version = Version(major, minor, patch) } diff --git a/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift b/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift index 0a06f2cdf3f..8ef5b9fde12 100644 --- a/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift +++ b/Sources/PackageRegistryCommand/PackageRegistryCommand+Publish.swift @@ -437,9 +437,9 @@ enum PackageArchiveSigner { } manifests.append(Manifest.filename) - let regex = try RegEx(pattern: #"^Package@swift-(\d+)(?:\.(\d+))?(?:\.(\d+))?.swift$"#) + let regex = #/^Package@swift-(\d+)(?:\.(\d+))?(?:\.(\d+))?.swift$/# let versionSpecificManifests: [String] = packageContents.filter { file in - let matchGroups = regex.matchGroups(in: file) + let matchGroups = file.matches(of: regex) return !matchGroups.isEmpty } manifests.append(contentsOf: versionSpecificManifests) diff --git a/Sources/SourceControl/GitRepository.swift b/Sources/SourceControl/GitRepository.swift index 06cc9bf46f6..9b3be1f9b5a 100644 --- a/Sources/SourceControl/GitRepository.swift +++ b/Sources/SourceControl/GitRepository.swift @@ -24,7 +24,6 @@ import enum TSCBasic.FileMode import struct TSCBasic.FileSystemError import class Basics.AsyncProcess import struct Basics.AsyncProcessResult -import struct TSCBasic.RegEx import protocol TSCUtility.DiagnosticLocationProviding import enum TSCUtility.Git @@ -1243,7 +1242,7 @@ public enum GitProgressParser: FetchProgress { case resolvingDeltas(progress: Double, currentObjects: Int, totalObjects: Int) /// The pattern used to match git output. Capture groups are labeled from ? to ?. - static let pattern = #""" + private static let regex = #/ (?xi) (?: remote: \h+ (?Enumerating \h objects): \h+ (?[0-9]+) @@ -1261,58 +1260,53 @@ public enum GitProgressParser: FetchProgress { (?Receiving \h objects): \h+ (?[0-9]+)% \h+ \((?[0-9]+)\/(?[0-9]+)\) (?:, \h+ (?[0-9]+.?[0-9]+ \h [A-Z]iB) \h+ \| \h+ (?[0-9]+.?[0-9]+ \h [A-Z]iB\/s))? ) - """# - static let regex = try? RegEx(pattern: pattern) + /# init?(from string: String) { - guard let matches = GitProgressParser.regex?.matchGroups(in: string).first, - matches.count == 20 else { return nil } - - if matches[0] == "Enumerating objects" { - guard let currentObjects = Int(matches[1]) else { return nil } - + guard let match = try? Self.regex.firstMatch(in: string) + else { return nil } + let captures = match.output + if captures.i0 == "Enumerating objects" { + guard let currentObjects = captures.i1.flatMap({ Int($0) }) + else { return nil } self = .enumeratingObjects(currentObjects: currentObjects) - } else if matches[2] == "Counting objects" { - guard let progress = Double(matches[3]), - let currentObjects = Int(matches[4]), - let totalObjects = Int(matches[5]) else { return nil } - + } else if captures.i2 == "Counting objects" { + guard let progress = captures.i3.flatMap({ Double($0) }), + let currentObjects = captures.i4.flatMap({ Int($0) }), + let totalObjects = captures.i5.flatMap({ Int($0) }) + else { return nil } self = .countingObjects( progress: progress / 100, currentObjects: currentObjects, totalObjects: totalObjects ) - - } else if matches[6] == "Compressing objects" { - guard let progress = Double(matches[7]), - let currentObjects = Int(matches[8]), - let totalObjects = Int(matches[9]) else { return nil } - + } else if captures.i6 == "Compressing objects" { + guard let progress = captures.i7.flatMap({ Double($0) }), + let currentObjects = captures.i8.flatMap({ Int($0) }), + let totalObjects = captures.i9.flatMap({ Int($0) }) + else { return nil } self = .compressingObjects( progress: progress / 100, currentObjects: currentObjects, totalObjects: totalObjects ) - - } else if matches[10] == "Resolving deltas" { - guard let progress = Double(matches[11]), - let currentObjects = Int(matches[12]), - let totalObjects = Int(matches[13]) else { return nil } - + } else if captures.i10 == "Resolving deltas" { + guard let progress = captures.i11.flatMap({ Double($0) }), + let currentObjects = captures.i12.flatMap({ Int($0) }), + let totalObjects = captures.i13.flatMap({ Int($0) }) + else { return nil } self = .resolvingDeltas( progress: progress / 100, currentObjects: currentObjects, totalObjects: totalObjects ) - - } else if matches[14] == "Receiving objects" { - guard let progress = Double(matches[15]), - let currentObjects = Int(matches[16]), - let totalObjects = Int(matches[17]) else { return nil } - - let downloadProgress = matches[18] - let downloadSpeed = matches[19] - + } else if captures.i14 == "Receiving objects" { + guard let progress = captures.i15.flatMap({ Double($0) }), + let currentObjects = captures.i16.flatMap({ Int($0) }), + let totalObjects = captures.i17.flatMap({ Int($0) }) + else { return nil } + let downloadProgress = captures.i18.map(String.init) + let downloadSpeed = captures.i19.map(String.init) self = .receivingObjects( progress: progress / 100, currentObjects: currentObjects, @@ -1320,7 +1314,6 @@ public enum GitProgressParser: FetchProgress { downloadProgress: downloadProgress, downloadSpeed: downloadSpeed ) - } else { return nil } diff --git a/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift b/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift index 080a3a4399c..20f1ecb9ae7 100644 --- a/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift +++ b/Sources/Workspace/PackageContainer/SourceControlPackageContainer.swift @@ -20,8 +20,6 @@ import PackageLoading import PackageModel import SourceControl -import struct TSCBasic.RegEx - import enum TSCUtility.Git import struct TSCUtility.Version @@ -280,8 +278,8 @@ internal final class SourceControlPackageContainer: PackageContainer, CustomStri ) } else { // Revision does not exist, so we customize the error. - let sha1RegEx = try! RegEx(pattern: #"\A[:xdigit:]{40}\Z"#) - let isBranchRev = sha1RegEx.matchGroups(in: revision).compactMap { $0 }.isEmpty + let sha1RegEx = #/\A[:xdigit:]{40}\Z/# + let isBranchRev = revision.matches(of: sha1RegEx).isEmpty let errorMessage = "could not find " + (isBranchRev ? "a branch named ‘\(revision)’" : "the commit \(revision)") let mainBranchExists = (try? repository.resolveRevision(identifier: "main")) != nil let suggestion = (revision == "master" && mainBranchExists) ? "did you mean ‘main’?" : nil