code complexity & repetition analysis tool
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs: mdbook & example/sample files

+1835 -1
+18 -1
docs/src/SUMMARY.md
··· 1 1 # Summary 2 2 3 - - [Chapter 1](./chapter_1.md) 3 + [Introduction](./introduction.md) 4 + 5 + # User Guide 6 + 7 + - [Installation](./installation.md) 8 + - [Quick Start](./quickstart.md) 9 + - [CLI Reference](./cli-reference.md) 10 + - [Configuration](./configuration.md) 11 + 12 + # Algorithms 13 + 14 + - [Cyclomatic Complexity](./cyclomatic-complexity.md) 15 + - [Lines of Code (LOC)](./lines-of-code.md) 16 + - [Clone Detection](./clone-detection.md) 17 + 18 + # Examples 19 + 20 + - [Usage](./examples.md)
+239
docs/src/cli-reference.md
··· 1 + # CLI Reference 2 + 3 + ## Commands 4 + 5 + ### `analyze` 6 + 7 + Run full analysis (complexity + clones + LOC). 8 + 9 + ```bash 10 + mccabre analyze [OPTIONS] [PATH] 11 + ``` 12 + 13 + **Arguments:** 14 + 15 + - `[PATH]` - Path to file or directory (default: `.`) 16 + 17 + **Options:** 18 + 19 + - `-j, --json` - Output in JSON format 20 + - `--threshold <N>` - Complexity warning threshold 21 + - `--min-tokens <N>` - Minimum tokens for clone detection (default: 30) 22 + - `-c, --config <FILE>` - Path to config file 23 + - `--no-gitignore` - Disable gitignore awareness 24 + 25 + **Examples:** 26 + 27 + ```bash 28 + # Analyze current directory 29 + mccabre analyze 30 + 31 + # Analyze specific path with JSON output 32 + mccabre analyze ./src --json 33 + 34 + # Custom thresholds 35 + mccabre analyze ./src --threshold 15 --min-tokens 25 36 + ``` 37 + 38 + ### `complexity` 39 + 40 + Analyze cyclomatic complexity and LOC only. 41 + 42 + ```bash 43 + mccabre complexity [OPTIONS] [PATH] 44 + ``` 45 + 46 + **Arguments:** 47 + 48 + - `[PATH]` - Path to file or directory (default: `.`) 49 + 50 + **Options:** 51 + 52 + - `-j, --json` - Output in JSON format 53 + - `--threshold <N>` - Complexity warning threshold 54 + - `-c, --config <FILE>` - Path to config file 55 + - `--no-gitignore` - Disable gitignore awareness 56 + 57 + **Examples:** 58 + 59 + ```bash 60 + # Check complexity of src/ 61 + mccabre complexity src/ 62 + 63 + # Fail if any file exceeds complexity of 20 64 + mccabre complexity src/ --threshold 20 65 + ``` 66 + 67 + ### `clones` 68 + 69 + Detect code clones only. 70 + 71 + ```bash 72 + mccabre clones [OPTIONS] [PATH] 73 + ``` 74 + 75 + **Arguments:** 76 + 77 + - `[PATH]` - Path to file or directory (default: `.`) 78 + 79 + **Options:** 80 + 81 + - `-j, --json` - Output in JSON format 82 + - `--min-tokens <N>` - Minimum tokens for detection (default: 30) 83 + - `-c, --config <FILE>` - Path to config file 84 + - `--no-gitignore` - Disable gitignore awareness 85 + 86 + **Examples:** 87 + 88 + ```bash 89 + # Find clones in current directory 90 + mccabre clones . 91 + 92 + # Find only large clones (50+ tokens) 93 + mccabre clones . --min-tokens 50 94 + 95 + # JSON output for processing 96 + mccabre clones src/ --json | jq '.clones | length' 97 + ``` 98 + 99 + ### `dump-config` 100 + 101 + Display current configuration. 102 + 103 + ```bash 104 + mccabre dump-config [OPTIONS] 105 + ``` 106 + 107 + **Options:** 108 + 109 + - `-c, --config <FILE>` - Path to config file (shows default if not specified) 110 + 111 + **Examples:** 112 + 113 + ```bash 114 + # Show default configuration 115 + mccabre dump-config 116 + 117 + # Show loaded configuration 118 + mccabre dump-config --config mccabre.toml 119 + ``` 120 + 121 + ## Global Options 122 + 123 + ### `-h, --help` 124 + 125 + Show help information. 126 + 127 + ```bash 128 + mccabre --help 129 + mccabre analyze --help 130 + ``` 131 + 132 + ### `-V, --version` 133 + 134 + Show version information. 135 + 136 + ```bash 137 + mccabre --version 138 + ``` 139 + 140 + ## Output Formats 141 + 142 + ### Terminal (Default) 143 + 144 + Colored, human-readable output: 145 + 146 + ```text 147 + FILE: src/main.rs 148 + Cyclomatic Complexity: 15 (warning) 149 + Physical LOC: 120 150 + Logical LOC: 85 151 + ``` 152 + 153 + Colors: 154 + 155 + - **Green**: Low/good 156 + - **Yellow**: Moderate/warning 157 + - **Red**: High/error 158 + 159 + ### JSON 160 + 161 + Machine-readable output for CI/CD: 162 + 163 + ```bash 164 + mccabre analyze src/ --json 165 + ``` 166 + 167 + ```json 168 + { 169 + "files": [ 170 + { 171 + "path": "src/main.rs", 172 + "loc": { 173 + "physical": 120, 174 + "logical": 85, 175 + "comments": 25, 176 + "blank": 10 177 + }, 178 + "cyclomatic": { 179 + "file_complexity": 15, 180 + "functions": [] 181 + } 182 + } 183 + ], 184 + "clones": [], 185 + "summary": { 186 + "total_files": 1, 187 + "total_physical_loc": 120, 188 + "total_logical_loc": 85, 189 + "avg_complexity": 15.0, 190 + "max_complexity": 15, 191 + "high_complexity_files": 1, 192 + "total_clones": 0 193 + } 194 + } 195 + ``` 196 + 197 + ## File Selection 198 + 199 + ### Supported Languages 200 + 201 + - **Rust**: `.rs` 202 + - **JavaScript**: `.js`, `.jsx`, `.mjs`, `.cjs` 203 + - **TypeScript**: `.ts`, `.tsx` 204 + - **Go**: `.go` 205 + - **Java**: `.java` 206 + - **C++**: `.cpp`, `.cc`, `.cxx`, `.h`, `.hpp`, `.hxx` 207 + 208 + ### Gitignore Support 209 + 210 + Mccabre respects `.gitignore` files by default: 211 + 212 + ```bash 213 + # Respects .gitignore (default) 214 + mccabre analyze . 215 + 216 + # Ignores .gitignore 217 + mccabre analyze . --no-gitignore 218 + ``` 219 + 220 + Automatically skips: 221 + 222 + - Files/directories in .gitignore 223 + - `.git/` directory 224 + - Binary files (by extension) 225 + 226 + ## Environment Variables 227 + 228 + Currently none. Configuration via: 229 + 230 + 1. CLI flags (highest priority) 231 + 2. Config file 232 + 3. Defaults 233 + 234 + ## See Also 235 + 236 + - [Configuration](./configuration.md) 237 + - [Cyclomatic Complexity](./cyclomatic-complexity.md) 238 + - [Clone Detection](./clone-detection.md) 239 + - [Examples](./examples.md)
+276
docs/src/clone-detection.md
··· 1 + # Clone Detection 2 + 3 + ## What is Code Cloning? 4 + 5 + Code clones are similar or identical code fragments that appear in multiple places. They indicate duplication and potential refactoring opportunities. 6 + 7 + ## How Mccabre Detects Clones 8 + 9 + Mccabre uses **Rabin-Karp rolling hash**, a fast string matching algorithm adapted for token sequences. 10 + 11 + ### Algorithm Overview 12 + 13 + 1. **Tokenization**: Convert source code to tokens 14 + 2. **Windowing**: Slide a window of N tokens across the sequence 15 + 3. **Hashing**: Compute a rolling hash for each window 16 + 4. **Matching**: Identify windows with identical hashes 17 + 5. **Reporting**: Group matches into clone groups 18 + 19 + ### Why This Approach? 20 + 21 + **Advantages:** 22 + 23 + - **Fast**: O(n) time complexity 24 + - **Language-agnostic**: Works on tokens, not syntax trees 25 + - **Tunable**: Adjust window size to find smaller or larger clones 26 + 27 + **Trade-offs:** 28 + 29 + - Finds exact token matches only 30 + - Doesn't detect semantic equivalence 31 + - May miss clones with renamed variables 32 + 33 + ## Using Clone Detection 34 + 35 + ### Basic Usage 36 + 37 + ```bash 38 + mccabre clones src/ 39 + ``` 40 + 41 + ### Adjust Sensitivity 42 + 43 + The `--min-tokens` flag controls the minimum clone size: 44 + 45 + ```bash 46 + # Find larger clones (more strict) 47 + mccabre clones src/ --min-tokens 50 48 + 49 + # Find smaller clones (more sensitive) 50 + mccabre clones src/ --min-tokens 15 51 + ``` 52 + 53 + ### Sample Output 54 + 55 + ```text 56 + DETECTED CLONES 57 + -------------------------------------------------------------------------------- 58 + Clone Group #1 (length: 32 tokens, 3 occurrences) 59 + - src/user.go:15-28 60 + - src/product.go:42-55 61 + - src/order.go:88-101 62 + 63 + Clone Group #2 (length: 45 tokens, 2 occurrences) 64 + - src/validators.rs:120-145 65 + - src/sanitizers.rs:67-92 66 + ``` 67 + 68 + ## Interpreting Results 69 + 70 + ### Clone Group Fields 71 + 72 + - **ID**: Unique identifier for the clone group 73 + - **Length**: Number of tokens in the duplicated sequence 74 + - **Occurrences**: How many times this clone appears 75 + - **Locations**: File paths and line ranges 76 + 77 + ### Significance 78 + 79 + | Tokens | Significance | Action | 80 + |--------|-------------|--------| 81 + | 15-25 | Minor duplication | Consider refactoring if repeated 3+ times | 82 + | 26-50 | Moderate duplication | Should refactor | 83 + | 50+ | Major duplication | Urgent refactoring needed | 84 + 85 + ## Refactoring Clones 86 + 87 + ### Example: Extract Function 88 + 89 + **Before:** 90 + 91 + ```go 92 + // In file1.go 93 + func processUser(input string) string { 94 + trimmed := strings.TrimSpace(input) 95 + if len(trimmed) == 0 { 96 + return "" 97 + } 98 + lower := strings.ToLower(trimmed) 99 + return lower 100 + } 101 + 102 + // In file2.go 103 + func processProduct(name string) string { 104 + trimmed := strings.TrimSpace(name) 105 + if len(trimmed) == 0 { 106 + return "" 107 + } 108 + lower := strings.ToLower(trimmed) 109 + return lower 110 + } 111 + ``` 112 + 113 + **After:** 114 + 115 + ```go 116 + // In utils.go 117 + func sanitizeString(input string) string { 118 + trimmed := strings.TrimSpace(input) 119 + if len(trimmed) == 0 { 120 + return "" 121 + } 122 + return strings.ToLower(trimmed) 123 + } 124 + 125 + // In file1.go 126 + func processUser(input string) string { 127 + return sanitizeString(input) 128 + } 129 + 130 + // In file2.go 131 + func processProduct(name string) string { 132 + return sanitizeString(name) 133 + } 134 + ``` 135 + 136 + ### Example: Extract Class/Module 137 + 138 + **Before:** Multiple files with similar validation logic 139 + 140 + **After:** Single `validation` module imported by all files 141 + 142 + ## Types of Clones 143 + 144 + ### Type 1: Exact Clones 145 + 146 + Identical code except for whitespace and comments. 147 + 148 + ```javascript 149 + // Clone 1 150 + function calc(a, b) { 151 + return a + b; 152 + } 153 + 154 + // Clone 2 155 + function calc(a, b) { 156 + return a + b; 157 + } 158 + ``` 159 + 160 + ✅ **Mccabre detects these** 161 + 162 + ### Type 2: Renamed Clones 163 + 164 + Identical except for variable/function names. 165 + 166 + ```javascript 167 + // Clone 1 168 + function add(x, y) { 169 + return x + y; 170 + } 171 + 172 + // Clone 2 173 + function sum(a, b) { 174 + return a + b; 175 + } 176 + ``` 177 + 178 + ❌ **Mccabre does NOT detect these** (yet) 179 + 180 + ### Type 3: Near-Miss Clones 181 + 182 + Similar structure with minor modifications. 183 + 184 + ```javascript 185 + // Clone 1 186 + function validate(user) { 187 + if (!user.email) return false; 188 + if (!user.name) return false; 189 + return true; 190 + } 191 + 192 + // Clone 2 193 + function validate(product) { 194 + if (!product.id) return false; 195 + if (!product.price) return false; 196 + if (!product.name) return false; 197 + return true; 198 + } 199 + ``` 200 + 201 + ❌ **Mccabre does NOT detect these** 202 + 203 + ### Type 4: Semantic Clones 204 + 205 + Different syntax, same behavior. 206 + 207 + ```javascript 208 + // Clone 1 209 + const sum = arr.reduce((a, b) => a + b, 0); 210 + 211 + // Clone 2 212 + let sum = 0; 213 + for (let num of arr) { 214 + sum += num; 215 + } 216 + ``` 217 + 218 + ❌ **Mccabre does NOT detect these** 219 + 220 + ## Configuration 221 + 222 + ### Via Command Line 223 + 224 + ```bash 225 + mccabre clones . --min-tokens 30 226 + ``` 227 + 228 + ### Via Config File 229 + 230 + Create `mccabre.toml`: 231 + 232 + ```toml 233 + [clones] 234 + enabled = true 235 + min_tokens = 30 236 + ``` 237 + 238 + ## JSON Output 239 + 240 + ```bash 241 + mccabre clones src/ --json 242 + ``` 243 + 244 + ```json 245 + { 246 + "clones": [ 247 + { 248 + "id": 1, 249 + "length": 32, 250 + "locations": [ 251 + { 252 + "file": "src/user.go", 253 + "start_line": 15, 254 + "end_line": 28 255 + }, 256 + { 257 + "file": "src/product.go", 258 + "start_line": 42, 259 + "end_line": 55 260 + } 261 + ] 262 + } 263 + ] 264 + } 265 + ``` 266 + 267 + ## References 268 + 269 + - [Rabin-Karp Algorithm](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) 270 + - [Code Clone Research](https://www.sei.cmu.edu/library/code-similarity-detection-using-syntax-agnostic-locality-sensitive-hashing/) 271 + 272 + ## See Also 273 + 274 + - [Cyclomatic Complexity](./cyclomatic-complexity.md) 275 + - [CLI Reference](./cli-reference.md) 276 + - [Examples](./examples.md)
+230
docs/src/configuration.md
··· 1 + # Configuration 2 + 3 + Mccabre can be configured via config files and CLI flags. 4 + 5 + ## Configuration Priority 6 + 7 + 1. **CLI flags** (highest priority) 8 + 2. **Config file** (`mccabre.toml`) 9 + 3. **Defaults** (lowest priority) 10 + 11 + CLI flags override config file settings. 12 + 13 + ## Config File 14 + 15 + Create a `mccabre.toml` file in your project root: 16 + 17 + ```toml 18 + [complexity] 19 + warning_threshold = 10 20 + error_threshold = 20 21 + 22 + [clones] 23 + enabled = true 24 + min_tokens = 30 25 + 26 + [files] 27 + respect_gitignore = true 28 + ``` 29 + 30 + ## Configuration Options 31 + 32 + ### Complexity Settings 33 + 34 + ```toml 35 + [complexity] 36 + warning_threshold = 10 # Yellow warning at this level 37 + error_threshold = 20 # Red error at this level 38 + ``` 39 + 40 + **Defaults:** 41 + 42 + - `warning_threshold`: 10 43 + - `error_threshold`: 20 44 + 45 + **CLI Override:** 46 + 47 + ```bash 48 + mccabre analyze --threshold 15 49 + ``` 50 + 51 + ### Clone Detection Settings 52 + 53 + ```toml 54 + [clones] 55 + enabled = true # Enable/disable clone detection 56 + min_tokens = 30 # Minimum token sequence length 57 + ``` 58 + 59 + **Defaults:** 60 + 61 + - `enabled`: true 62 + - `min_tokens`: 30 63 + 64 + **CLI Override:** 65 + 66 + ```bash 67 + mccabre analyze --min-tokens 25 68 + ``` 69 + 70 + ### File Settings 71 + 72 + ```toml 73 + [files] 74 + respect_gitignore = true # Honor .gitignore files 75 + ``` 76 + 77 + **Defaults:** 78 + 79 + - `respect_gitignore`: true 80 + 81 + **CLI Override:** 82 + 83 + ```bash 84 + mccabre analyze --no-gitignore 85 + ``` 86 + 87 + ## Loading Configuration 88 + 89 + ### Automatic Discovery 90 + 91 + Mccabre searches for config files in this order: 92 + 93 + 1. `mccabre.toml` 94 + 2. `.mccabre.toml` 95 + 3. `.mccabre/config.toml` 96 + 97 + The first file found is used. 98 + 99 + ### Explicit Path 100 + 101 + Specify a config file: 102 + 103 + ```bash 104 + mccabre analyze --config /path/to/config.toml 105 + ``` 106 + 107 + ### No Config File 108 + 109 + If no config file exists, defaults are used. 110 + 111 + ## Example Configurations 112 + 113 + ### Strict Mode 114 + 115 + For critical codebases: 116 + 117 + ```toml 118 + [complexity] 119 + warning_threshold = 5 120 + error_threshold = 10 121 + 122 + [clones] 123 + enabled = true 124 + min_tokens = 20 125 + 126 + [files] 127 + respect_gitignore = true 128 + ``` 129 + 130 + ### Lenient Mode 131 + 132 + For legacy codebases: 133 + 134 + ```toml 135 + [complexity] 136 + warning_threshold = 20 137 + error_threshold = 40 138 + 139 + [clones] 140 + enabled = true 141 + min_tokens = 50 142 + 143 + [files] 144 + respect_gitignore = true 145 + ``` 146 + 147 + ### Clone-Focused 148 + 149 + Focus on duplication: 150 + 151 + ```toml 152 + [complexity] 153 + warning_threshold = 100 # Effectively disable 154 + error_threshold = 200 155 + 156 + [clones] 157 + enabled = true 158 + min_tokens = 15 # Very sensitive 159 + 160 + [files] 161 + respect_gitignore = true 162 + ``` 163 + 164 + ## Per-Project Settings 165 + 166 + Different projects can have different configs: 167 + 168 + ```bash 169 + project-a/ 170 + ├── mccabre.toml # Strict settings 171 + └── src/ 172 + 173 + project-b/ 174 + ├── mccabre.toml # Lenient settings 175 + └── src/ 176 + ``` 177 + 178 + Each project's config is automatically loaded when analyzing that directory. 179 + 180 + ## Viewing Current Configuration 181 + 182 + Check what settings are active: 183 + 184 + ```bash 185 + mccabre dump-config 186 + ``` 187 + 188 + Output: 189 + 190 + ```text 191 + CONFIGURATION 192 + ================================================================================ 193 + 194 + Complexity Settings: 195 + Warning threshold: 10 196 + Error threshold: 20 197 + 198 + Clone Detection Settings: 199 + Enabled: true 200 + Minimum tokens: 30 201 + 202 + File Settings: 203 + Respect .gitignore: true 204 + ``` 205 + 206 + ## Ignoring Files 207 + 208 + Use `.gitignore` to exclude files/directories: 209 + 210 + ```text 211 + # .gitignore 212 + target/ 213 + node_modules/ 214 + build/ 215 + *.generated.rs 216 + ``` 217 + 218 + Mccabre automatically respects these exclusions. 219 + 220 + To analyze everything (ignore gitignore): 221 + 222 + ```bash 223 + mccabre analyze . --no-gitignore 224 + ``` 225 + 226 + ## See Also 227 + 228 + - [CLI Reference](./cli-reference.md) 229 + - [Cyclomatic Complexity](./cyclomatic-complexity.md) 230 + - [Clone Detection](./clone-detection.md)
+197
docs/src/cyclomatic-complexity.md
··· 1 + # Cyclomatic Complexity 2 + 3 + ## What is Cyclomatic Complexity? 4 + 5 + Cyclomatic Complexity (CC), introduced by Thomas McCabe in 1976, measures the number of independent paths through a program's source code. It provides a quantitative measure of code complexity. 6 + 7 + ## How It Works 8 + 9 + Mccabre uses a simplified formula: 10 + 11 + `CC = (number of decision points) + 1` 12 + 13 + ### Decision Points 14 + 15 + A decision point is any control flow statement that creates a branch: 16 + 17 + - `if`, `else if` 18 + - `while`, `for`, `loop` 19 + - `switch`, `match`, `case` 20 + - `catch` 21 + - Logical operators: `&&`, `||` 22 + - Ternary operator: `?` 23 + 24 + ### Example 25 + 26 + ```javascript 27 + function checkUser(user) { // CC starts at 1 28 + if (!user) { // +1 = 2 29 + return false; 30 + } 31 + 32 + if (user.age > 18 && user.verified) { // +1 (if) +1 (&&) = 4 33 + return true; 34 + } 35 + 36 + return false; 37 + } 38 + // Total CC = 4 39 + ``` 40 + 41 + ## Interpretation 42 + 43 + | CC Range | Risk Level | Recommendation | 44 + |----------|-----------|----------------| 45 + | 1-10 | Low | Simple, easy to test | 46 + | 11-20 | Moderate | Consider refactoring | 47 + | 21-50 | High | Should refactor | 48 + | 50+ | Very High | Urgent refactoring needed | 49 + 50 + ## Why It Matters 51 + 52 + ### Testing Complexity 53 + 54 + Higher CC means: 55 + 56 + - More test cases needed for full coverage 57 + - Higher chance of bugs 58 + - Harder to understand and maintain 59 + 60 + A function with CC=10 requires at least 10 test cases to cover all paths. 61 + 62 + ### Maintenance Burden 63 + 64 + Complex functions are: 65 + 66 + - Harder to modify without introducing bugs 67 + - More difficult for new developers to understand 68 + - More prone to subtle edge cases 69 + 70 + ## Reducing Complexity 71 + 72 + ### Extract Methods 73 + 74 + **Before (CC=8):** 75 + 76 + ```javascript 77 + function processOrder(order) { 78 + if (!order.id) throw new Error("No ID"); 79 + if (order.status === "cancelled") return null; 80 + if (order.items.length === 0) throw new Error("No items"); 81 + 82 + let total = 0; 83 + for (let item of order.items) { 84 + if (item.price && item.quantity) { 85 + total += item.price * item.quantity; 86 + } 87 + } 88 + 89 + return total; 90 + } 91 + ``` 92 + 93 + **After (CC=3 + 3 = 6 total):** 94 + 95 + ```javascript 96 + function processOrder(order) { // CC=3 97 + validateOrder(order); 98 + return calculateTotal(order); 99 + } 100 + 101 + function validateOrder(order) { // CC=3 102 + if (!order.id) throw new Error("No ID"); 103 + if (order.status === "cancelled") return null; 104 + if (order.items.length === 0) throw new Error("No items"); 105 + } 106 + 107 + function calculateTotal(order) { // CC=2 108 + let total = 0; 109 + for (let item of order.items) { 110 + if (item.price && item.quantity) { 111 + total += item.price * item.quantity; 112 + } 113 + } 114 + return total; 115 + } 116 + ``` 117 + 118 + ### Use Early Returns 119 + 120 + **Before:** 121 + 122 + ```rust 123 + fn check(x: i32) -> bool { // CC=3 124 + let mut result = false; 125 + if x > 0 { 126 + if x < 100 { 127 + result = true; 128 + } 129 + } 130 + result 131 + } 132 + ``` 133 + 134 + **After:** 135 + 136 + ```rust 137 + fn check(x: i32) -> bool { // CC=3 (same but cleaner) 138 + if x <= 0 { return false; } 139 + if x >= 100 { return false; } 140 + true 141 + } 142 + ``` 143 + 144 + ### Replace Complex Conditions 145 + 146 + **Before:** 147 + 148 + ```javascript 149 + if ((user.role === "admin" || user.role === "moderator") && 150 + user.active && !user.suspended) { // CC contribution: 4 151 + // ... 152 + } 153 + ``` 154 + 155 + **After:** 156 + 157 + ```javascript 158 + function canModerate(user) { // CC=3 159 + const isModerator = user.role === "admin" || user.role === "moderator"; 160 + return isModerator && user.active && !user.suspended; 161 + } 162 + 163 + if (canModerate(user)) { // CC=1 164 + // ... 165 + } 166 + ``` 167 + 168 + ## Using with Mccabre 169 + 170 + ### Check Specific Files 171 + 172 + ```bash 173 + mccabre complexity src/complex.js 174 + ``` 175 + 176 + ### Set Custom Threshold 177 + 178 + ```bash 179 + mccabre complexity . --threshold 15 180 + ``` 181 + 182 + ### JSON Output for CI 183 + 184 + ```bash 185 + mccabre complexity . --json | jq '.files[] | select(.cyclomatic.file_complexity > 20)' 186 + ``` 187 + 188 + ## References 189 + 190 + - [McCabe (1976): "A Complexity Measure"](https://www.literateprogramming.com/mccabe.pdf) 191 + - [NIST Special Publication 500-235](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication500-235.pdf) 192 + 193 + ## See Also 194 + 195 + - [Lines of Code](./lines-of-code.md) 196 + - [Clone Detection](./clone-detection.md) 197 + - [CLI Reference](./cli-reference.md)
+284
docs/src/examples.md
··· 1 + # Example Usage 2 + 3 + This page demonstrates common Mccabre workflows with real examples. 4 + 5 + ## Example Files 6 + 7 + The `examples/` directory contains sample files demonstrating different issues: 8 + 9 + - **complex.js** - High cyclomatic complexity 10 + - **long.py** - Many lines of code with comments 11 + - **not_dry.go** - Duplicated code (clones) 12 + 13 + ## Analyzing the Examples 14 + 15 + ### Full Analysis 16 + 17 + ```bash 18 + mccabre analyze examples/ 19 + ``` 20 + 21 + **Output:** 22 + 23 + ```text 24 + ================================================================================ 25 + MCCABRE CODE ANALYSIS REPORT 26 + ================================================================================ 27 + 28 + SUMMARY 29 + -------------------------------------------------------------------------------- 30 + Total files analyzed: 3 31 + Total physical LOC: 215 32 + Total logical LOC: 165 33 + Average complexity: 10.33 34 + Maximum complexity: 18 35 + High complexity files: 1 36 + Clone groups detected: 2 37 + 38 + FILE METRICS 39 + -------------------------------------------------------------------------------- 40 + FILE: examples/complex.js 41 + Cyclomatic Complexity: 18 (moderate) 42 + Physical LOC: 49 43 + Logical LOC: 42 44 + Comment lines: 2 45 + Blank lines: 5 46 + 47 + FILE: examples/long.py 48 + Cyclomatic Complexity: 8 (low) 49 + Physical LOC: 95 50 + Logical LOC: 62 51 + Comment lines: 18 52 + Blank lines: 15 53 + 54 + FILE: examples/not_dry.go 55 + Cyclomatic Complexity: 6 (low) 56 + Physical LOC: 71 57 + Logical LOC: 61 58 + Comment lines: 5 59 + Blank lines: 5 60 + 61 + DETECTED CLONES 62 + -------------------------------------------------------------------------------- 63 + Clone Group #1 (length: 30 tokens, 3 occurrences) 64 + - examples/not_dry.go:12-26 65 + - examples/not_dry.go:30-44 66 + - examples/not_dry.go:48-62 67 + ``` 68 + 69 + ### Complexity Only 70 + 71 + ```bash 72 + mccabre complexity examples/complex.js 73 + ``` 74 + 75 + Shows that `complex.js` has high cyclomatic complexity due to many conditional branches. 76 + 77 + ### Clone Detection Only 78 + 79 + ```bash 80 + mccabre clones examples/not_dry.go 81 + ``` 82 + 83 + Identifies the three nearly-identical functions in `not_dry.go`. 84 + 85 + ## Real-World Scenarios 86 + 87 + ### Scenario 1: Pre-Commit Check 88 + 89 + Ensure code quality before committing: 90 + 91 + ```bash 92 + #!/bin/sh 93 + # .git/hooks/pre-commit 94 + 95 + echo "Checking code complexity..." 96 + 97 + # Get list of staged Rust files 98 + FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.rs$') 99 + 100 + if [ -n "$FILES" ]; then 101 + mccabre complexity $FILES --threshold 15 102 + if [ $? -ne 0 ]; then 103 + echo "❌ Complexity check failed!" 104 + exit 1 105 + fi 106 + fi 107 + 108 + echo "✅ Complexity check passed!" 109 + ``` 110 + 111 + ### Scenario 2: Finding Refactoring Targets 112 + 113 + Combine complexity and clone detection: 114 + 115 + ```bash 116 + # Find high-complexity files 117 + echo "=== High Complexity Files ===" 118 + mccabre complexity src/ --json | \ 119 + jq -r '.files[] | select(.cyclomatic.file_complexity > 15) | .path' 120 + 121 + # Find duplicated code 122 + echo "\n=== Code Clones ===" 123 + mccabre clones src/ --min-tokens 25 124 + ``` 125 + 126 + Then refactor the flagged files. 127 + 128 + ### Scenario 3: Tracking Technical Debt 129 + 130 + Weekly complexity tracking: 131 + 132 + ```bash 133 + #!/bin/bash 134 + # weekly-report.sh 135 + 136 + DATE=$(date +%Y-%m-%d) 137 + REPORT_DIR="reports" 138 + 139 + mkdir -p "$REPORT_DIR" 140 + 141 + # Generate report 142 + mccabre analyze src/ --json > "$REPORT_DIR/report-$DATE.json" 143 + 144 + # Extract key metrics 145 + echo "Complexity Report - $DATE" 146 + jq '.summary' "$REPORT_DIR/report-$DATE.json" 147 + 148 + # Compare with last week 149 + LAST_WEEK=$(ls -t $REPORT_DIR/report-*.json | sed -n 2p) 150 + if [ -n "$LAST_WEEK" ]; then 151 + echo "\nChange from last week:" 152 + jq -s '.[1].summary.avg_complexity - .[0].summary.avg_complexity' \ 153 + "$LAST_WEEK" "$REPORT_DIR/report-$DATE.json" 154 + fi 155 + ``` 156 + 157 + ### Scenario 5: Code Review Automation 158 + 159 + Automatically comment on PRs with complexity issues: 160 + 161 + ```bash 162 + #!/bin/bash 163 + # pr-comment.sh 164 + 165 + # Run analysis 166 + mccabre analyze src/ --json > complexity.json 167 + 168 + # Extract high complexity files 169 + HIGH_COMPLEXITY=$(jq -r '.files[] | select(.cyclomatic.file_complexity > 15) | 170 + "- `\(.path)`: Complexity \(.cyclomatic.file_complexity)"' complexity.json) 171 + 172 + if [ -n "$HIGH_COMPLEXITY" ]; then 173 + # Post comment to GitHub PR (requires gh CLI) 174 + gh pr comment --body "## ⚠️ Complexity Warning 175 + 176 + Files with high complexity: 177 + $HIGH_COMPLEXITY 178 + 179 + Consider refactoring before merging." 180 + fi 181 + ``` 182 + 183 + ### Scenario 5: Compare Branches 184 + 185 + Compare complexity between branches: 186 + 187 + ```bash 188 + #!/bin/bash 189 + # compare-branches.sh 190 + 191 + MAIN_BRANCH="main" 192 + FEATURE_BRANCH=$(git branch --show-current) 193 + 194 + # Analyze main 195 + git checkout "$MAIN_BRANCH" 196 + mccabre analyze src/ --json > /tmp/main-complexity.json 197 + 198 + # Analyze feature 199 + git checkout "$FEATURE_BRANCH" 200 + mccabre analyze src/ --json > /tmp/feature-complexity.json 201 + 202 + # Compare 203 + echo "=== Complexity Comparison ===" 204 + echo "Main branch avg: $(jq '.summary.avg_complexity' /tmp/main-complexity.json)" 205 + echo "Feature branch avg: $(jq '.summary.avg_complexity' /tmp/feature-complexity.json)" 206 + 207 + # Check if complexity increased 208 + MAIN_AVG=$(jq '.summary.avg_complexity' /tmp/main-complexity.json) 209 + FEATURE_AVG=$(jq '.summary.avg_complexity' /tmp/feature-complexity.json) 210 + 211 + if (( $(echo "$FEATURE_AVG > $MAIN_AVG * 1.1" | bc -l) )); then 212 + echo "❌ Complexity increased by more than 10%!" 213 + exit 1 214 + else 215 + echo "✅ Complexity is acceptable" 216 + fi 217 + ``` 218 + 219 + ## Filtering and Processing 220 + 221 + ### Find Files Above Threshold 222 + 223 + ```bash 224 + # JSON output piped to jq 225 + mccabre complexity src/ --json | \ 226 + jq '.files[] | select(.cyclomatic.file_complexity > 10) | .path' 227 + ``` 228 + 229 + ### Count Clone Groups 230 + 231 + ```bash 232 + mccabre clones src/ --json | jq '.clones | length' 233 + ``` 234 + 235 + ### Generate HTML 236 + 237 + ```bash 238 + # Create HTML from JSON 239 + mccabre analyze src/ --json | \ 240 + jq -r '.files[] | "<tr><td>\(.path)</td><td>\(.cyclomatic.file_complexity)</td></tr>"' | \ 241 + (echo "<html><table>" && cat && echo "</table></html>") > report.html 242 + ``` 243 + 244 + ### Summary Statistics 245 + 246 + ```bash 247 + # Extract just the summary 248 + mccabre analyze src/ --json | jq '.summary' 249 + ``` 250 + 251 + ## Tips and Tricks 252 + 253 + ### Incremental Analysis 254 + 255 + Analyze only changed files: 256 + 257 + ```bash 258 + # Files changed in last commit 259 + git diff --name-only HEAD~1 | grep '\.rs$' | xargs mccabre complexity 260 + ``` 261 + 262 + ### Watch Mode 263 + 264 + Continuously monitor (requires `watch` or `entr`): 265 + 266 + ```bash 267 + # Using entr 268 + ls src/**/*.rs | entr mccabre complexity src/ 269 + ``` 270 + 271 + ### Focus on New Code 272 + 273 + Analyze only files in your feature branch: 274 + 275 + ```bash 276 + git diff --name-only main...HEAD | grep '\.rs$' | xargs mccabre analyze 277 + ``` 278 + 279 + ## Next Steps 280 + 281 + - Read about [Cyclomatic Complexity](./cyclomatic-complexity.md) 282 + - Learn about [Clone Detection](./clone-detection.md) 283 + - Configure [thresholds](./configuration.md) 284 + - Check the [CLI Reference](./cli-reference.md)
+56
docs/src/installation.md
··· 1 + # Installation 2 + 3 + ## From Source 4 + 5 + ### Prerequisites 6 + 7 + - Rust 1.70 or later 8 + - Cargo (comes with Rust) 9 + 10 + ### Build and Install 11 + 12 + ```bash 13 + # Clone the repository 14 + git clone https://github.com/yourusername/mccabre.git 15 + cd mccabre 16 + 17 + # Build and install 18 + cargo install --path crates/cli 19 + ``` 20 + 21 + ### Development Build 22 + 23 + ```bash 24 + # Build in debug mode 25 + cargo build 26 + 27 + # Run directly 28 + cargo run --bin mccabre -- analyze examples/ 29 + 30 + # Run tests 31 + cargo test --quiet 32 + ``` 33 + 34 + ## Verifying Installation 35 + 36 + After building, verify your installation: 37 + 38 + ```bash 39 + mccabre --version 40 + 41 + mccabre --help 42 + 43 + mccabre analyze examples/ 44 + ``` 45 + 46 + You should see colored output with complexity metrics and detected clones. 47 + 48 + ## Uninstall 49 + 50 + ```bash 51 + cargo uninstall mccabre 52 + ``` 53 + 54 + ## Configuration 55 + 56 + See [Configuration](./configuration.md) for customizing thresholds and behavior.
+29
docs/src/introduction.md
··· 1 + # Introduction 2 + 3 + **Mccabre** language-agnostic code complexity and clone detection tool designed to help developers identify problematic & repeated code patterns. 4 + 5 + ## Features 6 + 7 + - **Cyclomatic Complexity Analysis**: Measure control-flow complexity using McCabe's algorithm 8 + - **Lines of Code Metrics**: Count physical, logical, comment, and blank lines 9 + - **Clone Detection**: Find duplicated code using Rabin-Karp rolling hash 10 + - **Multi-Language Support**: Rust, JavaScript/TypeScript, Go, Java, and C++ 11 + - **Gitignore Aware**: Automatically respects .gitignore files 12 + - **Multiple Output Formats**: Beautiful terminal output or JSON 13 + 14 + ## Design Philosophy 15 + 16 + Mccabre prioritizes: 17 + 18 + 1. **Speed**: Linear or near-linear performance through tokenization instead of full parsing 19 + 2. **Simplicity**: Easy to use with sensible defaults 20 + 3. **Actionability**: Clear, color-coded output highlighting issues 21 + 4. **Extensibility**: Modular design allowing future enhancements 22 + 23 + ## Limitations 24 + 25 + - **Token-based**: Function detection is heuristic-based and may miss some functions 26 + - **Language Support**: Currently supports C-style languages; Python coming soon 27 + - **Clone Detection**: Finds exact token matches, not semantic equivalence (yet) 28 + 29 + See the [Quick Start](./quickstart.md) guide to begin using Mccabre.
+193
docs/src/lines-of-code.md
··· 1 + # Lines of Code (LOC) 2 + 3 + ## Overview 4 + 5 + Lines of Code (LOC) is a fundamental software metric that measures the size of a codebase. Mccabre provides several LOC variants to give you a complete picture. 6 + 7 + ## Metrics Provided 8 + 9 + ### Physical LOC 10 + 11 + Total number of lines in the file, including everything. 12 + 13 + ```rust 14 + fn hello() { // Line 1 15 + 16 + 17 + println!("Hi"); // Line 4 18 + } // Line 5 19 + // Physical LOC = 5 20 + ``` 21 + 22 + ### Logical LOC 23 + 24 + Non-blank, non-comment lines that contain actual code. 25 + 26 + ```rust 27 + fn hello() { // Logical 28 + // This is a comment (not counted) 29 + 30 + println!("Hi"); // Logical 31 + } // Logical 32 + // Logical LOC = 3 33 + ``` 34 + 35 + ### Comment Lines 36 + 37 + Lines that contain comments (single-line or multi-line). 38 + 39 + ```rust 40 + // This is counted // Comment line 41 + /* This too */ // Comment line 42 + let x = 5; // inline // Code line (code takes precedence) 43 + ``` 44 + 45 + ### Blank Lines 46 + 47 + Lines that contain only whitespace. 48 + 49 + ## Why LOC Matters 50 + 51 + ### Productivity Tracking 52 + 53 + - Monitor codebase growth over time 54 + - Estimate project size 55 + - Compare different implementations 56 + 57 + ### Maintenance Effort 58 + 59 + Larger codebases generally require: 60 + 61 + - More time to understand 62 + - More effort to maintain 63 + - More potential for bugs 64 + 65 + ### Code Density 66 + 67 + Compare logical vs physical LOC: 68 + 69 + - High ratio (logical/physical): Dense code, few comments 70 + - Low ratio: Well-commented, more likely to be readable code 71 + 72 + ## Using LOC with Mccabre 73 + 74 + ### Basic Usage 75 + 76 + ```bash 77 + # Analyze LOC for a directory 78 + mccabre analyze src/ 79 + 80 + # Complexity command also includes LOC 81 + mccabre complexity src/ 82 + ``` 83 + 84 + ### Sample Output 85 + 86 + ```text 87 + FILE: src/main.rs 88 + Cyclomatic Complexity: 5 89 + Physical LOC: 120 90 + Logical LOC: 85 91 + Comment lines: 25 92 + Blank lines: 10 93 + ``` 94 + 95 + ### JSON 96 + 97 + ```bash 98 + mccabre analyze src/ --json 99 + ``` 100 + 101 + ```json 102 + { 103 + "files": [ 104 + { 105 + "path": "src/main.rs", 106 + "loc": { 107 + "physical": 120, 108 + "logical": 85, 109 + "comments": 25, 110 + "blank": 10 111 + } 112 + } 113 + ] 114 + } 115 + ``` 116 + 117 + ## Interpreting Results 118 + 119 + ### Healthy Ratios 120 + 121 + **Comment Ratio**: `comments / logical` 122 + 123 + - 0.1-0.3 (10-30%): Generally good 124 + - <0.05: Likely under-commented 125 + - >0.5: Possibly over-commented or tutorial code 126 + 127 + **Code Density**: `logical / physical` 128 + 129 + - 0.6-0.8: Good balance 130 + - <0.5: Many blank lines/comments (verbose) 131 + - >0.9: Very dense (potentially hard to read) 132 + 133 + ## LOC Limitations 134 + 135 + ### Not a Quality Metric 136 + 137 + More LOC doesn't mean: 138 + 139 + - ✗ Better code 140 + - ✗ More features 141 + - ✗ More value 142 + 143 + ### Context Matters 144 + 145 + Compare LOC only within similar contexts: 146 + 147 + - Same language 148 + - Same problem domain 149 + - Same team/style 150 + 151 + ### Generated Code 152 + 153 + LOC counts everything, including: 154 + 155 + - Auto-generated code 156 + - Vendored dependencies 157 + - Build artifacts 158 + 159 + Use `.gitignore` to exclude these (Mccabre respects gitignore). 160 + 161 + ## Tracking LOC Over Time 162 + 163 + ### Baseline 164 + 165 + ```bash 166 + # Create baseline 167 + mccabre analyze src/ --json > baseline.json 168 + ``` 169 + 170 + ### Compare 171 + 172 + ```bash 173 + # Later... 174 + mccabre analyze src/ --json > current.json 175 + 176 + # Compare (using jq) 177 + jq '.summary.total_logical_loc' baseline.json 178 + jq '.summary.total_logical_loc' current.json 179 + ``` 180 + 181 + ### Visualize Growth 182 + 183 + Integrate with your CI to track: 184 + 185 + - LOC growth per sprint 186 + - LOC per feature 187 + - Comment ratio trends 188 + 189 + ## See Also 190 + 191 + - [Cyclomatic Complexity](./cyclomatic-complexity.md) 192 + - [Clone Detection](./clone-detection.md) 193 + - [Configuration](./configuration.md)
+83
docs/src/quickstart.md
··· 1 + # Quick Start 2 + 3 + This guide will get you analyzing code in minutes. 4 + 5 + ## Basic Usage 6 + 7 + ### Analyze Everything 8 + 9 + Run a full analysis on a directory: 10 + 11 + ```bash 12 + mccabre analyze ./src 13 + ``` 14 + 15 + This will show: 16 + 17 + - Cyclomatic complexity per file 18 + - Lines of code metrics 19 + - Detected code clones 20 + 21 + ### Complexity Only 22 + 23 + To check only complexity metrics: 24 + 25 + ```bash 26 + mccabre complexity ./src 27 + ``` 28 + 29 + ### Clone Detection Only 30 + 31 + To find duplicated code: 32 + 33 + ```bash 34 + mccabre clones ./src 35 + ``` 36 + 37 + ## Understanding the Output 38 + 39 + ### Terminal Output 40 + 41 + Mccabre uses colors to highlight issues: 42 + 43 + - **Green**: Low complexity (1-10) 44 + - **Yellow**: Moderate complexity (11-20) 45 + - **Red**: High complexity (21+) 46 + 47 + Example output: 48 + 49 + ```text 50 + ================================================================================ 51 + MCCABRE CODE ANALYSIS REPORT 52 + ================================================================================ 53 + 54 + SUMMARY 55 + -------------------------------------------------------------------------------- 56 + Total files analyzed: 5 57 + Total physical LOC: 450 58 + Total logical LOC: 320 59 + Average complexity: 8.50 60 + Maximum complexity: 18 61 + High complexity files: 2 62 + Clone groups detected: 3 63 + 64 + FILE METRICS 65 + -------------------------------------------------------------------------------- 66 + FILE: src/utils.rs 67 + Cyclomatic Complexity: 5 (low) 68 + Physical LOC: 45 69 + Logical LOC: 32 70 + ``` 71 + 72 + ### JSON 73 + 74 + ```bash 75 + mccabre analyze ./src --json > report.json 76 + ``` 77 + 78 + ## Next Steps 79 + 80 + - Learn about [Cyclomatic Complexity](./cyclomatic-complexity.md) 81 + - Understand [Clone Detection](./clone-detection.md) 82 + - Configure [thresholds](./configuration.md) 83 + - See more [examples](./examples.md)
+48
examples/complex.js
··· 1 + // Example of high cyclomatic complexity 2 + function processUserData(user, options) { 3 + if (!user) { 4 + throw new Error("User required"); 5 + } 6 + 7 + if (user.age < 18 && !options.allowMinors) { 8 + return { error: "User too young" }; 9 + } 10 + 11 + if (user.country === "US" || user.country === "CA") { 12 + if (user.state && user.state.length === 2) { 13 + console.log("North American user"); 14 + } 15 + } else if (user.country === "UK" || user.country === "IE") { 16 + console.log("European user"); 17 + } 18 + 19 + const result = {}; 20 + 21 + if (options.includeEmail && user.email) { 22 + result.email = user.email.toLowerCase(); 23 + } 24 + 25 + if (options.includePhone && user.phone) { 26 + result.phone = user.phone.replace(/\D/g, ''); 27 + } 28 + 29 + if (user.premium || (user.credits && user.credits > 100)) { 30 + result.tier = "premium"; 31 + } else if (user.credits && user.credits > 10) { 32 + result.tier = "standard"; 33 + } else { 34 + result.tier = "basic"; 35 + } 36 + 37 + for (let i = 0; i < user.preferences.length; i++) { 38 + const pref = user.preferences[i]; 39 + if (pref.enabled && pref.value !== null) { 40 + result.preferences = result.preferences || []; 41 + result.preferences.push(pref); 42 + } 43 + } 44 + 45 + return result; 46 + } 47 + 48 + // Cyclomatic complexity: ~15-20
+97
examples/long.py
··· 1 + #!/usr/bin/env python3 2 + """ 3 + Example Python file with many lines of code 4 + Demonstrates LOC counting with comments and blank lines 5 + """ 6 + 7 + import sys 8 + import os 9 + from typing import List, Dict, Optional 10 + 11 + 12 + class DataProcessor: 13 + """Process data with various transformations""" 14 + 15 + def __init__(self, config: Dict): 16 + self.config = config 17 + self.data = [] 18 + self.results = {} 19 + 20 + def load_data(self, filepath: str) -> bool: 21 + """Load data from a file""" 22 + try: 23 + with open(filepath, 'r') as f: 24 + self.data = [line.strip() for line in f] 25 + return True 26 + except FileNotFoundError: 27 + print(f"File not found: {filepath}") 28 + return False 29 + 30 + def process(self) -> List[str]: 31 + """Process the loaded data""" 32 + results = [] 33 + 34 + for item in self.data: 35 + # Skip empty lines 36 + if not item: 37 + continue 38 + 39 + # Transform the item 40 + transformed = self._transform(item) 41 + 42 + # Validate 43 + if self._validate(transformed): 44 + results.append(transformed) 45 + 46 + return results 47 + 48 + def _transform(self, item: str) -> str: 49 + """Transform a single item""" 50 + # Convert to lowercase 51 + item = item.lower() 52 + 53 + # Remove special characters 54 + item = ''.join(c for c in item if c.isalnum() or c.isspace()) 55 + 56 + # Trim whitespace 57 + item = item.strip() 58 + 59 + return item 60 + 61 + def _validate(self, item: str) -> bool: 62 + """Validate an item""" 63 + if len(item) < 3: 64 + return False 65 + 66 + if not any(c.isalpha() for c in item): 67 + return False 68 + 69 + return True 70 + 71 + def save_results(self, output_path: str) -> None: 72 + """Save processed results""" 73 + with open(output_path, 'w') as f: 74 + for item in self.results: 75 + f.write(f"{item}\n") 76 + 77 + 78 + def main(): 79 + """Main entry point""" 80 + if len(sys.argv) < 2: 81 + print("Usage: python long.py <input_file>") 82 + sys.exit(1) 83 + 84 + processor = DataProcessor({"strict": True}) 85 + processor.load_data(sys.argv[1]) 86 + results = processor.process() 87 + 88 + print(f"Processed {len(results)} items") 89 + 90 + 91 + if __name__ == "__main__": 92 + main() 93 + 94 + # Physical LOC: ~95 95 + # Logical LOC: ~60 96 + # Comment lines: ~15 97 + # Blank lines: ~20
+85
examples/not_dry.go
··· 1 + package main 2 + 3 + import ( 4 + "fmt" 5 + "strings" 6 + ) 7 + 8 + // Example demonstrating code duplication (clones) 9 + // Multiple functions have similar logic 10 + 11 + func processUserInput(input string) string { 12 + // Trim and validate input 13 + trimmed := strings.TrimSpace(input) 14 + if len(trimmed) == 0 { 15 + return "" 16 + } 17 + 18 + // Convert to lowercase 19 + lower := strings.ToLower(trimmed) 20 + 21 + // Remove special characters 22 + cleaned := "" 23 + for _, char := range lower { 24 + if (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char == ' ' { 25 + cleaned += string(char) 26 + } 27 + } 28 + 29 + return cleaned 30 + } 31 + 32 + func processProductName(name string) string { 33 + // Trim and validate input 34 + trimmed := strings.TrimSpace(name) 35 + if len(trimmed) == 0 { 36 + return "" 37 + } 38 + 39 + // Convert to lowercase 40 + lower := strings.ToLower(trimmed) 41 + 42 + // Remove special characters 43 + cleaned := "" 44 + for _, char := range lower { 45 + if (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char == ' ' { 46 + cleaned += string(char) 47 + } 48 + } 49 + 50 + return cleaned 51 + } 52 + 53 + func sanitizeFileName(filename string) string { 54 + // Trim and validate input 55 + trimmed := strings.TrimSpace(filename) 56 + if len(trimmed) == 0 { 57 + return "" 58 + } 59 + 60 + // Convert to lowercase 61 + lower := strings.ToLower(trimmed) 62 + 63 + // Remove special characters 64 + cleaned := "" 65 + for _, char := range lower { 66 + if (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char == ' ' { 67 + cleaned += string(char) 68 + } 69 + } 70 + 71 + return cleaned 72 + } 73 + 74 + func main() { 75 + user := processUserInput(" Hello World! ") 76 + product := processProductName(" Test Product 123 ") 77 + file := sanitizeFileName(" my_file.txt ") 78 + 79 + fmt.Println(user) 80 + fmt.Println(product) 81 + fmt.Println(file) 82 + } 83 + 84 + // This file has obvious code duplication across the three functions 85 + // Clone detection should identify the repeated token sequences