or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-processing.mdcopy-paste-detection.mdcore-analysis.mdindex.mdlanguage-framework.mdproperties-system.mdrendering-system.mdreporting-system.mdrule-system.mdutilities.md

ast-processing.mddocs/

0

# AST Processing

1

2

The AST (Abstract Syntax Tree) Processing module provides comprehensive navigation, querying, and analysis capabilities for parsed source code structures. It offers tree traversal, node searching, XPath querying, and streaming operations for efficient AST manipulation.

3

4

## Capabilities

5

6

### Node Interface

7

8

Core interface for Abstract Syntax Tree nodes providing navigation, querying, and content access capabilities.

9

10

```java { .api }

11

/**

12

* Core interface for Abstract Syntax Tree nodes.

13

* Provides navigation, querying, and content access for parsed source code.

14

*/

15

public interface Node {

16

17

/**

18

* Get XPath node name for XPath queries

19

* @return Node name used in XPath expressions

20

*/

21

String getXPathNodeName();

22

23

/**

24

* Get parent node in the AST

25

* @return Parent Node, or null if this is the root

26

*/

27

Node getParent();

28

29

/**

30

* Get child node by index

31

* @param index Zero-based index of child

32

* @return Child Node at specified index

33

* @throws IndexOutOfBoundsException if index is invalid

34

*/

35

Node getChild(int index);

36

37

/**

38

* Get number of direct children

39

* @return Count of immediate child nodes

40

*/

41

int getNumChildren();

42

43

/**

44

* Get all direct children as list

45

* @return Unmodifiable list of child nodes

46

*/

47

List<? extends Node> getChildren();

48

49

/**

50

* Get index of this node within its parent's children

51

* @return Zero-based index in parent, or -1 if no parent

52

*/

53

int getIndexInParent();

54

55

/**

56

* Get first child node

57

* @return First child, or null if no children

58

*/

59

Node getFirstChild();

60

61

/**

62

* Get last child node

63

* @return Last child, or null if no children

64

*/

65

Node getLastChild();

66

67

/**

68

* Get next sibling node

69

* @return Next sibling, or null if this is the last child

70

*/

71

Node getNextSibling();

72

73

/**

74

* Get previous sibling node

75

* @return Previous sibling, or null if this is the first child

76

*/

77

Node getPreviousSibling();

78

79

/**

80

* Check if any descendant matches the given node type

81

* @param type Class of node type to search for

82

* @return true if at least one descendant of the type exists

83

*/

84

boolean hasDescendantOfType(Class<? extends Node> type);

85

86

/**

87

* Find all descendants of specific type

88

* @param type Class of node type to find

89

* @return List of all descendant nodes matching the type

90

*/

91

List<? extends Node> findDescendantsOfType(Class<? extends Node> type);

92

93

/**

94

* Find first descendant of specific type

95

* @param type Class of node type to find

96

* @return First descendant matching type, or null if none found

97

*/

98

Node getFirstDescendantOfType(Class<? extends Node> type);

99

100

/**

101

* Stream all descendants of this node

102

* @return NodeStream for efficient descendant traversal

103

*/

104

NodeStream<? extends Node> descendants();

105

106

/**

107

* Stream direct children of this node

108

* @return NodeStream for child iteration

109

*/

110

NodeStream<? extends Node> children();

111

112

/**

113

* Get text region in source file

114

* @return TextRegion with start/end positions

115

*/

116

TextRegion getTextRegion();

117

118

/**

119

* Get file location for reporting purposes

120

* @return FileLocation with file and position information

121

*/

122

FileLocation getReportLocation();

123

124

/**

125

* Get source text content of this node

126

* @return Original source code text for this node

127

*/

128

String getText();

129

}

130

```

131

132

**Usage Examples:**

133

134

