Swift CPD
Duplicated code rarely looks dangerous on the day it’s pasted. It becomes dangerous the day one copy gets a fix and the other doesn’t. swift-cpd finds duplicated logic by its structure, not its text — through renamed variables, changed literals, edited lines and even different implementations of the same behavior — across Swift, Objective-C and C.
brew tap ericodx/homebrew-tools brew install swift-cpd
Different words, same code
Every field, literal and threshold below is different. A text diff sees two unrelated functions. swift-cpd sees the same 67 tokens once names and values are set aside — and the day the rules change in one of them, the other is your next bug report.
struct SignUpValidator {
func validate(_ form: Form) -> Bool {
guard !form.name.isEmpty else { return false }
guard form.age >= 18 else { return false }
guard form.email.contains("@") else { return false }
guard form.country.count == 2 else { return false }
return true
}
} struct CheckoutValidator {
func validate(_ form: Form) -> Bool {
guard !form.holder.isEmpty else { return false }
guard form.amount >= 1 else { return false }
guard form.card.contains(" ") else { return false }
guard form.currency.count == 3 else { return false }
return true
}
} $ swift-cpd Found 1 clone(s) in 2 files (0.00s) Clone 1 (Type-2, 67 tokens, 9 lines): Sources/App/Features/CheckoutValidator.swift:1-9 Sources/App/Features/SignUpValidator.swift:1-9
Four kinds of duplication
Most copy-paste detectors stop at the first two. The copies that hurt are usually the ones that were edited after the paste — or rewritten from memory.
How it works
Comparing every fragment with every other one doesn’t scale past a small project. Each stage narrows the work for the next, so the expensive comparisons only see pairs that are likely to be clones.
- 01 Discover Finds the Swift files in your configured paths — plus .m, .mm, .h, .c and .cpp with --cross-language — and leaves out what your exclude patterns name. Or reads them from a git ref instead of the working tree.
- 02 Tokenize SwiftSyntax tokenizes Swift; a C-family tokenizer handles the rest. Files are processed concurrently and cached by content hash, so unchanged files cost nothing on the next run.
- 03 Normalize Identifiers, numbers and strings become placeholders. Type names and called functions stay as they are, so code that only looks alike is not reported. Suppressed regions are removed here.
- 04 Detect A rolling hash finds exact and parameterized clones in linear time per file. Near-miss and semantic clones go through pre-filters first, so the expensive comparisons only run on likely pairs.
- 05 Report Text, JSON, a self-contained HTML page, or warnings Xcode shows in the editor — with the duplication percentage the quality gate checks.
Signal, not noise
A detector that flags every two functions with a guard statement gets turned off within a week. The defaults aim at duplication worth a refactor, and everything is tunable.
Built for CI
Duplication you only see in a dashboard keeps growing. Make it a rule instead: a threshold for the whole codebase, or a baseline that lets existing clones be and stops new ones at the door.
# Fail the build when duplication goes above 3% swift-cpd --max-duplication 3 Sources/ # Or accept today's clones once, and fail only on new ones swift-cpd --baseline-generate Sources/ swift-cpd --baseline .swift-cpd-baseline.json Sources/
# .pre-commit-config.yaml — check exactly what is being committed
- repo: https://github.com/ericodx/swift-cpd
hooks:
- id: swift-cpd
args: [--source-ref, ":0"] Two commands to a report
init writes a small .swift-cpd.yml with the folders it found. The second command is the whole analysis.
# Finds your sources and writes .swift-cpd.yml swift-cpd init # Run it swift-cpd
Warnings where you write code
The build plugin runs swift-cpd on every build of an Xcode or SPM target and shows each clone as a warning on its line in the editor. It never fails the build — it just makes the duplication hard to miss.
// Package.swift
.target(
name: "MyTarget",
plugins: [.plugin(name: "SwiftCPDPlugin", package: "swift-cpd")]
) Sources/App/Features/CheckoutValidator.swift:1:1: warning: Clone detected (Type-2, 67 tokens, 9 lines) — also in SignUpValidator.swift:1 Sources/App/Features/SignUpValidator.swift:1:1: warning: Clone detected (Type-2, 67 tokens, 9 lines) — also in CheckoutValidator.swift:1
Also included
From the blog
Notes from building it — the bugs, the measurements, and the design decisions.
Documentation
Free and open source
MIT-licensed, no account, no telemetry. It runs on your machine and your CI runners, and your code never leaves them. Issues, ideas and pull requests are welcome.