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

rule-system.mddocs/

0

# Rule System

1

2

The Rule System provides comprehensive infrastructure for defining, configuring, and executing static analysis rules. It includes rule interfaces, collection management, priority handling, loading mechanisms, and property-based configuration.

3

4

## Capabilities

5

6

### Rule Interface

7

8

Core interface for PMD rules with configuration, execution, and lifecycle management capabilities.

9

10

```java { .api }

11

/**

12

* Core interface for PMD rules with configuration and execution capabilities.

13

* Extends PropertySource to support configurable properties.

14

*/

15

public interface Rule extends PropertySource {

16

17

/**

18

* Suppress violations by regex pattern property descriptor

19

*/

20

PropertyDescriptor<Optional<Pattern>> VIOLATION_SUPPRESS_REGEX_DESCRIPTOR;

21

22

/**

23

* Suppress violations by XPath expression property descriptor

24

*/

25

PropertyDescriptor<Optional<String>> VIOLATION_SUPPRESS_XPATH_DESCRIPTOR;

26

27

/**

28

* Get rule's target language

29

* @return Language this rule analyzes

30

*/

31

Language getLanguage();

32

33

/**

34

* Set rule's target language

35

* @param language Language for this rule to analyze

36

*/

37

void setLanguage(Language language);

38

39

/**

40

* Get minimum language version required by rule

41

* @return Minimum LanguageVersion, or null if no minimum

42

*/

43

LanguageVersion getMinimumLanguageVersion();

44

45

/**

46

* Set minimum language version required

47

* @param version Minimum LanguageVersion required

48

*/

49

void setMinimumLanguageVersion(LanguageVersion version);

50

51

/**

52

* Get maximum language version supported by rule

53

* @return Maximum LanguageVersion, or null if no maximum

54

*/

55

LanguageVersion getMaximumLanguageVersion();

56

57

/**

58

* Set maximum language version supported

59

* @param version Maximum LanguageVersion supported

60

*/

61

void setMaximumLanguageVersion(LanguageVersion version);

62

63

/**

64

* Check if rule is deprecated

65

* @return true if rule is marked as deprecated

66

*/

67

boolean isDeprecated();

68

69

/**

70

* Set deprecation status

71

* @param deprecated true to mark rule as deprecated

72

*/

73

void setDeprecated(boolean deprecated);

74

75

/**

76

* Get rule name (unique identifier within ruleset)

77

* @return Name of the rule

78

*/

79

String getName();

80

81

/**

82

* Set rule name

83

* @param name Unique name for the rule

84

*/

85

void setName(String name);

86

87

/**

88

* Get PMD version when rule was added

89

* @return Version string when rule was introduced

90

*/

91

String getSince();

92

93

/**

94

* Set PMD version when rule was added

95

* @param since Version string when rule was introduced

96

*/

97

void setSince(String since);

98

99

/**

100

* Get rule implementation class name

101

* @return Fully qualified class name of rule implementation

102

*/

103

String getRuleClass();

104

105

/**

106

* Set rule implementation class name

107

* @param ruleClass Fully qualified class name

108

*/

109

void setRuleClass(String ruleClass);

110

111

/**

112

* Get violation message template

113

* @return Message template for violations (may contain {0} placeholders)

114

*/

115

String getMessage();

116

117

/**

118

* Set violation message template

119

* @param message Template for violation messages

120

*/

121

void setMessage(String message);

122

123

/**

124

* Get rule description

125

* @return Detailed description of what rule checks

126

*/

127

String getDescription();

128

129

/**

130

* Set rule description

131

* @param description Detailed description of rule purpose

132

*/

133

void setDescription(String description);

134

135

/**

136

* Get external documentation URL

137

* @return URL to external documentation, or null

138

*/

139

String getExternalInfoUrl();

140

141

/**

142

* Set external documentation URL

143

* @param externalInfoUrl URL to external documentation

144

*/

145

void setExternalInfoUrl(String externalInfoUrl);

146

147

/**

148

* Get rule priority level

149

* @return RulePriority indicating severity/importance

150

*/

151

RulePriority getPriority();

152

153

/**

154

* Set rule priority level

155

* @param priority RulePriority for violation severity

156

*/

157

void setPriority(RulePriority priority);

158

159

/**

160

* Check if rule uses Data Flow Analysis (DFA)

161

* @return true if rule requires DFA processing

162

*/

163

boolean isDfa();

164

165

/**

166

* Enable/disable Data Flow Analysis

167

* @param dfa true to enable DFA processing for this rule

168

*/

169

void setDfa(boolean dfa);

170

171

/**

172

* Check if rule uses type resolution

173

* @return true if rule requires type information

174

*/

175

boolean isTypeResolution();

176

177

/**

178

* Enable/disable type resolution

179

* @param typeResolution true to enable type resolution for this rule

180

*/

181

void setTypeResolution(boolean typeResolution);

182

183

/**

184

* Check if rule processes multiple files

185

* @return true if rule analyzes across multiple files

186

*/

187

boolean usesMultifile();

188

189

/**

190

* Set multifile processing capability

191

* @param multifile true if rule should process multiple files

192

*/

193

void setMultifile(boolean multifile);

194

195

/**

196

* Create deep copy of rule with all properties

197

* @return Independent copy of this rule

198

*/

199

Rule deepCopy();

200

201

/**

202

* Apply rule to AST node within rule context

203

* @param target AST node to analyze

204

* @param ctx RuleContext for violation reporting

205

*/

206

void apply(Node target, RuleContext ctx);

207

}

208

```

