Skip to content

Commit 2aacf4e

Browse files
alpslaclaude
andcommitted
feat(kb): Add script to create missing fix patterns
Creates patterns for 5 rules that only had text guidance: - FileTabCharacterCheck: Replace tabs with 4 spaces - HideUtilityClassConstructorCheck: Add private constructor - RightCurlyCheck: Same-line brace style - MissingJavadocMethodCheck: Javadoc template - CollapsibleIfStatements: Combine nested ifs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent dfb833f commit 2aacf4e

1 file changed

Lines changed: 326 additions & 0 deletions

File tree

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
#!/usr/bin/env npx ts-node
2+
/**
3+
* Add Missing Patterns for Rules Without Code Fixes
4+
*
5+
* These rules were identified as having text guidance only:
6+
* 1. FileTabCharacterCheck - ✅ Fixable (replace tabs with spaces)
7+
* 2. HideUtilityClassConstructorCheck - ✅ Fixable (add private constructor)
8+
* 3. CollapsibleIfStatements - ⚠️ Partial (needs AST)
9+
* 4. RightCurlyCheck - ⚠️ Partial (style dependent)
10+
* 5. MissingJavadocMethodCheck - ⚠️ Partial (template only)
11+
* 6. JavadocMethodCheck - ⚠️ Partial (template only)
12+
* 7. DesignForExtensionCheck - ❌ Not auto-fixable (architectural)
13+
*/
14+
15+
import * as dotenv from 'dotenv';
16+
import * as path from 'path';
17+
18+
dotenv.config({ path: path.join(__dirname, '../.env') });
19+
20+
import { createClient } from '@supabase/supabase-js';
21+
import { v4 as uuidv4 } from 'uuid';
22+
23+
const supabaseUrl = process.env.SUPABASE_URL;
24+
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
25+
26+
if (!supabaseUrl || !supabaseKey) {
27+
console.error('Missing SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY');
28+
process.exit(1);
29+
}
30+
31+
const supabase = createClient(supabaseUrl, supabaseKey);
32+
33+
interface PatternToAdd {
34+
rule_id: string;
35+
tool: string;
36+
name: string;
37+
description: string;
38+
transformation_type: string;
39+
file_types: string[];
40+
detection: object;
41+
fix_template: {
42+
template: string;
43+
variables: string[];
44+
};
45+
examples: Array<{
46+
before: string;
47+
after: string;
48+
explanation: string;
49+
}>;
50+
confidence: number;
51+
safe_for_auto_apply: boolean;
52+
tags: string[];
53+
}
54+
55+
const patternsToAdd: PatternToAdd[] = [
56+
// 1. FileTabCharacterCheck - Replace tabs with spaces
57+
{
58+
rule_id: 'FileTabCharacterCheck',
59+
tool: 'checkstyle',
60+
name: 'Replace Tab Characters with Spaces',
61+
description: 'Converts tab characters to spaces for consistent formatting. Uses 4 spaces per tab.',
62+
transformation_type: 'string_replacement',
63+
file_types: ['java'],
64+
detection: {
65+
pattern: '\\t',
66+
message_contains: ['tab', 'character']
67+
},
68+
fix_template: {
69+
template: '{{LINE_CONTENT:replace_tabs_with_spaces:4}}',
70+
variables: ['LINE_CONTENT', 'TAB_SIZE']
71+
},
72+
examples: [
73+
{
74+
before: '\tpublic void method() {',
75+
after: ' public void method() {',
76+
explanation: 'Replace tab with 4 spaces for consistent indentation'
77+
},
78+
{
79+
before: '\t\tint x = 1;',
80+
after: ' int x = 1;',
81+
explanation: 'Replace 2 tabs with 8 spaces'
82+
}
83+
],
84+
confidence: 95,
85+
safe_for_auto_apply: true,
86+
tags: ['formatting', 'whitespace', 'style']
87+
},
88+
89+
// 2. HideUtilityClassConstructorCheck - Add private constructor
90+
{
91+
rule_id: 'HideUtilityClassConstructorCheck',
92+
tool: 'checkstyle',
93+
name: 'Add Private Constructor to Utility Class',
94+
description: 'Adds a private constructor to prevent instantiation of utility classes.',
95+
transformation_type: 'code_insertion',
96+
file_types: ['java'],
97+
detection: {
98+
pattern: 'class\\s+\\w+Utils?',
99+
message_contains: ['utility', 'constructor', 'private']
100+
},
101+
fix_template: {
102+
template: `
103+
/**
104+
* Private constructor to prevent instantiation.
105+
*/
106+
private {{CLASS_NAME}}() {
107+
throw new UnsupportedOperationException("Utility class should not be instantiated");
108+
}`,
109+
variables: ['CLASS_NAME']
110+
},
111+
examples: [
112+
{
113+
before: `public final class StringUtils {
114+
public static boolean isEmpty(String s) {
115+
return s == null || s.isEmpty();
116+
}
117+
}`,
118+
after: `public final class StringUtils {
119+
/**
120+
* Private constructor to prevent instantiation.
121+
*/
122+
private StringUtils() {
123+
throw new UnsupportedOperationException("Utility class should not be instantiated");
124+
}
125+
126+
public static boolean isEmpty(String s) {
127+
return s == null || s.isEmpty();
128+
}
129+
}`,
130+
explanation: 'Add private constructor after class declaration'
131+
}
132+
],
133+
confidence: 90,
134+
safe_for_auto_apply: true,
135+
tags: ['design', 'utility-class', 'constructor']
136+
},
137+
138+
// 3. RightCurlyCheck - Fix curly brace placement
139+
{
140+
rule_id: 'RightCurlyCheck',
141+
tool: 'checkstyle',
142+
name: 'Fix Right Curly Brace Placement',
143+
description: 'Ensures right curly braces are on the same line as the next statement (same-line style).',
144+
transformation_type: 'line_adjustment',
145+
file_types: ['java'],
146+
detection: {
147+
pattern: '\\}\\s*$',
148+
message_contains: ['right curly', 'same line', 'brace']
149+
},
150+
fix_template: {
151+
template: '} {{NEXT_KEYWORD}}',
152+
variables: ['NEXT_KEYWORD']
153+
},
154+
examples: [
155+
{
156+
before: `if (condition) {
157+
doSomething();
158+
}
159+
else {
160+
doOther();
161+
}`,
162+
after: `if (condition) {
163+
doSomething();
164+
} else {
165+
doOther();
166+
}`,
167+
explanation: 'Move else to same line as closing brace'
168+
},
169+
{
170+
before: `try {
171+
riskyCode();
172+
}
173+
catch (Exception e) {
174+
handle(e);
175+
}`,
176+
after: `try {
177+
riskyCode();
178+
} catch (Exception e) {
179+
handle(e);
180+
}`,
181+
explanation: 'Move catch to same line as closing brace'
182+
}
183+
],
184+
confidence: 85,
185+
safe_for_auto_apply: true,
186+
tags: ['formatting', 'braces', 'style']
187+
},
188+
189+
// 4. MissingJavadocMethodCheck - Add Javadoc template
190+
{
191+
rule_id: 'MissingJavadocMethodCheck',
192+
tool: 'checkstyle',
193+
name: 'Add Javadoc to Public Method',
194+
description: 'Adds Javadoc documentation template to public methods.',
195+
transformation_type: 'code_insertion',
196+
file_types: ['java'],
197+
detection: {
198+
pattern: 'public\\s+\\w+\\s+\\w+\\s*\\(',
199+
message_contains: ['javadoc', 'missing', 'method']
200+
},
201+
fix_template: {
202+
template: `/**
203+
* {{METHOD_DESCRIPTION}}
204+
*
205+
{{PARAMS}}
206+
* @return {{RETURN_TYPE}} {{RETURN_DESCRIPTION}}
207+
*/`,
208+
variables: ['METHOD_DESCRIPTION', 'PARAMS', 'RETURN_TYPE', 'RETURN_DESCRIPTION']
209+
},
210+
examples: [
211+
{
212+
before: ` public String getName() {
213+
return this.name;
214+
}`,
215+
after: ` /**
216+
* Gets the name.
217+
*
218+
* @return String the name
219+
*/
220+
public String getName() {
221+
return this.name;
222+
}`,
223+
explanation: 'Add Javadoc with @return for getter method'
224+
},
225+
{
226+
before: ` public void setName(String name) {
227+
this.name = name;
228+
}`,
229+
after: ` /**
230+
* Sets the name.
231+
*
232+
* @param name the name to set
233+
*/
234+
public void setName(String name) {
235+
this.name = name;
236+
}`,
237+
explanation: 'Add Javadoc with @param for setter method'
238+
}
239+
],
240+
confidence: 75,
241+
safe_for_auto_apply: false, // Needs human review for accurate descriptions
242+
tags: ['documentation', 'javadoc', 'methods']
243+
},
244+
245+
// 5. CollapsibleIfStatements - Combine nested ifs
246+
{
247+
rule_id: 'CollapsibleIfStatements',
248+
tool: 'pmd',
249+
name: 'Combine Nested If Statements',
250+
description: 'Combines nested if statements with no else clause into a single if with AND condition.',
251+
transformation_type: 'code_restructure',
252+
file_types: ['java'],
253+
detection: {
254+
pattern: 'if\\s*\\([^)]+\\)\\s*\\{\\s*if',
255+
message_contains: ['collapsible', 'nested', 'if']
256+
},
257+
fix_template: {
258+
template: 'if ({{CONDITION_1}} && {{CONDITION_2}}) {\n{{BODY}}\n}',
259+
variables: ['CONDITION_1', 'CONDITION_2', 'BODY']
260+
},
261+
examples: [
262+
{
263+
before: `if (a != null) {
264+
if (a.isValid()) {
265+
process(a);
266+
}
267+
}`,
268+
after: `if (a != null && a.isValid()) {
269+
process(a);
270+
}`,
271+
explanation: 'Combine nested conditions with && operator'
272+
}
273+
],
274+
confidence: 80,
275+
safe_for_auto_apply: false, // AST understanding needed
276+
tags: ['refactoring', 'code-complexity', 'if-statements']
277+
}
278+
];
279+
280+
async function addPatterns(): Promise<void> {
281+
console.log('=== Adding Missing Fix Patterns ===\n');
282+
283+
for (const pattern of patternsToAdd) {
284+
console.log(`Adding pattern for: ${pattern.rule_id}`);
285+
286+
// Check if pattern already exists
287+
const { data: existing } = await supabase
288+
.from('fix_patterns')
289+
.select('id')
290+
.eq('rule_id', pattern.rule_id)
291+
.eq('tool', pattern.tool)
292+
.single();
293+
294+
if (existing) {
295+
console.log(` ⏭️ Pattern already exists (id: ${existing.id.substring(0, 8)})`);
296+
continue;
297+
}
298+
299+
// Insert new pattern
300+
const { data, error } = await supabase
301+
.from('fix_patterns')
302+
.insert({
303+
id: uuidv4(),
304+
...pattern,
305+
status: 'active',
306+
created_by: 'manual-script',
307+
source: 'manual',
308+
verified: true,
309+
apply_count: 0,
310+
success_count: 0,
311+
revert_count: 0
312+
})
313+
.select()
314+
.single();
315+
316+
if (error) {
317+
console.error(` ❌ Failed: ${error.message}`);
318+
} else {
319+
console.log(` ✅ Added (id: ${data.id.substring(0, 8)})`);
320+
}
321+
}
322+
323+
console.log('\n=== Done ===');
324+
}
325+
326+
addPatterns().catch(console.error);

0 commit comments

Comments
 (0)