or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-nodes.mdcpd.mdindex.mdlanguage-parsing.mdrule-development.mdvisitor-pattern.md

visitor-pattern.mddocs/

0

# Visitor Pattern

1

2

Type-safe visitor pattern implementation for traversing Scala ASTs, enabling rule development and custom analysis through comprehensive visit methods for all node types. The visitor pattern provides the primary mechanism for analyzing Scala code in PMD.

3

4

## Capabilities

5

6

### Visitor Interface

7

8

The main visitor interface defining methods for visiting every type of Scala AST node.

9

10

```java { .api }

11

/**

12

* A Visitor Pattern Interface for the Scala AST

13

* @param <D> The type of the data input to each visit method

14

* @param <R> the type of the returned data from each visit method

15

*/

16

public interface ScalaParserVisitor<D, R> {

17

/**

18

* Visit an arbitrary Scala Node (any node in the tree)

19

* @param node the node of the tree

20

* @param data context-specific data

21

* @return context-specific data

22

*/

23

R visit(ScalaNode<?> node, D data);

24

25

/**

26

* Visit the Source Node (the root node of the tree)

27

* @param node the root node of the tree

28

* @param data context-specific data

29

* @return context-specific data

30

*/

31

R visit(ASTSource node, D data);

32

33

// Case and Constructor nodes

34

R visit(ASTCase node, D data);

35

R visit(ASTCtorPrimary node, D data);

36

R visit(ASTCtorSecondary node, D data);

37

38

// Declaration nodes

39

R visit(ASTDeclDef node, D data);

40

R visit(ASTDeclType node, D data);

41

R visit(ASTDeclVal node, D data);

42

R visit(ASTDeclVar node, D data);

43

44

// Definition nodes

45

R visit(ASTDefnClass node, D data);

46

R visit(ASTDefnDef node, D data);

47

R visit(ASTDefnMacro node, D data);

48

R visit(ASTDefnObject node, D data);

49

R visit(ASTDefnTrait node, D data);

50

R visit(ASTDefnType node, D data);

51

R visit(ASTDefnVal node, D data);

52

R visit(ASTDefnVar node, D data);

53

54

// Term expression nodes

55

R visit(ASTTermApply node, D data);

56

R visit(ASTTermApplyInfix node, D data);

57

R visit(ASTTermApplyType node, D data);

58

R visit(ASTTermApplyUnary node, D data);

59

R visit(ASTTermAssign node, D data);

60

R visit(ASTTermBlock node, D data);

61

R visit(ASTTermFor node, D data);

62

R visit(ASTTermForYield node, D data);

63

R visit(ASTTermFunction node, D data);

64

R visit(ASTTermIf node, D data);

65

R visit(ASTTermMatch node, D data);

66

R visit(ASTTermName node, D data);

67

R visit(ASTTermNew node, D data);

68

R visit(ASTTermNewAnonymous node, D data);

69

R visit(ASTTermSelect node, D data);

70

R visit(ASTTermThis node, D data);

71

R visit(ASTTermTry node, D data);

72

R visit(ASTTermWhile node, D data);

73

74

// Type system nodes

75

R visit(ASTTypeApply node, D data);

76

R visit(ASTTypeFunction node, D data);

77

R visit(ASTTypeName node, D data);

78

R visit(ASTTypeParam node, D data);

79

R visit(ASTTypeTuple node, D data);

80

R visit(ASTTypeWith node, D data);

81

82

// Pattern matching nodes

83

R visit(ASTPatAlternative node, D data);

84

R visit(ASTPatBind node, D data);

85

R visit(ASTPatExtract node, D data);

86

R visit(ASTPatTuple node, D data);

87

R visit(ASTPatVar node, D data);

88

R visit(ASTPatWildcard node, D data);

89

90

// Literal nodes

91

R visit(ASTLitBoolean node, D data);

92

R visit(ASTLitInt node, D data);

93

R visit(ASTLitLong node, D data);

94

R visit(ASTLitDouble node, D data);

95

R visit(ASTLitFloat node, D data);

96

R visit(ASTLitString node, D data);

97

R visit(ASTLitChar node, D data);

98

R visit(ASTLitNull node, D data);

99

R visit(ASTLitUnit node, D data);

100

101

// Modifier nodes

102

R visit(ASTModAbstract node, D data);

103

R visit(ASTModCase node, D data);

104

R visit(ASTModFinal node, D data);

105

R visit(ASTModImplicit node, D data);

106

R visit(ASTModLazy node, D data);

107

R visit(ASTModOverride node, D data);

108

R visit(ASTModPrivate node, D data);

109

R visit(ASTModProtected node, D data);

110

R visit(ASTModSealed node, D data);

111

112

// Import system nodes

113

R visit(ASTImport node, D data);

114

R visit(ASTImporter node, D data);

115

R visit(ASTImporteeName node, D data);

116

R visit(ASTImporteeRename node, D data);

117

R visit(ASTImporteeWildcard node, D data);

118

R visit(ASTImporteeUnimport node, D data);

119

120

// And many more visit methods for all other AST node types...

121

}

122

```

