or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast.mdcpd.mdindex.mdlanguage-module.mdmetrics.mdmultifile.mdrules.md

rules.mddocs/

0

# Rule Development Framework

1

2

Base classes and framework for creating custom PMD rules. Includes abstract base rule class with visitor pattern integration and comprehensive set of built-in rules across 6 categories.

3

4

## Capabilities

5

6

### Base Rule Class

7

8

Abstract base class for all Apex PMD rules with visitor pattern integration.

9

10

```java { .api }

11

/**

12

* Base class for all Apex PMD rules

13

* Implements ApexVisitor for AST traversal and rule logic

14

*/

15

public abstract class AbstractApexRule implements ApexVisitor<Object, Object> {

16

/**

17

* Apply rule to a node with rule context

18

* @param target - AST node to analyze

19

* @param ctx - Rule context with violation reporting capabilities

20

*/

21

public void apply(Node target, RuleContext ctx);

22

23

// Inherits all 80+ visit methods from ApexVisitor interface

24

// Override specific visit methods to implement rule logic

25

Object visit(ASTUserClass node, Object data);

26

Object visit(ASTMethod node, Object data);

27

Object visit(ASTVariableExpression node, Object data);

28

// ... and 80+ more visit methods

29

}

30

```

31

32

### Rule Context

33

34

Context object providing violation reporting and analysis capabilities.

35

36

```java { .api }

37

/**

38

* Rule execution context

39

*/

40

public interface RuleContext {

41

/** Report a violation at the given node */

42

void addViolation(Node node, String message);

43

44

/** Report a violation with custom message parameters */

45

void addViolation(Node node, String message, Object... params);

46

47

/** Get current file name being analyzed */

48

String getFileDisplayName();

49

50

/** Get language processor for multifile analysis */

51

LanguageProcessor getLanguageProcessor();

52

53

/** Get rule being executed */

54

Rule getRule();

55

}

56

```

57

58

## Built-in Rules by Category

59

60

### Best Practices Rules

61

62

Rules promoting Apex coding best practices and testing standards.

63

64

```java { .api }

65

/**

66

* Base class for unit test related rules

67

*/

68

public abstract class AbstractApexUnitTestRule extends AbstractApexRule {

69

/** Check if class is a test class */

70

protected boolean isTestClass(ASTUserClass node);

71

}

72

73

/**

74

* Assertions should include descriptive messages

75

*/

76

public class ApexAssertionsShouldIncludeMessageRule extends AbstractApexRule {

77

// Checks System.assert() calls for message parameters

78

}

79

80

/**

81

* Test classes should contain assertions

82

*/

83

public class ApexUnitTestClassShouldHaveAssertsRule extends AbstractApexUnitTestRule {

84

// Verifies test methods contain assertion statements

85

}

86

87

/**

88

* Test classes should use RunAs for proper test isolation

89

*/

90

public class ApexUnitTestClassShouldHaveRunAsRule extends AbstractApexUnitTestRule {

91

// Checks for System.runAs() usage in test methods

92

}

93

94

/**

95

* Avoid SeeAllData=true in test classes

96

*/

97

public class ApexUnitTestShouldNotUseSeeAllDataTrueRule extends AbstractApexUnitTestRule {

98

// Detects @isTest(SeeAllData=true) annotations

99

}

100

101

/**

102

* Avoid using global modifier unnecessarily

103

*/

104

public class AvoidGlobalModifierRule extends AbstractApexRule {

105

// Flags unnecessary global access modifiers

106

}

107

108

/**

109

* Keep trigger logic simple, delegate to handler classes

110

*/

111

public class AvoidLogicInTriggerRule extends AbstractApexRule {

112

// Detects complex logic directly in trigger bodies

113

}

114

115

/**

116

* Queueable jobs should implement finalizer methods

117

*/

118

public class QueueableWithoutFinalizerRule extends AbstractApexRule {

119

// Checks Queueable classes for proper error handling

120

}

121

122

/**

123

* Detect unused local variables

124

*/

125

public class UnusedLocalVariableRule extends AbstractApexRule {

126

// Identifies local variables that are declared but never used

127

}

128

```

129

130

### Code Style Rules

131

132

Rules enforcing naming conventions and code organization.

133

134