209

210

**Usage Examples:**

211

212

```java

213

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

214

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

215

import net.sourceforge.pmd.properties.*;

216

217

// Example custom rule implementation

218

public class MyCustomRule extends AbstractRule {

219

220

private static final PropertyDescriptor<Integer> THRESHOLD_DESCRIPTOR =

221

PropertyFactory.intProperty("threshold")

222

.desc("Maximum allowed complexity")

223

.defaultValue(10)

224

.build();

225

226

public MyCustomRule() {

227

definePropertyDescriptor(THRESHOLD_DESCRIPTOR);

228

setName("MyCustomRule");

229

setMessage("Complexity {0} exceeds threshold {1}");

230

setDescription("Detects overly complex code structures");

231

setPriority(RulePriority.MEDIUM);

232

setLanguage(LanguageRegistry.getLanguage("java"));

233

}

234

235

@Override

236

public void apply(Node target, RuleContext ctx) {

237

int threshold = getProperty(THRESHOLD_DESCRIPTOR);

238

239

int complexity = calculateComplexity(target);

240

if (complexity > threshold) {

241

ctx.addViolation(target, complexity, threshold);

242

}

243

}

244

245

private int calculateComplexity(Node node) {

246

// Custom complexity calculation logic

247

return node.getNumChildren() + 1;

248

}

249

}

250

251

// Configuring rule properties programmatically

252

Rule rule = new MyCustomRule();

253

rule.setProperty(MyCustomRule.THRESHOLD_DESCRIPTOR, 15);

254

rule.setPriority(RulePriority.HIGH);

255

rule.setMessage("Custom message: complexity {0} is too high");

256

257

// Creating rule copy with modifications

258

Rule ruleCopy = rule.deepCopy();

259

ruleCopy.setName("MyCustomRuleVariant");

260

ruleCopy.setPriority(RulePriority.LOW);

261

```

262

263

### RuleSet Collection

264

265

Collection of rules with optional file filtering patterns and metadata management.

266

267

