CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-net-sourceforge-pmd--pmd-apex

PMD Apex language module providing static code analysis support for Salesforce Apex programming language.

Pending
Overview
Eval results
Files

rules.mddocs/

Rule Development Framework

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.

Capabilities

Base Rule Class

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

/**
 * Base class for all Apex PMD rules
 * Implements ApexVisitor for AST traversal and rule logic
 */
public abstract class AbstractApexRule implements ApexVisitor<Object, Object> {
    /** 
     * Apply rule to a node with rule context
     * @param target - AST node to analyze
     * @param ctx - Rule context with violation reporting capabilities
     */
    public void apply(Node target, RuleContext ctx);
    
    // Inherits all 80+ visit methods from ApexVisitor interface
    // Override specific visit methods to implement rule logic
    Object visit(ASTUserClass node, Object data);
    Object visit(ASTMethod node, Object data);
    Object visit(ASTVariableExpression node, Object data);
    // ... and 80+ more visit methods
}

Rule Context

Context object providing violation reporting and analysis capabilities.

/**
 * Rule execution context
 */
public interface RuleContext {
    /** Report a violation at the given node */
    void addViolation(Node node, String message);
    
    /** Report a violation with custom message parameters */
    void addViolation(Node node, String message, Object... params);
    
    /** Get current file name being analyzed */
    String getFileDisplayName();
    
    /** Get language processor for multifile analysis */
    LanguageProcessor getLanguageProcessor();
    
    /** Get rule being executed */
    Rule getRule();
}

Built-in Rules by Category

Best Practices Rules

Rules promoting Apex coding best practices and testing standards.

/**
 * Base class for unit test related rules
 */
public abstract class AbstractApexUnitTestRule extends AbstractApexRule {
    /** Check if class is a test class */
    protected boolean isTestClass(ASTUserClass node);
}

/**
 * Assertions should include descriptive messages
 */
public class ApexAssertionsShouldIncludeMessageRule extends AbstractApexRule {
    // Checks System.assert() calls for message parameters
}

/**
 * Test classes should contain assertions
 */
public class ApexUnitTestClassShouldHaveAssertsRule extends AbstractApexUnitTestRule {
    // Verifies test methods contain assertion statements
}

/**
 * Test classes should use RunAs for proper test isolation
 */
public class ApexUnitTestClassShouldHaveRunAsRule extends AbstractApexUnitTestRule {
    // Checks for System.runAs() usage in test methods
}

/**
 * Avoid SeeAllData=true in test classes
 */
public class ApexUnitTestShouldNotUseSeeAllDataTrueRule extends AbstractApexUnitTestRule {
    // Detects @isTest(SeeAllData=true) annotations
}

/**
 * Avoid using global modifier unnecessarily
 */
public class AvoidGlobalModifierRule extends AbstractApexRule {
    // Flags unnecessary global access modifiers
}

/**
 * Keep trigger logic simple, delegate to handler classes
 */
public class AvoidLogicInTriggerRule extends AbstractApexRule {
    // Detects complex logic directly in trigger bodies
}

/**
 * Queueable jobs should implement finalizer methods
 */
public class QueueableWithoutFinalizerRule extends AbstractApexRule {
    // Checks Queueable classes for proper error handling
}

/**
 * Detect unused local variables
 */
public class UnusedLocalVariableRule extends AbstractApexRule {
    // Identifies local variables that are declared but never used
}

Code Style Rules

Rules enforcing naming conventions and code organization.

/**
 * Base class for naming convention rules
 */
public abstract class AbstractNamingConventionsRule extends AbstractApexRule {
    /** Check if name matches expected pattern */
    protected boolean matches(String name, Pattern pattern);
}

/**
 * Enforce class naming conventions
 */
public class ClassNamingConventionsRule extends AbstractNamingConventionsRule {
    // Enforces PascalCase for class names
}

/**
 * Fields should be declared at the start of classes
 */
public class FieldDeclarationsShouldBeAtStartRule extends AbstractApexRule {
    // Checks field placement within class body
}

/**
 * Enforce field naming conventions
 */
public class FieldNamingConventionsRule extends AbstractNamingConventionsRule {
    // Enforces camelCase for field names
}

/**
 * Enforce method parameter naming conventions
 */
public class FormalParameterNamingConventionsRule extends AbstractNamingConventionsRule {
    // Enforces camelCase for parameter names
}

/**
 * Enforce local variable naming conventions
 */
public class LocalVariableNamingConventionsRule extends AbstractNamingConventionsRule {
    // Enforces camelCase for local variable names
}

/**
 * Enforce method naming conventions
 */
public class MethodNamingConventionsRule extends AbstractNamingConventionsRule {
    // Enforces camelCase for method names
}

/**
 * Enforce property naming conventions
 */
public class PropertyNamingConventionsRule extends AbstractNamingConventionsRule {
    // Enforces PascalCase for property names
}