```java { .api }

135

/**

136

* Base class for naming convention rules

137

*/

138

public abstract class AbstractNamingConventionsRule extends AbstractApexRule {

139

/** Check if name matches expected pattern */

140

protected boolean matches(String name, Pattern pattern);

141

}

142

143

/**

144

* Enforce class naming conventions

145

*/

146

public class ClassNamingConventionsRule extends AbstractNamingConventionsRule {

147

// Enforces PascalCase for class names

148

}

149

150

/**

151

* Fields should be declared at the start of classes

152

*/

153

public class FieldDeclarationsShouldBeAtStartRule extends AbstractApexRule {

154

// Checks field placement within class body

155

}

156

157

/**

158

* Enforce field naming conventions

159

*/

160

public class FieldNamingConventionsRule extends AbstractNamingConventionsRule {

161

// Enforces camelCase for field names

162

}

163

164

/**

165

* Enforce method parameter naming conventions

166

*/

167

public class FormalParameterNamingConventionsRule extends AbstractNamingConventionsRule {

168

// Enforces camelCase for parameter names

169

}

170

171

/**

172

* Enforce local variable naming conventions

173

*/

174

public class LocalVariableNamingConventionsRule extends AbstractNamingConventionsRule {

175

// Enforces camelCase for local variable names

176

}

177

178

/**

179

* Enforce method naming conventions

180

*/

181

public class MethodNamingConventionsRule extends AbstractNamingConventionsRule {

182

// Enforces camelCase for method names

183

}

184

185

/**

186

* Enforce property naming conventions

187

*/

188

public class PropertyNamingConventionsRule extends AbstractNamingConventionsRule {

189

// Enforces PascalCase for property names

190

}

191

```

192

193

### Design Rules

194

195

Rules measuring code complexity and design quality.

196

197

```java { .api }

198

/**

199

* Base class for NCSS (Non-Commenting Source Statements) counting rules

200

*/

201

public abstract class AbstractNcssCountRule extends AbstractApexRule {

202

/** Count NCSS for a node */

203

protected int countNcss(Node node);

204

}

205

206

/**

207

* Avoid deeply nested if statements

208

*/

209

public class AvoidDeeplyNestedIfStmtsRule extends AbstractApexRule {

210

// Detects excessive nesting depth in conditional statements

211

}

212

213

/**

214

* Measure cognitive complexity of methods

215

*/

216

public class CognitiveComplexityRule extends AbstractApexRule {

217

// Uses ApexMetrics.COGNITIVE_COMPLEXITY to measure understandability

218

}

219

220

/**

221

* Measure cyclomatic complexity of methods

222

*/

223

public class CyclomaticComplexityRule extends AbstractApexRule {

224

// Uses ApexMetrics.CYCLO to measure code paths

225

}

226

227

/**

228

* Limit class length

229

*/

230

public class ExcessiveClassLengthRule extends AbstractApexRule {

231

// Counts lines of code in class declarations

232

}

233

234

/**

235

* Limit method parameter count

236

*/

237

public class ExcessiveParameterListRule extends AbstractApexRule {

238

// Counts parameters in method signatures

239

}

240

241

/**

242

* Limit number of public members in classes

243

*/

244

public class ExcessivePublicCountRule extends AbstractApexRule {

245

// Counts public methods, fields, and properties

246

}

247

248

/**

249

* Measure constructor complexity using NCSS

250

*/

251

public class NcssConstructorCountRule extends AbstractNcssCountRule {

252

// Applies NCSS counting to constructor methods

253

}

254

255

/**

256

* Measure method complexity using NCSS

257

*/

258

public class NcssMethodCountRule extends AbstractNcssCountRule {

259

// Applies NCSS counting to regular methods

260

}

261

262

/**

263

* Measure type complexity using NCSS

264

*/

265

public class NcssTypeCountRule extends AbstractNcssCountRule {

266

// Applies NCSS counting to entire classes/interfaces

267

}

268

269

/**

270

* Standard cyclomatic complexity measurement

271

*/

272

public class StdCyclomaticComplexityRule extends AbstractApexRule {

273

// Standard implementation of cyclomatic complexity

274

}

275

276

/**

277

* Limit number of fields in classes

278

*/

279

public class TooManyFieldsRule extends AbstractApexRule {

280

// Counts field declarations in classes

281

}

282

283

/**

284

* Detect unused methods

285

*/

286

public class UnusedMethodRule extends AbstractApexRule {

287

// Identifies methods that are declared but never called

288

}

289

```