```java { .api }

268

/**

269

* Collection of rules with optional file filtering patterns.

270

* Implements ChecksumAware for caching support.

271

*/

272

public class RuleSet implements ChecksumAware {

273

274

/**

275

* Copy constructor

276

* @param rs RuleSet to copy

277

*/

278

RuleSet(RuleSet rs);

279

280

/**

281

* Create ruleset containing single rule

282

* @param rule Single rule for the ruleset

283

* @return RuleSet containing only the specified rule

284

*/

285

static RuleSet forSingleRule(Rule rule);

286

287

/**

288

* Create new ruleset builder for programmatic construction

289

* @return RuleSetBuilder for fluent ruleset creation

290

*/

291

static RuleSetBuilder create();

292

293

/**

294

* Get source filename where ruleset was defined

295

* @return Filename of ruleset definition, or null if programmatic

296

*/

297

String getFileName();

298

299

/**

300

* Get ruleset name

301

* @return Name of the ruleset

302

*/

303

String getName();

304

305

/**

306

* Get ruleset description

307

* @return Description of ruleset purpose and contents

308

*/

309

String getDescription();

310

311

/**

312

* Get all rules in set

313

* @return Unmodifiable list of rules

314

*/

315

List<Rule> getRules();

316

317

/**

318

* Get rule by name

319

* @param ruleName Name of rule to find

320

* @return Rule with matching name, or null if not found

321

*/

322

Rule getRuleByName(String ruleName);

323

324

/**

325

* Check if ruleset applies to specific file

326

* @param file FileId to check against include/exclude patterns

327

* @return true if ruleset should be applied to file

328

*/

329

boolean applies(FileId file);

330

331

/**

332

* Check if ruleset applies to language version

333

* @param languageVersion LanguageVersion to check compatibility

334

* @return true if ruleset contains rules for language version

335

*/

336

boolean applies(LanguageVersion languageVersion);

337

338

/**

339

* Get checksum for caching and change detection

340

* @return Checksum representing current ruleset state

341

*/

342

long getChecksum();

343

344

/**

345

* Get number of rules in set

346

* @return Count of rules

347

*/

348

int size();

349

350

/**

351

* Iterate over rules in set

352

* @return Iterator for rules

353

*/

354

Iterator<Rule> iterator();

355

}

356

```

357

358

**Usage Examples:**

359

360

```java

361

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

362

import java.util.Arrays;

363

364

// Creating ruleset from single rule

365

Rule customRule = new MyCustomRule();

366

RuleSet singleRuleSet = RuleSet.forSingleRule(customRule);

367

368

// Building ruleset programmatically

369

RuleSet programmaticRuleSet = RuleSet.create()

370

.withName("Custom Analysis Rules")

371

.withDescription("Rules for custom static analysis")

372

.addRule(new MyCustomRule())

373

.addRule(new AnotherCustomRule())

374

.build();

375

376

// Working with loaded ruleset

377

RuleSetLoader loader = analysis.newRuleSetLoader();

378

RuleSet javaRules = loader.loadFromResource("rulesets/java/quickstart.xml");

379

380

System.out.printf("Loaded %d rules from %s%n",

381

javaRules.size(),

382

javaRules.getName());

383

384

// Check rule applicability

385

FileId javaFile = FileId.fromPathLikeString("src/main/java/Example.java");

386

if (javaRules.applies(javaFile)) {

387

System.out.println("Ruleset applies to Java files");

388

}

389

390

// Find specific rule

391

Rule specificRule = javaRules.getRuleByName("UnusedPrivateField");

392

if (specificRule != null) {

393

System.out.printf("Found rule: %s - %s%n",

394

specificRule.getName(),

395

specificRule.getDescription());

396

}

397

398

// Iterate through rules

399

for (Rule rule : javaRules) {

400

System.out.printf("Rule: %s (Priority: %s)%n",

401

rule.getName(),

402

rule.getPriority().getName());

403

}

404

```

405

406

### RuleSet Loading

407

408

Comprehensive ruleset loading from various sources including files, resources, URLs, and programmatic construction.

409

410

```java { .api }

411

/**

412

* Loads rulesets from various sources with filtering and configuration options.

413

*/

414

public final class RuleSetLoader {

415

416

/**

417

* Create RuleSetLoader from PMD configuration

418

* @param configuration PMDConfiguration with loading settings

419

* @return Configured RuleSetLoader instance

420

*/

421

static RuleSetLoader fromPmdConfig(PMDConfiguration configuration);

422

423

/**

424

* Load ruleset from resource path, file, or URL

425

* @param ruleSetReferenceId Resource path, file path, or URL to ruleset

426

* @return Loaded RuleSet with all rules and configurations

427

* @throws RuleSetLoadException if loading fails

428

*/

429

RuleSet loadFromResource(String ruleSetReferenceId);

430

431

/**

432

* Load multiple rulesets from list of references

433

* @param ruleSetReferenceIds List of resource paths, file paths, or URLs

434

* @return List of loaded RuleSet instances

435

* @throws RuleSetLoadException if any loading fails

436

*/

437

List<RuleSet> loadFromResources(List<String> ruleSetReferenceIds);

438

439

/**

440

* Create new ruleset builder for programmatic construction

441

* @return RuleSetBuilder for creating custom rulesets

442

*/

443

RuleSetBuilder newRuleSetBuilder();

444

445

/**

446

* Enable compatibility mode for legacy rulesets

447

* @param enable true to enable compatibility processing

448

* @return Previous compatibility mode setting

449

*/

450

boolean enableCompatibility(boolean enable);

451

452

/**

453

* Create filtered loader that only loads rules above priority threshold

454

* @param minimumPriority Minimum RulePriority to include

455

* @return New RuleSetLoader with priority filtering

456

*/

457

RuleSetLoader filterAbovePriority(RulePriority minimumPriority);

458

459

/**

460

* Get exceptions that occurred during loading

461

* @return List of RuleSetLoadException for failed loads

462

*/

463

List<RuleSetLoadException> getLoadExceptions();

464

}

465

```

