0
# AST Parsing
1
2
AST parsing capabilities transform Scala source code into PMD-compatible abstract syntax trees using Scalameta parsers. The parsing system provides complete coverage of Scala language constructs through 140+ AST node types.
3
4
## Core Parsing Components
5
6
### ScalaParser
7
8
Main parser implementation that converts Scala source code into PMD-compatible AST structures.
9
10
```java { .api }
11
public final class ScalaParser implements Parser {
12
public ScalaParser();
13
public ASTSource parse(ParserTask task) throws ParseException;
14
}
15
```
16
17
**Usage Example**:
18
19
```java
20
// Create parser instance
21
ScalaParser parser = new ScalaParser();
22
23
// Parse source code from task
24
ParserTask task = ParserTask.forFile(new File("Example.scala"), charset, languageVersion);
25
ASTSource ast = parser.parse(task);
26
27
// Process the resulting AST
28
ast.getChildren().forEach(node -> {
29
System.out.println("Node type: " + node.getClass().getSimpleName());
30
});
31
```
32
33
### ASTSource
34
35
Root node representing a complete Scala source file, serving as the entry point for AST analysis.
36
37
```java { .api }
38
public final class ASTSource extends AbstractScalaNode<Source> implements RootNode {
39
public AstInfo<ASTSource> getAstInfo();
40
public void addTaskInfo(ParserTask task);
41
}
42
```
43
44
**Usage Example**:
45
46
```java
47
ASTSource sourceRoot = parser.parse(task);
48
49
// Get AST metadata
50
AstInfo<ASTSource> info = sourceRoot.getAstInfo();
51
52
// Access root node properties
53
TextRegion region = sourceRoot.getTextRegion();
54
String sourceText = sourceRoot.getText();
55
56
// Navigate to child nodes
57
sourceRoot.descendants(ASTDefnClass.class)
58
.forEach(classNode -> processClassDefinition(classNode));
59
```
60
61
### ScalaTreeBuilder
62
63
Internal component that converts Scalameta AST structures to PMD-compatible node hierarchies.
64
65
```java { .api }
66
final class ScalaTreeBuilder {
67
<T extends Tree> ScalaNode<T> build(T astNode);
68
}
69
```
70
71
**Internal Usage**:
72
73
```java
74
// Used internally by ScalaParser
75
Dialect dialect = ScalaDialect.dialectOf(languageVersion);
76
Source scalametaAst = new ScalametaParser(virtualFile, dialect).parseSource();
77
ASTSource pmdAst = (ASTSource) new ScalaTreeBuilder().build(scalametaAst);
78
```
79
80
### AbstractScalaNode
81
82
Base class for all Scala AST nodes, providing PMD integration and common node functionality.
83
84
```java { .api }
85
abstract class AbstractScalaNode<T extends Tree> extends AbstractNode<AbstractScalaNode<?>, ScalaNode<?>> implements ScalaNode<T> {
86
protected final T node;
87
88
public boolean isImplicit();
89
public T getNode();
90
public TextRegion getTextRegion();
91
public <R, P> R acceptVisitor(AstVisitor<? super P, ? extends R> visitor, P data);
92
}
93
```
94
95
**Key Methods**:
96
97
```java
98
// Check if node represents implicit Scala construct
99
boolean implicit = astNode.isImplicit();
100
101
// Get underlying Scalameta node
102
Tree scalametaNode = astNode.getNode();
103
104
// Access text positioning information
105
TextRegion region = astNode.getTextRegion();
106
```
107
108
### ScalaDialect
109
110
Internal utility that maps PMD language versions to appropriate Scalameta dialect configurations.
111
112
```java { .api }
113
final class ScalaDialect {
114
static scala.meta.Dialect dialectOf(LanguageVersion version);
115
}
116
```
117
118
**Internal Usage**:
119
120
```java
121
// Map PMD language version to Scalameta dialect
122
LanguageVersion version = task.getLanguageVersion();
123
Dialect dialect = ScalaDialect.dialectOf(version); // Returns appropriate scala.meta.Dialect
124
```
125
126
## AST Node Hierarchy
127
128
### Base Node Types
129
130
All Scala AST nodes extend from common base types providing core functionality.
131
132
```java { .api }
133
public abstract class AbstractScalaNode<T extends Tree> extends AbstractNode<AbstractScalaNode<?>, ScalaNode<?>> implements ScalaNode<T> {
134
public <P, R> R acceptVisitor(AstVisitor<? super P, ? extends R> visitor, P data);
135
public boolean isImplicit();
136
public TextRegion getTextRegion();
137
public int compareLocation(Node other);
138
public String getXPathNodeName();
139
}
140
141
public interface ScalaNode<T extends Tree> extends GenericNode<ScalaNode<?>> {
142
boolean isImplicit();
143
}
144
```
145
146
**Usage Example**:
147
148
```java
149
// Work with any Scala AST node
150
public void processNode(ScalaNode<?> node) {
151
// Check if node represents implicit construct
152
if (node.isImplicit()) {
153
System.out.println("Implicit node: " + node.getXPathNodeName());
154
}
155
156
// Get source location
157
TextRegion region = ((AbstractScalaNode<?>) node).getTextRegion();
158
System.out.println("Location: " + region.getStartPos() + "-" + region.getEndPos());
159
}
160
```
161
162
## Declaration and Definition Nodes
163
164
### Method and Function Definitions
165
166
```java { .api }
167
// Method declarations in traits/classes
168
public final class ASTDeclDef extends AbstractScalaNode<Decl.Def> implements DeclNode {
169
// Inherited node methods
170
}
171
172
// Method implementations
173
public final class ASTDefnDef extends AbstractScalaNode<Defn.Def> implements DefnNode {
174
// Inherited node methods
175
}
176
```
177
178
**Usage Example**:
179
180
```java
181
// Find all method definitions in source
182
ast.descendants(ASTDefnDef.class).forEach(method -> {
183
String methodName = method.getText(); // Get source text
184
System.out.println("Found method: " + methodName);
185
});
186
187
// Find abstract method declarations
188
ast.descendants(ASTDeclDef.class).forEach(decl -> {
189
System.out.println("Found method declaration: " + decl.getText());
190
});
191
```
192
193
### Class and Object Definitions
194
195
```java { .api }
196
// Class definitions
197
public final class ASTDefnClass extends AbstractScalaNode<Defn.Class> implements DefnNode {
198
// Inherited node methods
199
}
200
201
// Object definitions (singletons)
202
public final class ASTDefnObject extends AbstractScalaNode<Defn.Object> implements DefnNode {
203
// Inherited node methods
204
}
205
206
// Trait definitions
207
public final class ASTDefnTrait extends AbstractScalaNode<Defn.Trait> implements DefnNode {
208
// Inherited node methods
209
}
210
```
211
212
**Usage Example**:
213
214
```java
215
// Process all class definitions
216
ast.descendants(ASTDefnClass.class).forEach(classNode -> {
217
System.out.println("Class: " + classNode.getText());
218
219
// Find methods in this class
220
classNode.descendants(ASTDefnDef.class).forEach(method -> {
221
System.out.println(" Method: " + method.getText());
222
});
223
});
224
225
// Find all singleton objects
226
ast.descendants(ASTDefnObject.class).forEach(obj -> {
227
System.out.println("Object: " + obj.getText());
228
});
229
```
230
231
### Value and Variable Definitions
232
233
```java { .api }
234
// Immutable values (val)
235
public final class ASTDefnVal extends AbstractScalaNode<Defn.Val> implements DefnNode {
236
// Inherited node methods
237
}
238
239
// Mutable variables (var)
240
public final class ASTDefnVar extends AbstractScalaNode<Defn.Var> implements DefnNode {
241
// Inherited node methods
242
}
243
244
// Type definitions
245
public final class ASTDefnType extends AbstractScalaNode<Defn.Type> implements DefnNode {
246
// Inherited node methods
247
}
248
```
249
250
**Usage Example**:
251
252
```java
253
// Find all value definitions
254
ast.descendants(ASTDefnVal.class).forEach(value -> {
255
System.out.println("Val definition: " + value.getText());
256
});
257
258
// Find mutable variables
259
ast.descendants(ASTDefnVar.class).forEach(variable -> {
260
System.out.println("Var definition: " + variable.getText());
261
});
262
```
263
264
## Expression and Term Nodes
265
266
### Method Calls and Applications
267
268
```java { .api }
269
// Method applications (function calls)
270
public final class ASTTermApply extends AbstractScalaNode<Term.Apply> implements TermNode {
271
// Inherited node methods
272
}
273
274
// Infix method applications (a + b)
275
public final class ASTTermApplyInfix extends AbstractScalaNode<Term.ApplyInfix> implements TermNode {
276
// Inherited node methods
277
}
278
279
// Type applications (List[String])
280
public final class ASTTermApplyType extends AbstractScalaNode<Term.ApplyType> implements TermNode {
281
// Inherited node methods
282
}
283
```
284
285
**Usage Example**:
286
287
```java
288
// Find all method calls
289
ast.descendants(ASTTermApply.class).forEach(call -> {
290
System.out.println("Method call: " + call.getText());
291
});
292
293
// Find infix operations
294
ast.descendants(ASTTermApplyInfix.class).forEach(infix -> {
295
System.out.println("Infix operation: " + infix.getText());
296
});
297
```
298
299
### Control Flow Nodes
300
301
```java { .api }
302
// If expressions
303
public final class ASTTermIf extends AbstractScalaNode<Term.If> implements TermNode {
304
// Inherited node methods
305
}
306
307
// Match expressions (pattern matching)
308
public final class ASTTermMatch extends AbstractScalaNode<Term.Match> implements TermNode {
309
// Inherited node methods
310
}
311
312
// For comprehensions
313
public final class ASTTermFor extends AbstractScalaNode<Term.For> implements TermNode {
314
// Inherited node methods
315
}
316
317
// For-yield expressions
318
public final class ASTTermForYield extends AbstractScalaNode<Term.ForYield> implements TermNode {
319
// Inherited node methods
320
}
321
```
322
323
**Usage Example**:
324
325
```java
326
// Analyze control flow patterns
327
ast.descendants(ASTTermIf.class).forEach(ifExpr -> {
328
System.out.println("If expression: " + ifExpr.getText());
329
});
330
331
ast.descendants(ASTTermMatch.class).forEach(match -> {
332
System.out.println("Pattern match: " + match.getText());
333
334
// Find case clauses within match
335
match.descendants(ASTCase.class).forEach(caseClause -> {
336
System.out.println(" Case: " + caseClause.getText());
337
});
338
});
339
```
340
341
## Literal and Constant Nodes
342
343
### Primitive Literals
344
345
```java { .api }
346
// String literals
347
public final class ASTLitString extends AbstractScalaNode<Lit.String> implements LitNode {
348
// Inherited node methods
349
}
350
351
// Numeric literals
352
public final class ASTLitInt extends AbstractScalaNode<Lit.Int> implements LitNode {
353
// Inherited node methods
354
}
355
356
public final class ASTLitDouble extends AbstractScalaNode<Lit.Double> implements LitNode {
357
// Inherited node methods
358
}
359
360
// Boolean literals
361
public final class ASTLitBoolean extends AbstractScalaNode<Lit.Boolean> implements LitNode {
362
// Inherited node methods
363
}
364
365
// Null literal
366
public final class ASTLitNull extends AbstractScalaNode<Lit.Null> implements LitNode {
367
// Inherited node methods
368
}
369
```
370
371
**Usage Example**:
372
373
```java
374
// Find all string literals
375
ast.descendants(ASTLitString.class).forEach(str -> {
376
System.out.println("String literal: " + str.getText());
377
});
378
379
// Find numeric constants
380
ast.descendants(ASTLitInt.class).forEach(num -> {
381
System.out.println("Integer literal: " + num.getText());
382
});
383
```
384
385
## Package and Import Management
386
387
### Package Declarations
388
389
```java { .api }
390
// Package declarations
391
public final class ASTPkg extends AbstractScalaNode<Pkg> implements PkgNode {
392
// Inherited node methods
393
}
394
395
// Package objects
396
public final class ASTPkgObject extends AbstractScalaNode<Pkg.Object> implements PkgNode {
397
// Inherited node methods
398
}
399
```
400
401
### Import Statements
402
403
```java { .api }
404
// Import statements
405
public final class ASTImport extends AbstractScalaNode<Import> implements ImportNode {
406
// Inherited node methods
407
}
408
409
// Import expressions (what to import)
410
public final class ASTImporter extends AbstractScalaNode<Importer> implements ImportNode {
411
// Inherited node methods
412
}
413
```
414
415
**Usage Example**:
416
417
```java
418
// Analyze imports and packages
419
ast.descendants(ASTImport.class).forEach(importStmt -> {
420
System.out.println("Import: " + importStmt.getText());
421
422
// Analyze specific import expressions
423
importStmt.descendants(ASTImporter.class).forEach(importer -> {
424
System.out.println(" Importing: " + importer.getText());
425
});
426
});
427
428
// Find package declarations
429
ast.descendants(ASTPkg.class).forEach(pkg -> {
430
System.out.println("Package: " + pkg.getText());
431
});
432
```
433
434
## Parsing Configuration
435
436
### Scalameta Integration
437
438
The parser integrates with Scalameta's parsing infrastructure to support all Scala language constructs:
439
440
```java
441
// Internal parser configuration (handled automatically)
442
scala.meta.Dialect dialect = ScalaDialect.dialectOf(languageVersion);
443
scala.meta.parsers.Parsed<scala.meta.Source> parsed = dialect.parse(sourceText);
444
```
445
446
### Error Handling
447
448
```java { .api }
449
// Parse exceptions for syntax errors
450
public class ParseException extends Exception {
451
// Standard exception methods
452
}
453
```
454
455
**Usage Example**:
456
457
```java
458
try {
459
ASTSource ast = parser.parse(task);
460
// Process successful parse
461
} catch (ParseException e) {
462
System.err.println("Parse error: " + e.getMessage());
463
// Handle syntax errors in source code
464
}
465
```
466
467
## Integration with PMD Framework
468
469
### Parser Task Configuration
470
471
```java
472
// Configure parsing task
473
ParserTask.Builder builder = ParserTask.builder()
474
.file(sourceFile)
475
.charset(StandardCharsets.UTF_8)
476
.languageVersion(scalaVersion);
477
478
ParserTask task = builder.build();
479
ASTSource ast = parser.parse(task);
480
```
481
482
### AST Processing Pipeline
483
484
The parsed AST integrates with PMD's analysis pipeline:
485
486
1. **Parsing**: Source code → Scalameta AST → PMD AST
487
2. **Validation**: Node consistency and hierarchy validation
488
3. **Analysis**: Rule application and visitor pattern traversal
489
4. **Reporting**: Issue detection and report generation
490
491
This parsing system provides the foundation for all static analysis capabilities in the PMD Scala module.