```java

135

import net.sourceforge.pmd.lang.ast.Node;

136

import net.sourceforge.pmd.lang.ast.NodeStream;

137

import java.util.List;

138

139

// Basic node navigation

140

public void analyzeNode(Node node) {

141

System.out.printf("Node: %s (Children: %d)%n",

142

node.getXPathNodeName(),

143

node.getNumChildren());

144

145

// Navigate to parent

146

Node parent = node.getParent();

147

if (parent != null) {

148

System.out.printf("Parent: %s%n", parent.getXPathNodeName());

149

}

150

151

// Iterate through children

152

for (int i = 0; i < node.getNumChildren(); i++) {

153

Node child = node.getChild(i);

154

System.out.printf("Child %d: %s%n", i, child.getXPathNodeName());

155

}

156

157

// Or use the children list

158

List<? extends Node> children = node.getChildren();

159

for (Node child : children) {

160

analyzeNode(child); // Recursive analysis

161

}

162

163

// Navigate siblings

164

Node nextSibling = node.getNextSibling();

165

Node prevSibling = node.getPreviousSibling();

166

167

// Get source information

168

TextRegion region = node.getTextRegion();

169

System.out.printf("Text region: %d-%d%n",

170

region.getStartOffset(),

171

region.getEndOffset());

172

173

String sourceText = node.getText();

174

System.out.printf("Source text: %s%n", sourceText.trim());

175

}

176

177

// Finding specific node types (example with hypothetical Java nodes)

178

public void findMethodNodes(Node root) {

179

// Check if any method declarations exist

180

if (root.hasDescendantOfType(ASTMethodDeclaration.class)) {

181

System.out.println("Found method declarations");

182

183

// Find all method declarations

184

List<? extends Node> methods = root.findDescendantsOfType(ASTMethodDeclaration.class);

185

System.out.printf("Found %d methods%n", methods.size());

186

187

for (Node method : methods) {

188

System.out.printf("Method: %s%n", method.getText());

189

}

190

191

// Find first method only

192

Node firstMethod = root.getFirstDescendantOfType(ASTMethodDeclaration.class);

193

if (firstMethod != null) {

194

System.out.printf("First method: %s%n", firstMethod.getText());

195

}

196

}

197

}

198

199

// Using NodeStream for efficient traversal

200

public void streamExample(Node root) {

201

// Stream all descendants

202

root.descendants()

203

.filterIs(ASTVariableDeclarator.class)

204

.forEach(var -> System.out.printf("Variable: %s%n", var.getText()));

205

206

// Stream direct children only

207

root.children()

208

.filter(child -> child.getNumChildren() > 0)

209

.map(Node::getXPathNodeName)

210

.distinct()

211

.forEach(System.out::println);

212

213

// Count specific node types

214

long methodCount = root.descendants()

215

.filterIs(ASTMethodDeclaration.class)

216

.count();

217

218

// Find nodes with specific characteristics

219

List<Node> complexNodes = root.descendants()

220

.filter(node -> node.getNumChildren() > 5)

221

.toList();

222

}

223

```

224

225

### Node Stream Operations

226

227

Efficient streaming API for traversing and filtering AST nodes with functional programming patterns.

228

229