123

124

### Visitor Adapter

125

126

Default implementation providing convenient base for custom visitors.

127

128

```java { .api }

129

/**

130

* Default implementation of ScalaParserVisitor providing no-op visit methods

131

* Extend this class and override specific visit methods as needed

132

*/

133

public class ScalaParserVisitorAdapter implements ScalaParserVisitor<Object, Object> {

134

/**

135

* Default visit method that can be overridden for generic node handling

136

* @param node any Scala AST node

137

* @param data context data

138

* @return context data (default implementation returns data unchanged)

139

*/

140

@Override

141

public Object visit(ScalaNode<?> node, Object data) {

142

// Default: visit all children

143

for (ScalaNode<?> child : node.children()) {

144

child.accept(this, data);

145

}

146

return data;

147

}

148

149

/**

150

* Visit root source node - delegates to generic visit method by default

151

* @param node the source root node

152

* @param data context data

153

* @return result of generic visit method

154

*/

155

@Override

156

public Object visit(ASTSource node, Object data) {

157

return visit((ScalaNode<?>) node, data);

158

}

159

160

// All other visit methods delegate to the generic visit method by default

161

// Override specific methods to customize behavior for particular node types

162

163

@Override

164

public Object visit(ASTDefnClass node, Object data) {

165

return visit((ScalaNode<?>) node, data);

166

}

167

168

@Override

169

public Object visit(ASTDefnDef node, Object data) {

170

return visit((ScalaNode<?>) node, data);

171

}

172

173

@Override

174

public Object visit(ASTTermApply node, Object data) {

175

return visit((ScalaNode<?>) node, data);

176

}

177

178

// ... similar delegating implementations for all other node types

179

}

180

```

181

182

**Usage Examples:**

183

184

```java

185

// Create custom visitor by extending adapter

186

public class ScalaMethodVisitor extends ScalaParserVisitorAdapter {

187

private List<String> methodNames = new ArrayList<>();

188

189

@Override

190

public Object visit(ASTDefnDef node, Object data) {

191

// Custom logic for method definitions

192

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

193

194

// Continue traversing children

195

return super.visit(node, data);

196

}

197

198

@Override

199

public Object visit(ASTTermName node, Object data) {

200

// Extract method names

201

String name = extractName(node);

202

methodNames.add(name);

203

return super.visit(node, data);

204

}

205

206

public List<String> getMethodNames() {

207

return methodNames;

208

}

209

}

210

211

// Use the visitor

212

ASTSource ast = parser.parse("Example.scala", sourceReader);

213

ScalaMethodVisitor visitor = new ScalaMethodVisitor();

214

ast.accept(visitor, null);

215

List<String> methods = visitor.getMethodNames();

216

```

217

218

### Generic Type-Safe Visitor

219

220

Template for creating strongly typed visitors with custom data and return types.

221

222

```java { .api }

223

/**

224

* Example of a type-safe visitor with custom data and return types

225

*/

226

public class CustomAnalysisVisitor implements ScalaParserVisitor<AnalysisContext, AnalysisResult> {

227

@Override

228

public AnalysisResult visit(ScalaNode<?> node, AnalysisContext data) {

229

// Generic node processing

230

AnalysisResult result = new AnalysisResult();

231

232

// Visit all children and combine results

233

for (ScalaNode<?> child : node.children()) {

234

AnalysisResult childResult = child.accept(this, data);

235

result.merge(childResult);

236

}

237

238

return result;

239

}

240

241

@Override

242

public AnalysisResult visit(ASTSource node, AnalysisContext data) {

243

// Root node specific logic

244

data.setSourceFile(node);

245

return visit((ScalaNode<?>) node, data);

246

}

247

248

@Override

249

public AnalysisResult visit(ASTDefnClass node, AnalysisContext data) {

250

// Class-specific analysis

251

AnalysisResult result = new AnalysisResult();

252

result.addClass(extractClassName(node));

253

254

// Analyze class body

255

AnalysisResult bodyResult = visit((ScalaNode<?>) node, data);

256

result.merge(bodyResult);

257

258

return result;

259

}

260

261

@Override

262

public AnalysisResult visit(ASTDefnDef node, AnalysisContext data) {

263

// Method-specific analysis

264

AnalysisResult result = new AnalysisResult();

265

result.addMethod(extractMethodInfo(node));

266

267

return result;

268

}

269

270

// Implement other visit methods as needed...

271

}

272

273

// Supporting classes

274

class AnalysisContext {

275

private ASTSource sourceFile;

276

private String currentClass;

277

278

public void setSourceFile(ASTSource source) { this.sourceFile = source; }

279

public ASTSource getSourceFile() { return sourceFile; }

280

// ... other context methods

281

}

282

283

class AnalysisResult {

284

private List<String> classes = new ArrayList<>();

285

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

286

287

public void addClass(String className) { classes.add(className); }

288

public void addMethod(String methodName) { methods.add(methodName); }

289

public void merge(AnalysisResult other) {

290

classes.addAll(other.classes);

291

methods.addAll(other.methods);

292

}

293

// ... other result methods

294

}

295

```