466

467

**Usage Examples:**

468

469

```java

470

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

471

import java.util.Arrays;

472

import java.util.List;

473

474

// Create loader from configuration

475

PMDConfiguration config = new PMDConfiguration();

476

RuleSetLoader loader = RuleSetLoader.fromPmdConfig(config);

477

478

// Load single ruleset

479

try {

480

RuleSet javaQuickstart = loader.loadFromResource("rulesets/java/quickstart.xml");

481

System.out.printf("Loaded %d rules%n", javaQuickstart.size());

482

} catch (RuleSetLoadException e) {

483

System.err.println("Failed to load ruleset: " + e.getMessage());

484

}

485

486

// Load multiple rulesets

487

List<String> rulesetPaths = Arrays.asList(

488

"rulesets/java/quickstart.xml",

489

"rulesets/java/design.xml",

490

"rulesets/java/errorprone.xml",

491

"custom-rules.xml",

492

"https://example.com/remote-rules.xml"

493

);

494

495

try {

496

List<RuleSet> ruleSets = loader.loadFromResources(rulesetPaths);

497

int totalRules = ruleSets.stream().mapToInt(RuleSet::size).sum();

498

System.out.printf("Loaded %d rulesets with %d total rules%n",

499

ruleSets.size(), totalRules);

500

} catch (RuleSetLoadException e) {

501

System.err.println("Failed to load some rulesets: " + e.getMessage());

502

}

503

504

// Filter rules by priority

505

RuleSetLoader highPriorityLoader = loader.filterAbovePriority(RulePriority.MEDIUM);

506

RuleSet filteredRules = highPriorityLoader.loadFromResource("rulesets/java/quickstart.xml");

507

508

// Check for loading exceptions

509

List<RuleSetLoadException> exceptions = loader.getLoadExceptions();

510

if (!exceptions.isEmpty()) {

511

System.err.println("Loading issues occurred:");

512

for (RuleSetLoadException exception : exceptions) {

513

System.err.printf(" %s: %s%n", exception.getLocation(), exception.getMessage());

514

}

515

}

516

517

// Build custom ruleset

518

RuleSetBuilder builder = loader.newRuleSetBuilder();

519

RuleSet customRuleSet = builder

520

.withName("Custom Project Rules")

521

.withDescription("Rules specific to our project")

522

.addRule(new MyCustomRule())

523

.addRuleByReference("rulesets/java/design.xml/CyclomaticComplexity")

524

.build();

525

```

526

527

### Rule Priority System

528

529

Enumeration defining rule priority levels from HIGH to LOW with utility methods for comparison and conversion.

530

531

```java { .api }

532

/**

533

* Rule priority levels from HIGH (most important) to LOW (least important).

534

* Used for filtering rules and determining violation severity.

535

*/

536

public enum RulePriority {

537

538

/** Highest priority - critical issues */

539

HIGH(1),

540

541

/** Medium-high priority - important issues */

542

MEDIUM_HIGH(2),

543

544

/** Medium priority - standard issues */

545

MEDIUM(3),

546

547

/** Medium-low priority - minor issues */

548

MEDIUM_LOW(4),

549

550

/** Lowest priority - informational issues */

551

LOW(5);

552

553

/**

554

* Get numeric priority value (lower numbers = higher priority)

555

* @return Numeric priority (1-5)

556

*/

557

int getPriority();

558

559

/**

560

* Get priority name as string

561

* @return String representation of priority level

562

*/

563

String getName();

564

565

/**

566

* Get RulePriority by numeric value

567

* @param priority Numeric priority (1-5)

568

* @return RulePriority corresponding to numeric value

569

* @throws IllegalArgumentException if priority not in valid range

570

*/

571

static RulePriority valueOf(int priority);

572

573

/**

574

* Get RulePriority by name (case-insensitive)

575

* @param name Priority name (HIGH, MEDIUM, LOW, etc.)

576

* @return RulePriority corresponding to name

577

* @throws IllegalArgumentException if name not recognized

578

*/

579

static RulePriority valueOfName(String name);

580

}

581

```