Design Rules

Rules measuring code complexity and design quality.

/**
 * Base class for NCSS (Non-Commenting Source Statements) counting rules
 */
public abstract class AbstractNcssCountRule extends AbstractApexRule {
    /** Count NCSS for a node */
    protected int countNcss(Node node);
}

/**
 * Avoid deeply nested if statements
 */
public class AvoidDeeplyNestedIfStmtsRule extends AbstractApexRule {
    // Detects excessive nesting depth in conditional statements
}

/**
 * Measure cognitive complexity of methods
 */
public class CognitiveComplexityRule extends AbstractApexRule {
    // Uses ApexMetrics.COGNITIVE_COMPLEXITY to measure understandability
}

/**
 * Measure cyclomatic complexity of methods
 */
public class CyclomaticComplexityRule extends AbstractApexRule {
    // Uses ApexMetrics.CYCLO to measure code paths
}

/**
 * Limit class length
 */
public class ExcessiveClassLengthRule extends AbstractApexRule {
    // Counts lines of code in class declarations
}

/**
 * Limit method parameter count
 */
public class ExcessiveParameterListRule extends AbstractApexRule {
    // Counts parameters in method signatures
}

/**
 * Limit number of public members in classes
 */
public class ExcessivePublicCountRule extends AbstractApexRule {
    // Counts public methods, fields, and properties
}

/**
 * Measure constructor complexity using NCSS
 */
public class NcssConstructorCountRule extends AbstractNcssCountRule {
    // Applies NCSS counting to constructor methods
}

/**
 * Measure method complexity using NCSS
 */
public class NcssMethodCountRule extends AbstractNcssCountRule {
    // Applies NCSS counting to regular methods
}

/**
 * Measure type complexity using NCSS
 */
public class NcssTypeCountRule extends AbstractNcssCountRule {
    // Applies NCSS counting to entire classes/interfaces
}

/**
 * Standard cyclomatic complexity measurement
 */
public class StdCyclomaticComplexityRule extends AbstractApexRule {
    // Standard implementation of cyclomatic complexity
}

/**
 * Limit number of fields in classes
 */
public class TooManyFieldsRule extends AbstractApexRule {
    // Counts field declarations in classes
}

/**
 * Detect unused methods
 */
public class UnusedMethodRule extends AbstractApexRule {
    // Identifies methods that are declared but never called
}

Error Prone Rules

Rules detecting common programming errors and potential bugs.

/**
 * Detect CSRF vulnerabilities in Apex
 */
public class ApexCSRFRule extends AbstractApexRule {
    // Identifies potential Cross-Site Request Forgery issues
}

/**
 * Avoid hardcoded Salesforce record IDs
 */
public class AvoidHardcodingIdRule extends AbstractApexRule {
    // Detects hardcoded 15 or 18 character Salesforce IDs
}

/**
 * Check for non-existent annotations
 */
public class AvoidNonExistentAnnotationsRule extends AbstractApexRule {
    // Validates annotation usage against known Apex annotations
}

/**
 * Avoid stateful database operations that can cause issues
 */
public class AvoidStatefulDatabaseResultRule extends AbstractApexRule {
    // Detects problematic database result handling patterns
}

/**
 * Check Aura-enabled getter accessibility
 */
public class InaccessibleAuraEnabledGetterRule extends AbstractApexRule {
    // Validates @AuraEnabled getter method accessibility
}

/**
 * Method names should not match enclosing class name
 */
public class MethodWithSameNameAsEnclosingClassRule extends AbstractApexRule {
    // Detects methods that should be constructors
}

/**
 * Override both equals and hashCode together
 */
public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule {
    // Ensures both methods are implemented when one is overridden
}

/**
 * Avoid shadowing built-in namespace names
 */
public class TypeShadowsBuiltInNamespaceRule extends AbstractApexRule {
    // Detects types that shadow Salesforce built-in namespaces
}

Performance Rules

Rules identifying performance bottlenecks and optimization opportunities.

/**
 * Base class for rules detecting expensive operations in loops
 */
public abstract class AbstractAvoidNodeInLoopsRule extends AbstractApexRule {
    /** Check if node is inside a loop construct */
    protected boolean isInLoop(Node node);
}

/**
 * Avoid non-selective SOQL queries
 */
public class AvoidNonRestrictiveQueriesRule extends AbstractApexRule {
    // Detects SOQL queries without WHERE clauses or selective filters
}

/**
 * Avoid expensive operations inside loops
 */
public class OperationWithHighCostInLoopRule extends AbstractAvoidNodeInLoopsRule {
    // Detects DML, SOQL, and other expensive operations in loops
}

/**
 * Avoid operations that consume governor limits inside loops
 */
public class OperationWithLimitsInLoopRule extends AbstractAvoidNodeInLoopsRule {
    // Identifies operations that can hit Salesforce governor limits
}

Security Rules

