Skip to content

Commit 428d8b0

Browse files
authored
Add Claude derived instructions for tests and recipes (#700)
1 parent e775b10 commit 428d8b0

File tree

1 file changed

+75
-7
lines changed

1 file changed

+75
-7
lines changed

CLAUDE.md

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,21 @@ void testName() {
9393

9494
## Test Development Guidelines
9595

96-
### 1. Remove Overlapping Test Cases
96+
### 1. Test Structure
97+
- Implement `RewriteTest` interface
98+
- Override `defaults(RecipeSpec spec)` to set the recipe being tested
99+
- Use `@DocumentExample` on the primary test case that best demonstrates the recipe
100+
- Add `@Issue("https://github.com/...")` annotations for tests related to specific issues
101+
102+
### 2. Test Naming
103+
- Use descriptive test method names that explain the scenario being tested
104+
- Prefix negative tests with `doNotChange` or `unchanged`
105+
- Group related test scenarios (e.g., `removeRedundantNullCheck`, `removeRedundantNullCheckWithMethodInvocation`)
106+
107+
### 3. Test Case Design
97108
- Avoid test cases that essentially test the same functionality
98109
- Each test should cover a distinct scenario or edge case
99110
- Consolidate similar tests when they don't add unique value
100-
101-
### 2. Use Method Parameters Instead of Local Variables
102111
- In test text blocks, prefer method parameters over local variable declarations
103112
- This makes tests more concise and focuses on the transformation being tested
104113
- Example:
@@ -119,11 +128,70 @@ void testName() {
119128
}
120129
```
121130

122-
### 3. Suppress Warnings Appropriately
123-
- Add `@SuppressWarnings` annotations to test classes to suppress expected warnings
124-
- Common suppressions for this type of recipe:
131+
### 4. Suppress Warnings Appropriately
132+
- Add `@SuppressWarnings` at the class level for expected warnings in test code:
125133
- `"ConstantConditions"` - for redundant null checks that the recipe will remove
126-
- Other suppressions as needed based on the specific warnings in test cases
134+
- `"ConditionCoveredByFurtherCondition"` - for redundant conditions
135+
- `"RedundantCast"` - for unnecessary casts
136+
- `"unused"` - for unused variables/methods
137+
138+
### 5. Language Comments for IDE Support
139+
- Add `//language=java` before `java()` calls when there's no customization
140+
- Place comments on individual strings when there's customization or multiple `java()` calls
141+
- Don't add language comments to JavaTemplate strings with parameters
142+
143+
### 6. Test Coverage Guidelines
144+
- Test basic functionality with simple cases
145+
- Include edge cases (method invocations, field access, complex expressions)
146+
- Test cases where the recipe should NOT make changes
147+
- Test partial transformations in complex conditions
148+
- Test different operator positions and combinations
149+
- Include tests for language-specific behavior (e.g., Kotlin's `!is` operator)
150+
151+
## Recipe Implementation Conventions
152+
153+
### 1. Recipe Structure
154+
- Use `@EqualsAndHashCode(callSuper = false)` and `@Value` from Lombok for immutable recipe classes
155+
- Implement `getEstimatedEffortPerOccurrence()` - use the same time estimate from SonarQube when implementing RSPEC rules
156+
- Always include RSPEC tags in `getTags()` when implementing SonarQube rules (e.g., `"RSPEC-S1697"`)
157+
- Use `Preconditions.check()` to exclude specific file types (e.g., Kotlin files) when a recipe is language-specific
158+
159+
### 2. Visitor Choice
160+
- Use `JavaIsoVisitor<ExecutionContext>` when returning the same type of LST element being visited
161+
- Use `JavaVisitor<ExecutionContext>` when the transformation changes the tree structure or returns a different type
162+
- For multi-language support, implement separate visitors for each language (JavaVisitor, KotlinVisitor, etc.)
163+
164+
### 3. Expression Comparison
165+
- Always use `SemanticallyEqual.areEqual()` for comparing expressions instead of string matching
166+
- Use `J.Literal.isLiteralValue()` for checking literal values
167+
- Use `TypeUtils` helper methods (e.g., `TypeUtils.asArray()`, `TypeUtils.asFullyQualified()`) instead of instanceof checks with JavaType
168+
169+
### 4. Method Matching
170+
- Use `MethodMatcher` for reliable method matching instead of manual name/type checks
171+
- Example: `private static final MethodMatcher TO_ARRAY = new MethodMatcher("java.util.Collection toArray()", true);`
172+
173+
### 5. Refaster Templates
174+
- Simple transformations can use Refaster-style templates with `@BeforeTemplate` and `@AfterTemplate` annotations
175+
- Use `@RecipeDescriptor` annotation for metadata on Refaster template classes
176+
177+
### 6. JavaTemplate Best Practices
178+
- Always declare imports when templates introduce types: `.imports(fqn)`
179+
- Call `maybeAddImport()` after applying templates that use new types
180+
- Use `service(ImportService.class).shortenFullyQualifiedTypeReferencesIn()` to clean up imports
181+
- Don't add `//language=java` comments to JavaTemplate strings containing parameters like `#{any()}`
182+
183+
## YAML Configuration
184+
185+
### Recipe Collections
186+
Recipes are organized into YAML files in `src/main/resources/META-INF/rewrite/`:
187+
- `common-static-analysis.yml` - Common static analysis fixes that apply across languages
188+
- `java-best-practices.yml` - Java-specific best practices and idioms
189+
- `static-analysis.yml` - General static analysis recipes
190+
191+
### Adding Recipes to Collections
192+
- Add new recipes to appropriate YAML files based on their scope
193+
- Comment out recipes that are experimental or have known issues (with a comment explaining why)
194+
- Group related recipes together in the YAML lists
127195

128196
## Important Notes
129197

0 commit comments

Comments
 (0)