```java { .api }

230

/**

231

* Streaming API for efficient AST traversal and filtering.

232

* Provides functional programming patterns for node processing.

233

*/

234

interface NodeStream<T extends Node> {

235

236

/**

237

* Filter nodes by predicate

238

* @param predicate Function to test each node

239

* @return NodeStream containing only matching nodes

240

*/

241

NodeStream<T> filter(Predicate<? super T> predicate);

242

243

/**

244

* Filter nodes by specific type

245

* @param nodeType Class of target node type

246

* @return NodeStream containing only nodes of specified type

247

*/

248

<R extends Node> NodeStream<R> filterIs(Class<? extends R> nodeType);

249

250

/**

251

* Transform nodes to different type

252

* @param mapper Function to transform each node

253

* @return Stream containing transformed values

254

*/

255

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

256

257

/**

258

* Transform nodes to NodeStream of different type

259

* @param mapper Function to transform each node to NodeStream

260

* @return Flattened NodeStream of transformed nodes

261

*/

262

<R extends Node> NodeStream<R> flatMap(Function<? super T, NodeStream<? extends R>> mapper);

263

264

/**

265

* Get descendants of each node in stream

266

* @return NodeStream containing all descendants

267

*/

268

NodeStream<Node> descendants();

269

270

/**

271

* Get children of each node in stream

272

* @return NodeStream containing all children

273

*/

274

NodeStream<Node> children();

275

276

/**

277

* Execute action for each node

278

* @param action Action to perform on each node

279

*/

280

void forEach(Consumer<? super T> action);

281

282

/**

283

* Collect nodes to list

284

* @return List containing all nodes in stream

285

*/

286

List<T> toList();

287

288

/**

289

* Count nodes in stream

290

* @return Number of nodes in stream

291

*/

292

long count();

293

294

/**

295

* Get first node in stream

296

* @return First node, or empty Optional if stream is empty

297

*/

298

Optional<T> first();

299

300

/**

301

* Get last node in stream

302

* @return Last node, or empty Optional if stream is empty

303

*/

304

Optional<T> last();

305

306

/**

307

* Check if any nodes match predicate

308

* @param predicate Function to test nodes

309

* @return true if at least one node matches

310

*/

311

boolean any(Predicate<? super T> predicate);

312

313

/**

314

* Check if no nodes match predicate

315

* @param predicate Function to test nodes

316

* @return true if no nodes match

317

*/

318

boolean none(Predicate<? super T> predicate);

319

320

/**

321

* Get nodes with distinct values based on key function

322

* @param keyExtractor Function to extract comparison key

323

* @return NodeStream with distinct nodes

324

*/

325

NodeStream<T> distinct(Function<? super T, ?> keyExtractor);

326

327

/**

328

* Limit stream to first N nodes

329

* @param maxSize Maximum number of nodes to include

330

* @return NodeStream limited to specified size

331

*/

332

NodeStream<T> take(long maxSize);

333

334

/**

335

* Skip first N nodes

336

* @param n Number of nodes to skip

337

* @return NodeStream with first N nodes skipped

338

*/

339

NodeStream<T> drop(long n);

340

}

341

```

342

343

**Usage Examples:**

344

345

```java

346

import net.sourceforge.pmd.lang.ast.*;

347

import java.util.List;

348

import java.util.Optional;

349

350

// Complex filtering and transformation examples

351

public class ASTAnalysisExamples {

352

353

public void analyzeComplexity(Node root) {

354

// Find all method declarations with high complexity

355

List<Node> complexMethods = root.descendants()

356

.filterIs(ASTMethodDeclaration.class)

357

.filter(method -> method.descendants()

358

.filterIs(ASTIfStatement.class)

359

.count() > 3)

360

.toList();

361

362

System.out.printf("Found %d complex methods%n", complexMethods.size());

363

}

364

365

public void findUnusedVariables(Node root) {

366

// Find variable declarations

367

NodeStream<ASTVariableDeclarator> variables = root.descendants()

368

.filterIs(ASTVariableDeclarator.class);

369

370

// Check for unused variables (simplified example)

371

variables.filter(var -> {

372

String varName = var.getName();

373

return root.descendants()

374

.filterIs(ASTName.class)

375

.none(name -> name.getImage().equals(varName));

376

}).forEach(unusedVar ->

377

System.out.printf("Unused variable: %s%n", unusedVar.getName())

378

);

379

}

380

381

public void collectStatistics(Node root) {

382

// Count different node types

383

long classCount = root.descendants()

384

.filterIs(ASTClassOrInterfaceDeclaration.class)

385

.count();

386

387

long methodCount = root.descendants()

388

.filterIs(ASTMethodDeclaration.class)

389

.count();

390

391

long lineCount = root.descendants()

392

.map(node -> node.getTextRegion().getEndLine() -

393

node.getTextRegion().getStartLine() + 1)

394

.mapToLong(Integer::longValue)

395

.sum();

396

397

System.out.printf("Classes: %d, Methods: %d, Lines: %d%n",

398

classCount, methodCount, lineCount);

399

}

400

401

public void findDesignPatterns(Node root) {

402

// Find potential singleton pattern usage

403

root.descendants()

404

.filterIs(ASTMethodDeclaration.class)

405

.filter(method -> method.getName().equals("getInstance"))

406

.filter(method -> method.isStatic())

407

.forEach(method -> System.out.printf("Potential singleton: %s%n",

408

method.getParent().getFirstChildOfType(ASTClassOrInterfaceDeclaration.class).getName()));

409

410

// Find builder pattern usage

411

root.descendants()

412

.filterIs(ASTMethodDeclaration.class)

413

.filter(method -> method.getName().startsWith("set") || method.getName().startsWith("with"))

414

.filter(method -> method.getReturnType().equals(method.getDeclaringClass().getName()))

415

.distinct(method -> method.getDeclaringClass())

416

.forEach(builder -> System.out.printf("Potential builder: %s%n",

417

builder.getDeclaringClass().getName()));

418

}

419

420

public void securityAnalysis(Node root) {

421

// Find potential SQL injection points

422

root.descendants()

423

.filterIs(ASTMethodCallExpression.class)

424

.filter(call -> call.getMethodName().contains("executeQuery") ||

425

call.getMethodName().contains("execute"))

426

.filter(call -> call.getArguments().stream()

427

.anyMatch(arg -> arg.hasDescendantOfType(ASTStringLiteral.class)))

428

.forEach(call -> System.out.printf("Potential SQL injection: %s%n",

429

call.getTextRegion()));

430

431

// Find hardcoded passwords/keys

432

root.descendants()

433

.filterIs(ASTStringLiteral.class)

434

.map(Node::getText)

435

.filter(text -> text.toLowerCase().contains("password") ||

436

text.toLowerCase().contains("key") ||

437

text.toLowerCase().contains("secret"))

438

.forEach(suspicious -> System.out.printf("Suspicious string: %s%n", suspicious));

439

}

440

}

441

```