290

291

### Error Prone Rules

292

293

Rules detecting common programming errors and potential bugs.

294

295

```java { .api }

296

/**

297

* Detect CSRF vulnerabilities in Apex

298

*/

299

public class ApexCSRFRule extends AbstractApexRule {

300

// Identifies potential Cross-Site Request Forgery issues

301

}

302

303

/**

304

* Avoid hardcoded Salesforce record IDs

305

*/

306

public class AvoidHardcodingIdRule extends AbstractApexRule {

307

// Detects hardcoded 15 or 18 character Salesforce IDs

308

}

309

310

/**

311

* Check for non-existent annotations

312

*/

313

public class AvoidNonExistentAnnotationsRule extends AbstractApexRule {

314

// Validates annotation usage against known Apex annotations

315

}

316

317

/**

318

* Avoid stateful database operations that can cause issues

319

*/

320

public class AvoidStatefulDatabaseResultRule extends AbstractApexRule {

321

// Detects problematic database result handling patterns

322

}

323

324

/**

325

* Check Aura-enabled getter accessibility

326

*/

327

public class InaccessibleAuraEnabledGetterRule extends AbstractApexRule {

328

// Validates @AuraEnabled getter method accessibility

329

}

330

331

/**

332

* Method names should not match enclosing class name

333

*/

334

public class MethodWithSameNameAsEnclosingClassRule extends AbstractApexRule {

335

// Detects methods that should be constructors

336

}

337

338

/**

339

* Override both equals and hashCode together

340

*/

341

public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule {

342

// Ensures both methods are implemented when one is overridden

343

}

344

345

/**

346

* Avoid shadowing built-in namespace names

347

*/

348

public class TypeShadowsBuiltInNamespaceRule extends AbstractApexRule {

349

// Detects types that shadow Salesforce built-in namespaces

350

}

351

```

352

353

### Performance Rules

354

355

Rules identifying performance bottlenecks and optimization opportunities.

356

357

```java { .api }

358

/**

359

* Base class for rules detecting expensive operations in loops

360

*/

361

public abstract class AbstractAvoidNodeInLoopsRule extends AbstractApexRule {

362

/** Check if node is inside a loop construct */

363

protected boolean isInLoop(Node node);

364

}

365

366

/**

367

* Avoid non-selective SOQL queries

368

*/

369

public class AvoidNonRestrictiveQueriesRule extends AbstractApexRule {

370

// Detects SOQL queries without WHERE clauses or selective filters

371

}

372

373

/**

374

* Avoid expensive operations inside loops

375

*/

376

public class OperationWithHighCostInLoopRule extends AbstractAvoidNodeInLoopsRule {

377

// Detects DML, SOQL, and other expensive operations in loops

378

}

379

380

/**

381

* Avoid operations that consume governor limits inside loops

382

*/

383

public class OperationWithLimitsInLoopRule extends AbstractAvoidNodeInLoopsRule {

384

// Identifies operations that can hit Salesforce governor limits

385

}

386

```

387

388

### Security Rules

389

390

Rules detecting security vulnerabilities and unsafe practices.

391

392

