|
| 1 | +--- |
| 2 | +llms-gk: 'devmate_elp_development_md' |
| 3 | +apply_to_regex: '^(.*\.rs|.*\.md)$' |
| 4 | +oncalls: ['vscode_erlang'] |
| 5 | +--- |
| 6 | +# ELP Development Rules for LLMs (OSS) |
| 7 | + |
| 8 | +## Project Overview |
| 9 | + |
| 10 | +ELP (Erlang Language Platform) is a language server and development tools suite |
| 11 | +for Erlang, built in Rust. This project provides IDE features, diagnostics, and |
| 12 | +code analysis for Erlang codebases. |
| 13 | + |
| 14 | +## Diagnostic Code Management |
| 15 | + |
| 16 | +### Adding New Diagnostic Codes |
| 17 | + |
| 18 | +When adding new diagnostic codes to `DiagnosticCode` enum: |
| 19 | + |
| 20 | +1. **Naming Convention**: Use descriptive PascalCase names that clearly indicate |
| 21 | + the issue |
| 22 | + - Good: `UnusedFunctionArg`, `MissingCompileWarnMissingSpec` |
| 23 | + - Bad: `Error1`, `BadCode` |
| 24 | + |
| 25 | +2. **Code Assignment**: Follow the established numbering scheme |
| 26 | + - `W0000-W9999`: Native ELP diagnostics, visible in the OSS version |
| 27 | + - Use the next available number in the appropriate range |
| 28 | + - Never change the number of an existing diagnostic code |
| 29 | + - Never change the label of an existing diagnostic code |
| 30 | + - Always add the new diagnostic constructor to the end of the list |
| 31 | + |
| 32 | +3. **Required Methods**: When adding a new variant, update ALL match statements: |
| 33 | + - `as_code()`: Return the diagnostic code (e.g., "W0053") |
| 34 | + - `as_label()`: Return snake_case label (e.g., "unused_function_arg") |
| 35 | + - `allows_fixme_comment()`: Determine if FIXME comments are allowed |
| 36 | + - `is_syntax_error()`: Mark if this is a syntax error |
| 37 | + |
| 38 | +4. **Documentation**: Add comments explaining complex diagnostic codes |
| 39 | + |
| 40 | +5. **Documentation File**: Create a corresponding documentation file in the |
| 41 | + website |
| 42 | + - Location: `website/docs/erlang-error-index/{namespace}/{code}.md` |
| 43 | + - Example: `W0051` → `website/docs/erlang-error-index/w/W0051.md` |
| 44 | + - Include frontmatter with `sidebar_position` matching the code number |
| 45 | + - Structure should include: |
| 46 | + - Title with code and brief description |
| 47 | + - Severity level (Error, Warning, WeakWarning, Information) |
| 48 | + - Code example showing the diagnostic in action |
| 49 | + - Explanation section describing the issue and why it matters |
| 50 | + - Optional: Fix suggestions or alternatives |
| 51 | + - The `as_uri()` method automatically generates URLs pointing to these docs |
| 52 | + |
| 53 | +### Creating DiagnosticDescriptor |
| 54 | + |
| 55 | +Every diagnostic must have a corresponding `DiagnosticDescriptor` that defines |
| 56 | +when and how the diagnostic runs: |
| 57 | + |
| 58 | +1. **Static Descriptor Declaration**: Create a public static descriptor in your |
| 59 | + diagnostic module |
| 60 | + - Use `pub(crate) static DESCRIPTOR: DiagnosticDescriptor` pattern |
| 61 | + - Define `DiagnosticConditions` with appropriate flags |
| 62 | + - Provide a checker function that implements the diagnostic logic |
| 63 | + |
| 64 | +2. **Diagnostic Conditions**: Configure when the diagnostic should run |
| 65 | + - `experimental`: Mark as true for experimental/unstable diagnostics |
| 66 | + - `include_generated`: Set to false if diagnostic shouldn't run on generated |
| 67 | + code |
| 68 | + - `include_tests`: Set to false if diagnostic shouldn't run on test files |
| 69 | + - `default_disabled`: Set to true if diagnostic requires explicit enabling |
| 70 | + |
| 71 | +3. **Checker Function**: Implement the diagnostic logic |
| 72 | + - Must match signature: `&dyn AdhocSemanticDiagnostics` |
| 73 | + - Push diagnostics to the `diags` vector using `Diagnostic::new()` |
| 74 | + - Use helper functions to keep the checker clean and focused |
| 75 | + |
| 76 | +4. **Registration**: Add the descriptor to `diagnostics_descriptors()` function |
| 77 | + in `diagnostics.rs` |
| 78 | + - Include your module's `DESCRIPTOR` in the returned vector |
| 79 | + |
| 80 | +5. **Module Structure**: Follow the established pattern |
| 81 | + - Create separate module files for each diagnostic type |
| 82 | + - Export the `DESCRIPTOR` as `pub(crate) static` |
| 83 | + - Include comprehensive tests with `#[cfg(test)]` |
| 84 | + - Use SSR patterns when appropriate for complex matching |
| 85 | + |
| 86 | +## Rust Code Style |
| 87 | + |
| 88 | +### Error Handling |
| 89 | + |
| 90 | +- Use `Result<T, E>` for fallible operations |
| 91 | +- Prefer `?` operator over explicit match for error propagation |
| 92 | +- Use descriptive error messages with context |
| 93 | + |
| 94 | +### Pattern Matching |
| 95 | + |
| 96 | +- Use exhaustive matches for enums to catch new variants at compile time |
| 97 | +- Add explicit comments when intentionally using catch-all patterns |
| 98 | +- Prefer early returns to reduce nesting |
| 99 | + |
| 100 | +### String Handling |
| 101 | + |
| 102 | +- Use `&str` for borrowed strings, `String` for owned |
| 103 | +- Use `format!()` for complex string formatting |
| 104 | +- Use `to_string()` for simple conversions |
| 105 | + |
| 106 | +### Collections |
| 107 | + |
| 108 | +- Use `FxHashMap` instead of `std::HashMap` for better performance |
| 109 | +- Use `lazy_static!` for expensive static computations |
| 110 | +- Prefer iterators over manual loops where possible |
| 111 | + |
| 112 | +## Testing Guidelines |
| 113 | + |
| 114 | +### Test Structure |
| 115 | + |
| 116 | +- Use `expect_test` for snapshot testing of complex outputs |
| 117 | +- Group related tests in the same module |
| 118 | +- Use descriptive test names that explain the scenario |
| 119 | + |
| 120 | +### Declarative Test Fixtures |
| 121 | + |
| 122 | +ELP uses a declarative test fixture system that allows you to write tests with |
| 123 | +inline annotations and markers directly in test strings. This system is defined |
| 124 | +in `crates/project_model/src/test_fixture.rs`. |
| 125 | + |
| 126 | +#### Key Features |
| 127 | + |
| 128 | +1. **File Organization**: Use `//- /path/to/file.erl` to define multiple files |
| 129 | + in a single test |
| 130 | +2. **Metadata Markers**: Specify app names, include paths, OTP apps, etc. using |
| 131 | + metadata after the path |
| 132 | +3. **Annotations**: Mark expected diagnostics or ranges using `%% ^^^` syntax |
| 133 | +4. **Cursors and Ranges**: Use `~` markers to indicate positions or ranges in |
| 134 | + test code |
| 135 | + |
| 136 | +#### Annotation Syntax |
| 137 | + |
| 138 | +Annotations allow you to mark expected diagnostics, types, or other information |
| 139 | +directly in test code: |
| 140 | + |
| 141 | +- **Basic annotation**: `%% ^^^ some text` - Points to the range above matching |
| 142 | + the caret length |
| 143 | +- **Top-of-file marker**: `%% <<< text` (at file start) - Creates annotation at |
| 144 | + position 0..0 |
| 145 | +- **File-wide annotation**: `%% ^^^file text` - Annotation spans the entire file |
| 146 | + contents |
| 147 | +- **Left-margin annotation**: `%%<^^^ text` - Annotation starts at `%%` position |
| 148 | + instead of first `^` |
| 149 | +- **Multiline annotations**: Use continuation lines with `%% | next line` |
| 150 | + |
| 151 | +#### Example Test Fixture |
| 152 | + |
| 153 | +```rust |
| 154 | +let fixture = r#" |
| 155 | +//- /src/main.erl |
| 156 | +-module(main). |
| 157 | +
|
| 158 | +foo(X) -> |
| 159 | + X + undefined. |
| 160 | + %% ^^^^^^^^^ error: type mismatch |
| 161 | +"#; |
| 162 | +``` |
| 163 | + |
| 164 | +### Test Data |
| 165 | + |
| 166 | +- Create minimal test cases that focus on specific functionality |
| 167 | +- Use realistic Erlang code examples in tests |
| 168 | +- Test both positive and negative cases |
| 169 | + |
| 170 | +### Running Tests for Specific Crates |
| 171 | + |
| 172 | +When running tests for a specific crate, you need to specify the crate name, not |
| 173 | +the directory name. The mapping is: |
| 174 | + |
| 175 | +| Crate Name | Directory Name | |
| 176 | +| -------------------- | ----------------------- | |
| 177 | +| `elp` | `crates/elp` | |
| 178 | +| `elp_base_db` | `crates/base_db` | |
| 179 | +| `elp_eqwalizer` | `crates/eqwalizer` | |
| 180 | +| `elp_erlang_service` | `crates/erlang_service` | |
| 181 | +| `elp_ide` | `crates/ide` | |
| 182 | +| `elp_ide_assists` | `crates/ide_assists` | |
| 183 | +| `elp_ide_completion` | `crates/ide_completion` | |
| 184 | +| `elp_ide_db` | `crates/ide_db` | |
| 185 | +| `elp_ide_ssr` | `crates/ide_ssr` | |
| 186 | +| `elp_log` | `crates/elp_log` | |
| 187 | +| `elp_project_model` | `crates/project_model` | |
| 188 | +| `elp_syntax` | `crates/syntax` | |
| 189 | +| `elp_text_edit` | `crates/text_edit` | |
| 190 | +| `elp_types_db` | `crates/types_db` | |
| 191 | +| `hir` | `crates/hir` | |
| 192 | + |
| 193 | +Example: To run tests for the `elp_ide` crate: |
| 194 | + |
| 195 | +```bash |
| 196 | +cargo test -p elp_ide |
| 197 | +``` |
| 198 | + |
| 199 | +Or to run tests in a specific directory: |
| 200 | + |
| 201 | +```bash |
| 202 | +cargo test --manifest-path crates/ide/Cargo.toml |
| 203 | +``` |
| 204 | + |
| 205 | +### Existing tests |
| 206 | + |
| 207 | +- Do not change existing tests without asking |
| 208 | + |
| 209 | +## Documentation |
| 210 | + |
| 211 | +### Code Comments |
| 212 | + |
| 213 | +- Document complex algorithms and business logic |
| 214 | +- Explain WHY, not just WHAT the code does |
| 215 | +- Use `///` for public API documentation |
| 216 | +- Use `//` for internal implementation notes |
| 217 | + |
| 218 | +### Error Messages |
| 219 | + |
| 220 | +- Make error messages actionable and user-friendly |
| 221 | +- Include context about what was expected vs. what was found |
| 222 | +- Provide suggestions for fixing the issue when possible |
| 223 | + |
| 224 | +## Performance Considerations |
| 225 | + |
| 226 | +### Memory Usage |
| 227 | + |
| 228 | +- Use `Box<T>` for large enum variants to keep enum size small |
| 229 | +- Consider using `Cow<str>` for strings that might be borrowed or owned |
| 230 | +- Use `Arc<T>` for shared immutable data |
| 231 | + |
| 232 | +### Computation |
| 233 | + |
| 234 | +- Cache expensive computations using `lazy_static!` or `once_cell` |
| 235 | +- Use appropriate data structures (HashMap for lookups, Vec for sequences) |
| 236 | +- Profile code paths that handle large Erlang codebases |
| 237 | + |
| 238 | +## Integration Guidelines |
| 239 | + |
| 240 | +### Erlang Service Integration |
| 241 | + |
| 242 | +- Handle Erlang service errors gracefully |
| 243 | +- Use appropriate namespaces for different error sources |
| 244 | +- Maintain backward compatibility with existing error codes |
| 245 | + |
| 246 | +### IDE Integration |
| 247 | + |
| 248 | +- Provide rich diagnostic information (ranges, severity, fixes) |
| 249 | +- Support quick fixes and code actions where appropriate |
| 250 | +- Ensure diagnostics are fast enough for real-time feedback |
| 251 | + |
| 252 | +## Maintenance |
| 253 | + |
| 254 | +### Backward Compatibility |
| 255 | + |
| 256 | +- Don't change existing diagnostic codes or their meanings |
| 257 | +- Deprecate old codes before removing them |
| 258 | +- Maintain serialization compatibility for configuration files |
| 259 | + |
| 260 | +### Code Organization |
| 261 | + |
| 262 | +- Keep related functionality together in modules |
| 263 | +- Use clear module boundaries and public APIs |
| 264 | +- Minimize dependencies between modules |
| 265 | + |
| 266 | +### Version Management |
| 267 | + |
| 268 | +- Follow semantic versioning for public APIs |
| 269 | +- Document breaking changes in release notes |
| 270 | +- Provide migration guides for major changes |
| 271 | + |
| 272 | +## Common Patterns |
| 273 | + |
| 274 | +### Regex Usage |
| 275 | + |
| 276 | +- Use `lazy_static!` for compiled regexes |
| 277 | +- Prefer specific patterns over overly broad ones |
| 278 | +- Test regex patterns thoroughly with edge cases |
| 279 | + |
| 280 | +### Configuration |
| 281 | + |
| 282 | +- Support both code-based and label-based diagnostic references |
| 283 | +- Use serde for serialization/deserialization |
| 284 | +- Provide sensible defaults for all configuration options |
| 285 | + |
| 286 | +### Error Recovery |
| 287 | + |
| 288 | +- Continue processing after encountering errors when possible |
| 289 | +- Collect multiple errors rather than failing on the first one |
| 290 | +- Provide partial results when full analysis isn't possible |
| 291 | + |
| 292 | +### Process |
| 293 | + |
| 294 | +- Always run tests before finishing |
| 295 | +- Always run `cargo clippy --tests` before submitting PRs |
| 296 | +- Use `cargo fmt` for code formatting |
0 commit comments