582

583

**Usage Examples:**

584

585

```java

586

import net.sourceforge.pmd.lang.rule.RulePriority;

587

588

// Working with rule priorities

589

RulePriority high = RulePriority.HIGH;

590

System.out.printf("Priority %s has numeric value %d%n",

591

high.getName(), high.getPriority());

592

593

// Creating priorities from values

594

RulePriority medium = RulePriority.valueOf(3);

595

RulePriority low = RulePriority.valueOfName("LOW");

596

597

// Comparing priorities

598

if (RulePriority.HIGH.getPriority() < RulePriority.LOW.getPriority()) {

599

System.out.println("HIGH priority has lower numeric value than LOW");

600

}

601

602

// Using priorities in rule configuration

603

Rule rule = new MyCustomRule();

604

rule.setPriority(RulePriority.MEDIUM_HIGH);

605

606

// Filtering by priority in configuration

607

PMDConfiguration config = new PMDConfiguration();

608

config.setMinimumPriority(RulePriority.MEDIUM); // Only MEDIUM+ priority rules

609

610

// Priority-based rule filtering

611

RuleSetLoader loader = RuleSetLoader.fromPmdConfig(config);

612

RuleSetLoader highPriorityLoader = loader.filterAbovePriority(RulePriority.MEDIUM_HIGH);

613

614

// Setting priorities programmatically for different environments

615

if (isProductionBuild()) {

616

config.setMinimumPriority(RulePriority.HIGH);

617

} else if (isDevelopmentBuild()) {

618

config.setMinimumPriority(RulePriority.LOW);

619

} else {

620

config.setMinimumPriority(RulePriority.MEDIUM);

621

}

622

```

623

624

## Types

625

626

```java { .api }

627

/**

628

* Builder for constructing RuleSet instances programmatically

629

*/

630

interface RuleSetBuilder {

631

RuleSetBuilder withName(String name);

632

RuleSetBuilder withDescription(String description);

633

RuleSetBuilder addRule(Rule rule);

634

RuleSetBuilder addRuleByReference(String ruleReference);

635

RuleSetBuilder filterRules(Predicate<Rule> filter);

636

RuleSet build();

637

}

638

639

/**

640

* Context for rule execution providing violation reporting capabilities

641

*/

642

interface RuleContext {

643

void addViolation(Node node);

644

void addViolation(Node node, Object... args);

645

void addViolationWithMessage(Node node, String message);

646

LanguageVersion getLanguageVersion();

647

String getFilename();

648

TextDocument getTextDocument();

649

boolean isIgnored(Rule rule);

650

}

651

652

/**

653

* Exception thrown when ruleset loading fails

654

*/

655

class RuleSetLoadException extends Exception {

656

String getLocation();

657

String getCause();

658

List<String> getErrors();

659

}

660

661

/**

662

* Interface for objects that can compute checksums for caching

663

*/

664

interface ChecksumAware {

665

long getChecksum();

666

}

667

668

/**

669

* File identifier for tracking files across analysis

670

*/

671

interface FileId {

672

String getAbsolutePath();

673

String getDisplayName();

674

URI getUri();

675

static FileId fromPathLikeString(String pathLike);

676

static FileId fromPath(Path path);

677

}

678

679

/**

680

* Language version representing specific version of programming language

681

*/

682

interface LanguageVersion extends Comparable<LanguageVersion> {

683

Language getLanguage();

684

String getVersion();

685

String getName();

686

String getShortName();

687

String getTerseName();

688

}

689

690

/**

691

* Abstract base class for rule implementations

692

*/

693

abstract class AbstractRule implements Rule {

694

protected void definePropertyDescriptor(PropertyDescriptor<?> propertyDescriptor);

695

protected void addRuleChainVisit(String astNodeName);

696

protected void addRuleChainVisit(Class<? extends Node> nodeType);

697

}

698

```