442

443

### AST Visitor Pattern

444

445

Visitor pattern implementation for traversing and processing AST nodes with type-safe method dispatch.

446

447

```java { .api }

448

/**

449

* Visitor pattern for AST traversal with type-safe method dispatch.

450

* Provides structured way to process different node types.

451

*/

452

interface ASTVisitor<T, P> {

453

454

/**

455

* Visit any node (default implementation)

456

* @param node Node to visit

457

* @param data Additional data for processing

458

* @return Result of visiting the node

459

*/

460

T visit(Node node, P data);

461

462

/**

463

* Visit child nodes of given node

464

* @param node Parent node whose children should be visited

465

* @param data Additional data for processing

466

* @return Result of visiting children

467

*/

468

T visitChildren(Node node, P data);

469

}

470

471

/**

472

* Base visitor class with default traversal behavior

473

*/

474

abstract class ASTVisitorBase<T, P> implements ASTVisitor<T, P> {

475

476

@Override

477

public T visit(Node node, P data) {

478

return node.childrenAccept(this, data);

479

}

480

481

@Override

482

public T visitChildren(Node node, P data) {

483

T result = null;

484

for (Node child : node.getChildren()) {

485

T childResult = child.jjtAccept(this, data);

486

if (childResult != null) {

487

result = childResult;

488

}

489

}

490

return result;

491

}

492

}

493

```

494

495

**Usage Examples:**

496

497

```java

498

import net.sourceforge.pmd.lang.ast.*;

499

500

// Example visitor for collecting method information

501

public class MethodCollectorVisitor extends ASTVisitorBase<Void, List<String>> {

502

503

public Void visit(ASTMethodDeclaration node, List<String> methods) {

504

// Collect method information

505

String methodInfo = String.format("%s (line %d)",

506

node.getName(),

507

node.getTextRegion().getStartLine());

508

methods.add(methodInfo);

509

510

// Continue visiting children

511

return visitChildren(node, methods);

512

}

513

514

public Void visit(ASTClassOrInterfaceDeclaration node, List<String> methods) {

515

System.out.printf("Analyzing class: %s%n", node.getName());

516

return visitChildren(node, methods);

517

}

518

}

519

520

// Usage example

521

public void collectMethods(Node root) {

522

List<String> methods = new ArrayList<>();

523

MethodCollectorVisitor visitor = new MethodCollectorVisitor();

524

root.jjtAccept(visitor, methods);

525

526

System.out.printf("Found %d methods:%n", methods.size());

527

methods.forEach(System.out::println);

528

}

529

530

// Visitor for complexity calculation

531

public class ComplexityVisitor extends ASTVisitorBase<Integer, Void> {

532

533

public Integer visit(ASTMethodDeclaration node, Void data) {

534

int complexity = 1; // Base complexity

535

536

// Add complexity for control flow nodes

537

complexity += node.descendants()

538

.filterIs(ASTIfStatement.class)

539

.count();

540

541

complexity += node.descendants()

542

.filterIs(ASTWhileStatement.class)

543

.count();

544

545

complexity += node.descendants()

546

.filterIs(ASTForStatement.class)

547

.count();

548

549

complexity += node.descendants()

550

.filterIs(ASTSwitchStatement.class)

551

.children()

552

.filterIs(ASTSwitchLabel.class)

553

.count();

554

555

System.out.printf("Method %s complexity: %d%n", node.getName(), complexity);

556

return complexity;

557

}

558

}

559

```