```java { .api }

393

/**

394

* Detect bad cryptographic practices

395

*/

396

public class ApexBadCryptoRule extends AbstractApexRule {

397

// Identifies weak encryption algorithms and practices

398

}

399

400

/**

401

* Detect CRUD and FLS security violations

402

*/

403

public class ApexCRUDViolationRule extends AbstractApexRule {

404

// Checks for proper CRUD/FLS security enforcement

405

}

406

407

/**

408

* Detect usage of dangerous methods

409

*/

410

public class ApexDangerousMethodsRule extends AbstractApexRule {

411

// Identifies potentially unsafe method calls

412

}

413

414

/**

415

* Detect insecure HTTP endpoints

416

*/

417

public class ApexInsecureEndpointRule extends AbstractApexRule {

418

// Checks for HTTP instead of HTTPS endpoints

419

}

420

421

/**

422

* Detect open redirect vulnerabilities

423

*/

424

public class ApexOpenRedirectRule extends AbstractApexRule {

425

// Identifies potential open redirect vulnerabilities

426

}

427

428

/**

429

* Detect SOQL injection vulnerabilities

430

*/

431

public class ApexSOQLInjectionRule extends AbstractApexRule {

432

// Checks for dynamic SOQL construction without proper sanitization

433

}

434

435

/**

436

* Detect sharing rule violations

437

*/

438

public class ApexSharingViolationsRule extends AbstractApexRule {

439

// Identifies bypass of Salesforce sharing rules

440

}

441

442

/**

443

* Suggest using Named Credentials for external calls

444

*/

445

public class ApexSuggestUsingNamedCredRule extends AbstractApexRule {

446

// Recommends Named Credentials over hardcoded endpoints

447

}

448

449

/**

450

* Detect XSS vulnerabilities from escape=false

451

*/

452

public class ApexXSSFromEscapeFalseRule extends AbstractApexRule {

453

// Identifies potential XSS from unescaped output

454

}

455

456

/**

457

* Detect XSS vulnerabilities from URL parameters

458

*/

459

public class ApexXSSFromURLParamRule extends AbstractApexRule {

460

// Checks for unsafe URL parameter handling

461

}

462

```

463

464

**Usage Examples:**

465

466

```java

467

// Create custom rule extending AbstractApexRule

468

public class CustomComplexityRule extends AbstractApexRule {

469

@Override

470

public Object visit(ASTMethod node, Object data) {

471

RuleContext ctx = (RuleContext) data;

472

473

// Use metrics to check complexity

474

int complexity = MetricsUtil.computeMetric(ApexMetrics.CYCLO, node);

475

if (complexity > 15) {

476

ctx.addViolation(node, "Method complexity too high: {0}", complexity);

477

}

478

479

// Check for specific patterns

480

List<ASTSoqlExpression> queries = node.findDescendantsOfType(ASTSoqlExpression.class);

481

if (queries.size() > 3) {

482

ctx.addViolation(node, "Too many SOQL queries in method: {0}", queries.size());

483

}

484

485

return super.visit(node, data);

486

}

487

488

@Override

489

public Object visit(ASTUserClass node, Object data) {

490

RuleContext ctx = (RuleContext) data;

491

492

// Check class naming

493

String className = node.getQualifiedName().getClassName();

494

if (!className.matches("^[A-Z][a-zA-Z0-9]*$")) {

495

ctx.addViolation(node, "Class name should follow PascalCase convention");

496

}

497

498

// Use multifile analysis if available

499

ApexMultifileAnalysis multifile = ctx.getLanguageProcessor().getMultiFileState();

500

if (!multifile.isFailed()) {

501

List<Issue> issues = multifile.getFileIssues(ctx.getFileDisplayName());

502

for (Issue issue : issues) {

503

if (issue.getSeverity() == Severity.ERROR) {

504

ctx.addViolation(node, "Multifile analysis error: {0}", issue.getMessage());

505

}

506

}

507

}

508

509

return super.visit(node, data);

510

}

511

}

512

513

// Rule that checks for test class patterns

514

public class TestClassValidationRule extends AbstractApexUnitTestRule {

515

@Override

516

public Object visit(ASTUserClass node, Object data) {

517

if (isTestClass(node)) {

518

RuleContext ctx = (RuleContext) data;

519

520

// Check for test methods

521

List<ASTMethod> methods = node.findDescendantsOfType(ASTMethod.class);

522

boolean hasTestMethods = methods.stream()

523

.anyMatch(method -> method.getName().toLowerCase().contains("test"));

524

525

if (!hasTestMethods) {

526

ctx.addViolation(node, "Test class should contain test methods");

527

}

528

529

// Check for assertions in test methods

530

for (ASTMethod method : methods) {

531

if (method.getName().toLowerCase().contains("test")) {

532

List<ASTMethodCallExpression> calls = method.findDescendantsOfType(ASTMethodCallExpression.class);

533

boolean hasAssertions = calls.stream()

534

.anyMatch(call -> call.getMethodName().startsWith("assert"));

535

536

if (!hasAssertions) {

537

ctx.addViolation(method, "Test method should contain assertions");

538

}

539

}

540

}

541

}

542

543

return super.visit(node, data);

544

}

545

}

546

```