296

297

### Visitor Usage Patterns

298

299

Common patterns for using visitors effectively in Scala AST analysis.

300

301

**Pattern 1: Collecting Information**

302

303

```java

304

public class InfoCollector extends ScalaParserVisitorAdapter {

305

private final Map<String, Integer> nodeCounts = new HashMap<>();

306

307

@Override

308

public Object visit(ScalaNode<?> node, Object data) {

309

// Count node types

310

String nodeType = node.getClass().getSimpleName();

311

nodeCounts.merge(nodeType, 1, Integer::sum);

312

313

return super.visit(node, data);

314

}

315

316

public Map<String, Integer> getNodeCounts() {

317

return nodeCounts;

318

}

319

}

320

```

321

322

**Pattern 2: Conditional Processing**

323

324

```java

325

public class ConditionalVisitor extends ScalaParserVisitorAdapter {

326

private final Predicate<ScalaNode<?>> condition;

327

private final Consumer<ScalaNode<?>> processor;

328

329

public ConditionalVisitor(Predicate<ScalaNode<?>> condition, Consumer<ScalaNode<?>> processor) {

330

this.condition = condition;

331

this.processor = processor;

332

}

333

334

@Override

335

public Object visit(ScalaNode<?> node, Object data) {

336

if (condition.test(node)) {

337

processor.accept(node);

338

}

339

return super.visit(node, data);

340

}

341

}

342

343

// Usage

344

ConditionalVisitor visitor = new ConditionalVisitor(

345

node -> node instanceof ASTDefnDef,

346

node -> System.out.println("Processing method: " + node)

347

);

348

```

349

350

**Pattern 3: Early Termination**

351

352

```java

353

public class SearchVisitor extends ScalaParserVisitorAdapter {

354

private final Predicate<ScalaNode<?>> searchCondition;

355

private ScalaNode<?> foundNode;

356

357

public SearchVisitor(Predicate<ScalaNode<?>> searchCondition) {

358

this.searchCondition = searchCondition;

359

}

360

361

@Override

362

public Object visit(ScalaNode<?> node, Object data) {

363

if (foundNode != null) {

364

return data; // Early termination - already found

365

}

366

367

if (searchCondition.test(node)) {

368

foundNode = node;

369

return data; // Found - stop searching

370

}

371

372

return super.visit(node, data);

373

}

374

375

public Optional<ScalaNode<?>> getFoundNode() {

376

return Optional.ofNullable(foundNode);

377

}

378

}

379

```

380

381

**Pattern 4: Context-Aware Traversal**

382

383

```java

384

public class ContextAwareVisitor extends ScalaParserVisitorAdapter {

385

private final Stack<ScalaNode<?>> context = new Stack<>();

386

387

@Override

388

public Object visit(ScalaNode<?> node, Object data) {

389

context.push(node);

390

try {

391

// Process with full context available

392

processWithContext(node, context);

393

return super.visit(node, data);

394

} finally {

395

context.pop();

396

}

397

}

398

399

private void processWithContext(ScalaNode<?> current, Stack<ScalaNode<?>> context) {

400

// Access parent nodes via context stack

401

if (current instanceof ASTTermName && !context.isEmpty()) {

402

ScalaNode<?> parent = context.get(context.size() - 2); // Skip current

403

if (parent instanceof ASTDefnDef) {

404

System.out.println("Term name inside method definition");

405

}

406

}

407

}

408

}

409

```

410

411

### Integration with PMD Rules

412

413

The visitor pattern integrates directly with PMD's rule system through the ScalaRule base class:

414

415

```java

416

// ScalaRule automatically implements ScalaParserVisitor

417

public class CustomScalaRule extends ScalaRule {

418

@Override

419

public Object visit(ASTDefnDef node, Object data) {

420

RuleContext ctx = (RuleContext) data;

421

422

// Rule logic here

423

if (violatesRule(node)) {

424

addViolation(ctx, node, "Rule violation message");

425

}

426

427

return super.visit(node, data);

428

}

429

430

private boolean violatesRule(ASTDefnDef method) {

431

// Custom rule logic

432

return true;

433

}

434

}

435

```