560

561

## Types

562

563

```java { .api }

564

/**

565

* Text region representing position in source file

566

*/

567

interface TextRegion {

568

569

/**

570

* Get start offset in characters

571

* @return Zero-based start position

572

*/

573

int getStartOffset();

574

575

/**

576

* Get end offset in characters

577

* @return Zero-based end position (exclusive)

578

*/

579

int getEndOffset();

580

581

/**

582

* Get start line number

583

* @return One-based start line

584

*/

585

int getStartLine();

586

587

/**

588

* Get end line number

589

* @return One-based end line

590

*/

591

int getEndLine();

592

593

/**

594

* Get start column number

595

* @return One-based start column

596

*/

597

int getStartColumn();

598

599

/**

600

* Get end column number

601

* @return One-based end column

602

*/

603

int getEndColumn();

604

605

/**

606

* Get length in characters

607

* @return Number of characters in region

608

*/

609

int getLength();

610

611

/**

612

* Check if region contains given offset

613

* @param offset Character offset to check

614

* @return true if offset is within region

615

*/

616

boolean contains(int offset);

617

}

618

619

/**

620

* File location for reporting and error messages

621

*/

622

interface FileLocation {

623

624

/**

625

* Get file identifier

626

* @return FileId for the source file

627

*/

628

FileId getFileId();

629

630

/**

631

* Get text region within file

632

* @return TextRegion with position information

633

*/

634

TextRegion getTextRegion();

635

636

/**

637

* Get display string for location

638

* @return Human-readable location description

639

*/

640

String getDisplayName();

641

}

642

643

/**

644

* Text document interface for source content

645

*/

646

interface TextDocument {

647

648

/**

649

* Get complete document text

650

* @return Full source text content

651

*/

652

String getText();

653

654

/**

655

* Get text for specific region

656

* @param region TextRegion to extract

657

* @return Text content for the region

658

*/

659

String getText(TextRegion region);

660

661

/**

662

* Get line content by number

663

* @param lineNumber One-based line number

664

* @return Text content of the line

665

*/

666

String getLine(int lineNumber);

667

668

/**

669

* Get total number of lines

670

* @return Line count in document

671

*/

672

int getLineCount();

673

674

/**

675

* Get file identifier

676

* @return FileId for this document

677

*/

678

FileId getFileId();

679

}

680

681

/**

682

* XPath query capabilities for AST nodes

683

*/

684

interface XPathCapable {

685

686

/**

687

* Execute XPath query on node

688

* @param xpath XPath expression to evaluate

689

* @return List of matching nodes

690

*/

691

List<Node> findChildNodesWithXPath(String xpath);

692

693

/**

694

* Check if XPath query matches any nodes

695

* @param xpath XPath expression to test

696

* @return true if query matches at least one node

697

*/

698

boolean hasDescendantMatchingXPath(String xpath);

699

}

700

701

/**

702

* Abstract base node class providing common functionality

703

*/

704

abstract class AbstractNode implements Node {

705

706

/**

707

* Accept visitor with double dispatch

708

* @param visitor ASTVisitor to accept

709

* @param data Additional data for visitor

710

* @return Result from visitor

711

*/

712

public abstract <T, P> T jjtAccept(ASTVisitor<T, P> visitor, P data);

713

714

/**

715

* Have children accept visitor

716

* @param visitor ASTVisitor for children

717

* @param data Additional data for visitor

718

* @return Result from visiting children

719

*/

720

public <T, P> T childrenAccept(ASTVisitor<T, P> visitor, P data) {

721

T result = null;

722

for (Node child : getChildren()) {

723

T childResult = child.jjtAccept(visitor, data);

724

if (childResult != null) {

725

result = childResult;

726

}

727

}

728

return result;

729

}

730

}

731

```