Rules detecting security vulnerabilities and unsafe practices.

/**
 * Detect bad cryptographic practices
 */
public class ApexBadCryptoRule extends AbstractApexRule {
    // Identifies weak encryption algorithms and practices
}

/**
 * Detect CRUD and FLS security violations
 */
public class ApexCRUDViolationRule extends AbstractApexRule {
    // Checks for proper CRUD/FLS security enforcement
}

/**
 * Detect usage of dangerous methods
 */
public class ApexDangerousMethodsRule extends AbstractApexRule {
    // Identifies potentially unsafe method calls
}

/**
 * Detect insecure HTTP endpoints
 */
public class ApexInsecureEndpointRule extends AbstractApexRule {
    // Checks for HTTP instead of HTTPS endpoints
}

/**
 * Detect open redirect vulnerabilities
 */
public class ApexOpenRedirectRule extends AbstractApexRule {
    // Identifies potential open redirect vulnerabilities
}

/**
 * Detect SOQL injection vulnerabilities
 */
public class ApexSOQLInjectionRule extends AbstractApexRule {
    // Checks for dynamic SOQL construction without proper sanitization
}

/**
 * Detect sharing rule violations
 */
public class ApexSharingViolationsRule extends AbstractApexRule {
    // Identifies bypass of Salesforce sharing rules
}

/**
 * Suggest using Named Credentials for external calls
 */
public class ApexSuggestUsingNamedCredRule extends AbstractApexRule {
    // Recommends Named Credentials over hardcoded endpoints
}

/**
 * Detect XSS vulnerabilities from escape=false
 */
public class ApexXSSFromEscapeFalseRule extends AbstractApexRule {
    // Identifies potential XSS from unescaped output
}

/**
 * Detect XSS vulnerabilities from URL parameters
 */
public class ApexXSSFromURLParamRule extends AbstractApexRule {
    // Checks for unsafe URL parameter handling
}

Usage Examples:

// Create custom rule extending AbstractApexRule
public class CustomComplexityRule extends AbstractApexRule {
    @Override
    public Object visit(ASTMethod node, Object data) {
        RuleContext ctx = (RuleContext) data;
        
        // Use metrics to check complexity
        int complexity = MetricsUtil.computeMetric(ApexMetrics.CYCLO, node);
        if (complexity > 15) {
            ctx.addViolation(node, "Method complexity too high: {0}", complexity);
        }
        
        // Check for specific patterns
        List<ASTSoqlExpression> queries = node.findDescendantsOfType(ASTSoqlExpression.class);
        if (queries.size() > 3) {
            ctx.addViolation(node, "Too many SOQL queries in method: {0}", queries.size());
        }
        
        return super.visit(node, data);
    }
    
    @Override
    public Object visit(ASTUserClass node, Object data) {
        RuleContext ctx = (RuleContext) data;
        
        // Check class naming
        String className = node.getQualifiedName().getClassName();
        if (!className.matches("^[A-Z][a-zA-Z0-9]*$")) {
            ctx.addViolation(node, "Class name should follow PascalCase convention");
        }
        
        // Use multifile analysis if available
        ApexMultifileAnalysis multifile = ctx.getLanguageProcessor().getMultiFileState();
        if (!multifile.isFailed()) {
            List<Issue> issues = multifile.getFileIssues(ctx.getFileDisplayName());
            for (Issue issue : issues) {
                if (issue.getSeverity() == Severity.ERROR) {
                    ctx.addViolation(node, "Multifile analysis error: {0}", issue.getMessage());
                }
            }
        }
        
        return super.visit(node, data);
    }
}

// Rule that checks for test class patterns
public class TestClassValidationRule extends AbstractApexUnitTestRule {
    @Override
    public Object visit(ASTUserClass node, Object data) {
        if (isTestClass(node)) {
            RuleContext ctx = (RuleContext) data;
            
            // Check for test methods
            List<ASTMethod> methods = node.findDescendantsOfType(ASTMethod.class);
            boolean hasTestMethods = methods.stream()
                .anyMatch(method -> method.getName().toLowerCase().contains("test"));
            
            if (!hasTestMethods) {
                ctx.addViolation(node, "Test class should contain test methods");
            }
            
            // Check for assertions in test methods
            for (ASTMethod method : methods) {
                if (method.getName().toLowerCase().contains("test")) {
                    List<ASTMethodCallExpression> calls = method.findDescendantsOfType(ASTMethodCallExpression.class);
                    boolean hasAssertions = calls.stream()
                        .anyMatch(call -> call.getMethodName().startsWith("assert"));
                    
                    if (!hasAssertions) {
                        ctx.addViolation(method, "Test method should contain assertions");
                    }
                }
            }
        }
        
        return super.visit(node, data);
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-net-sourceforge-pmd--pmd-apex

docs

ast.md

cpd.md

index.md

language-module.md

metrics.md

multifile.md

rules.md

tile.json