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.
Main parser implementation that converts Scala source code into PMD-compatible AST structures.
public final class ScalaParser implements Parser {
public ScalaParser();
public ASTSource parse(ParserTask task) throws ParseException;
}Usage Example:
// Create parser instance
ScalaParser parser = new ScalaParser();
// Parse source code from task
ParserTask task = ParserTask.forFile(new File("Example.scala"), charset, languageVersion);
ASTSource ast = parser.parse(task);
// Process the resulting AST
ast.getChildren().forEach(node -> {
System.out.println("Node type: " + node.getClass().getSimpleName());
});Root node representing a complete Scala source file, serving as the entry point for AST analysis.
public final class ASTSource extends AbstractScalaNode<Source> implements RootNode {
public AstInfo<ASTSource> getAstInfo();
public void addTaskInfo(ParserTask task);
}Usage Example:
ASTSource sourceRoot = parser.parse(task);
// Get AST metadata
AstInfo<ASTSource> info = sourceRoot.getAstInfo();
// Access root node properties
TextRegion region = sourceRoot.getTextRegion();
String sourceText = sourceRoot.getText();
// Navigate to child nodes
sourceRoot.descendants(ASTDefnClass.class)
.forEach(classNode -> processClassDefinition(classNode));Internal component that converts Scalameta AST structures to PMD-compatible node hierarchies.
final class ScalaTreeBuilder {
<T extends Tree> ScalaNode<T> build(T astNode);
}Internal Usage:
// Used internally by ScalaParser
Dialect dialect = ScalaDialect.dialectOf(languageVersion);
Source scalametaAst = new ScalametaParser(virtualFile, dialect).parseSource();
ASTSource pmdAst = (ASTSource) new ScalaTreeBuilder().build(scalametaAst);Base class for all Scala AST nodes, providing PMD integration and common node functionality.
abstract class AbstractScalaNode<T extends Tree> extends AbstractNode<AbstractScalaNode<?>, ScalaNode<?>> implements ScalaNode<T> {
protected final T node;
public boolean isImplicit();
public T getNode();
public TextRegion getTextRegion();
public <R, P> R acceptVisitor(AstVisitor<? super P, ? extends R> visitor, P data);
}Key Methods:
// Check if node represents implicit Scala construct
boolean implicit = astNode.isImplicit();
// Get underlying Scalameta node
Tree scalametaNode = astNode.getNode();
// Access text positioning information
TextRegion region = astNode.getTextRegion();Internal utility that maps PMD language versions to appropriate Scalameta dialect configurations.
final class ScalaDialect {
static scala.meta.Dialect dialectOf(LanguageVersion version);
}Internal Usage:
// Map PMD language version to Scalameta dialect
LanguageVersion version = task.getLanguageVersion();
Dialect dialect = ScalaDialect.dialectOf(version); // Returns appropriate scala.meta.DialectAll Scala AST nodes extend from common base types providing core functionality.
public abstract class AbstractScalaNode<T extends Tree> extends AbstractNode<AbstractScalaNode<?>, ScalaNode<?>> implements ScalaNode<T> {
public <P, R> R acceptVisitor(AstVisitor<? super P, ? extends R> visitor, P data);
public boolean isImplicit();
public TextRegion getTextRegion();
public int compareLocation(Node other);
public String getXPathNodeName();
}
public interface ScalaNode<T extends Tree> extends GenericNode<ScalaNode<?>> {
boolean isImplicit();
}Usage Example:
// Work with any Scala AST node
public void processNode(ScalaNode<?> node) {
// Check if node represents implicit construct
if (node.isImplicit()) {
System.out.println("Implicit node: " + node.getXPathNodeName());
}
// Get source location
TextRegion region = ((AbstractScalaNode<?>) node).getTextRegion();
System.out.println("Location: " + region.getStartPos() + "-" + region.getEndPos());
}// Method declarations in traits/classes
public final class ASTDeclDef extends AbstractScalaNode<Decl.Def> implements DeclNode {
// Inherited node methods
}
// Method implementations
public final class ASTDefnDef extends AbstractScalaNode<Defn.Def> implements DefnNode {
// Inherited node methods
}Usage Example:
// Find all method definitions in source
ast.descendants(ASTDefnDef.class).forEach(method -> {
String methodName = method.getText(); // Get source text
System.out.println("Found method: " + methodName);
});
// Find abstract method declarations
ast.descendants(ASTDeclDef.class).forEach(decl -> {
System.out.println("Found method declaration: " + decl.getText());
});// Class definitions
public final class ASTDefnClass extends AbstractScalaNode<Defn.Class> implements DefnNode {
// Inherited node methods
}
// Object definitions (singletons)
public final class ASTDefnObject extends AbstractScalaNode<Defn.Object> implements DefnNode {
// Inherited node methods
}
// Trait definitions
public final class ASTDefnTrait extends AbstractScalaNode<Defn.Trait> implements DefnNode {
// Inherited node methods
}Usage Example:
// Process all class definitions
ast.descendants(ASTDefnClass.class).forEach(classNode -> {
System.out.println("Class: " + classNode.getText());
// Find methods in this class
classNode.descendants(ASTDefnDef.class).forEach(method -> {
System.out.println(" Method: " + method.getText());
});
});
// Find all singleton objects
ast.descendants(ASTDefnObject.class).forEach(obj -> {
System.out.println("Object: " + obj.getText());
});// Immutable values (val)
public final class ASTDefnVal extends AbstractScalaNode<Defn.Val> implements DefnNode {
// Inherited node methods
}
// Mutable variables (var)
public final class ASTDefnVar extends AbstractScalaNode<Defn.Var> implements DefnNode {
// Inherited node methods
}
// Type definitions
public final class ASTDefnType extends AbstractScalaNode<Defn.Type> implements DefnNode {
// Inherited node methods
}Usage Example:
// Find all value definitions
ast.descendants(ASTDefnVal.class).forEach(value -> {
System.out.println("Val definition: " + value.getText());
});
// Find mutable variables
ast.descendants(ASTDefnVar.class).forEach(variable -> {
System.out.println("Var definition: " + variable.getText());
});// Method applications (function calls)
public final class ASTTermApply extends AbstractScalaNode<Term.Apply> implements TermNode {
// Inherited node methods
}
// Infix method applications (a + b)
public final class ASTTermApplyInfix extends AbstractScalaNode<Term.ApplyInfix> implements TermNode {
// Inherited node methods
}
// Type applications (List[String])
public final class ASTTermApplyType extends AbstractScalaNode<Term.ApplyType> implements TermNode {
// Inherited node methods
}Usage Example:
// Find all method calls
ast.descendants(ASTTermApply.class).forEach(call -> {
System.out.println("Method call: " + call.getText());
});
// Find infix operations
ast.descendants(ASTTermApplyInfix.class).forEach(infix -> {
System.out.println("Infix operation: " + infix.getText());
});// If expressions
public final class ASTTermIf extends AbstractScalaNode<Term.If> implements TermNode {
// Inherited node methods
}
// Match expressions (pattern matching)
public final class ASTTermMatch extends AbstractScalaNode<Term.Match> implements TermNode {
// Inherited node methods
}
// For comprehensions
public final class ASTTermFor extends AbstractScalaNode<Term.For> implements TermNode {
// Inherited node methods
}
// For-yield expressions
public final class ASTTermForYield extends AbstractScalaNode<Term.ForYield> implements TermNode {
// Inherited node methods
}Usage Example:
// Analyze control flow patterns
ast.descendants(ASTTermIf.class).forEach(ifExpr -> {
System.out.println("If expression: " + ifExpr.getText());
});
ast.descendants(ASTTermMatch.class).forEach(match -> {
System.out.println("Pattern match: " + match.getText());
// Find case clauses within match
match.descendants(ASTCase.class).forEach(caseClause -> {
System.out.println(" Case: " + caseClause.getText());
});
});// String literals
public final class ASTLitString extends AbstractScalaNode<Lit.String> implements LitNode {
// Inherited node methods
}
// Numeric literals
public final class ASTLitInt extends AbstractScalaNode<Lit.Int> implements LitNode {
// Inherited node methods
}
public final class ASTLitDouble extends AbstractScalaNode<Lit.Double> implements LitNode {
// Inherited node methods
}
// Boolean literals
public final class ASTLitBoolean extends AbstractScalaNode<Lit.Boolean> implements LitNode {
// Inherited node methods
}
// Null literal
public final class ASTLitNull extends AbstractScalaNode<Lit.Null> implements LitNode {
// Inherited node methods
}Usage Example:
// Find all string literals
ast.descendants(ASTLitString.class).forEach(str -> {
System.out.println("String literal: " + str.getText());
});
// Find numeric constants
ast.descendants(ASTLitInt.class).forEach(num -> {
System.out.println("Integer literal: " + num.getText());
});// Package declarations
public final class ASTPkg extends AbstractScalaNode<Pkg> implements PkgNode {
// Inherited node methods
}
// Package objects
public final class ASTPkgObject extends AbstractScalaNode<Pkg.Object> implements PkgNode {
// Inherited node methods
}// Import statements
public final class ASTImport extends AbstractScalaNode<Import> implements ImportNode {
// Inherited node methods
}
// Import expressions (what to import)
public final class ASTImporter extends AbstractScalaNode<Importer> implements ImportNode {
// Inherited node methods
}Usage Example:
// Analyze imports and packages
ast.descendants(ASTImport.class).forEach(importStmt -> {
System.out.println("Import: " + importStmt.getText());
// Analyze specific import expressions
importStmt.descendants(ASTImporter.class).forEach(importer -> {
System.out.println(" Importing: " + importer.getText());
});
});
// Find package declarations
ast.descendants(ASTPkg.class).forEach(pkg -> {
System.out.println("Package: " + pkg.getText());
});The parser integrates with Scalameta's parsing infrastructure to support all Scala language constructs:
// Internal parser configuration (handled automatically)
scala.meta.Dialect dialect = ScalaDialect.dialectOf(languageVersion);
scala.meta.parsers.Parsed<scala.meta.Source> parsed = dialect.parse(sourceText);// Parse exceptions for syntax errors
public class ParseException extends Exception {
// Standard exception methods
}Usage Example:
try {
ASTSource ast = parser.parse(task);
// Process successful parse
} catch (ParseException e) {
System.err.println("Parse error: " + e.getMessage());
// Handle syntax errors in source code
}// Configure parsing task
ParserTask.Builder builder = ParserTask.builder()
.file(sourceFile)
.charset(StandardCharsets.UTF_8)
.languageVersion(scalaVersion);
ParserTask task = builder.build();
ASTSource ast = parser.parse(task);The parsed AST integrates with PMD's analysis pipeline:
This parsing system provides the foundation for all static analysis capabilities in the PMD Scala module.