Skip to content
This repository was archived by the owner on Jun 4, 2025. It is now read-only.
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: 9 additions & 0 deletions .buildkite/check_regex.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash -eu

cd Tools

echo '--- :swift: Build'
swift build --product RegexChecker

echo '--- :face_with_monocle: Check Regular Expressions'
swift run --skip-build RegexChecker
10 changes: 10 additions & 0 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ steps:
env: *common_env
plugins: *common_plugins

#################
# Check Regex
#################
- label: ":face_with_monocle: Check Regular Expressions"
key: "check-regex"
command: ".buildkite/check_regex.sh"
env: *common_env
plugins: *common_plugins

#################
# Publish the Podspec (if we're building a tag)
#################
Expand All @@ -52,4 +61,5 @@ steps:
- "test"
- "validate"
- "lint"
- "check-regex"
if: build.tag != null
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ vendor/
fastlane/test_output
fastlane/README.md
fastlane/report.xml

.swiftpm
14 changes: 14 additions & 0 deletions Tools/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions Tools/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// swift-tools-version: 5.7

import PackageDescription

let package = Package(
name: "Tools",
platforms: [.macOS(.v12)],
products: [
.executable(
name: "RegexChecker",
targets: ["RegexChecker"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-syntax.git", exact: "0.50700.1"),
],
targets: [
.executableTarget(
name: "RegexChecker",
dependencies: [
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxParser", package: "swift-syntax"),
]
)
]
)
95 changes: 95 additions & 0 deletions Tools/Sources/RegexChecker/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import Foundation
import SwiftSyntax
import SwiftSyntaxParser

class RegexCheckerVisitor: SyntaxVisitor {
var declaration: VariableDeclSyntax?

var regexes = [String]()
var shouldFail: Bool = false

override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind {
for piece in (node.leadingTrivia ?? []) {
if case let .lineComment(comment) = piece,
comment.hasPrefix("// CHECK-REGEX") {
self.declaration = node
break
}
}

return .visitChildren
}

override func visitPost(_ node: VariableDeclSyntax) {
self.declaration = nil
}

override func visit(_ literal: StringLiteralExprSyntax) -> SyntaxVisitorContinueKind {
guard declaration != nil else {
return .visitChildren
}


for line in declaration!.description.split(separator: "\n") {
print("> \(line.trimmingCharacters(in: .whitespaces))")
}

guard literal.firstToken?.tokenKind == .rawStringDelimiter("#"),
literal.lastToken?.tokenKind == .rawStringDelimiter("#") else {
print("❌ Regex declaration must use raw string literal (i.e. #\"regex\"#).")
shouldFail = true
return .skipChildren
}

var regex = literal.description
regex.removeFirst()
regex.removeLast()

do {
let _ = try NSRegularExpression(pattern: regex)
print("✅ Valid regular expression.")
} catch {
print("❌ Not a regular expression.")
shouldFail = true
}

return .skipChildren
}

private func reportFailure(_ message: String) {
print("^ \(message)")

shouldFail = true
}
}

func checkRegexLiterals(directory: URL) throws -> Bool {
guard let enumerator = FileManager.default.enumerator(at: directory, includingPropertiesForKeys: nil) else {
fatalError("Can't enumerate directory at \(directory)")
}

var hasInvalidRegex = false

for case let file as URL in enumerator {
guard file.lastPathComponent.hasSuffix(".swift") else { continue }

let visitor = RegexCheckerVisitor()
let syntax = try SyntaxParser.parse(file)
visitor.walk(syntax)

if visitor.shouldFail {
hasInvalidRegex = true
}
}

return !hasInvalidRegex
}

func main() throws {
let thisFile = URL(fileURLWithPath: #filePath)
let sourcesDirectory = URL(string: "../../../WordPressShared", relativeTo: thisFile)!
let success = try checkRegexLiterals(directory: sourcesDirectory)
exit(success ? 0 : 1)
}

try main()
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ extension String {
/// Strips Gutenberg galleries from strings.
///
private func strippingGutenbergGalleries() -> String {
let pattern = "(?s)<!--\\swp:gallery?(.*?)wp:gallery\\s-->"
// CHECK-REGEX
let pattern = #"(?s)<!--\swp:gallery?(.*?)wp:gallery\s-->"#

return removingMatches(pattern: pattern, options: .caseInsensitive)
}
Expand Down
3 changes: 2 additions & 1 deletion WordPressShared/Core/Utility/String+StripShortcodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ extension String {
/// Creates a new string by stripping all shortcodes from this string.
///
func strippingShortcodes() -> String {
let pattern = "\\[[^\\]]+\\]"
// CHECK-REGEX
let pattern = #"\[[^\]]+\]"#

return removingMatches(pattern: pattern, options: .caseInsensitive)
}
Expand Down