Local variable is never mentioned after its declaration

ID

apex.maintainability.unused_local_variable

Severity

info

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Dead Code

Language

Apex

Description

Reports a local variable, declared with or without an initialiser, whose name appears nowhere after its declaration in the same method. A mention anywhere in the body counts as a use, including a later assignment, a SOQL bind (WHERE Id = :accountId), a string concatenation, a ternary, a collection literal, a for header, a switch on, an if, while or do condition, a constructor named argument, a Map.put, a return, a throw, a body nested inside try, and a System.runAs block. Names beginning with an underscore are exempt, and loop control variables and class fields are out of scope.

Rationale

A local nothing reads is work the transaction pays for and nobody collects. Where the initialiser is a query, a describe call or a JSON.deserialize, that cost lands against the governor limits of every execution - a SOQL statement the method does not need, heap the transaction is charged for - and the limit is reached sooner for no result. Where the initialiser is cheap, the variable is usually the remains of an edit: the value was being computed for a line that has since been deleted or rewritten, and what is left tells the next reader that something is still using it.

The following code illustrates the pattern detected by this rule:

public class ShipmentReport {

    private Integer neverReadField;

    public static String summarise(List<Shipment__c> shipments) {
        // FLAGGED: Local variable is never mentioned after its declaration
        String header = 'Shipment report';
        Integer total = shipments.size();
        return 'Total: ' + total;
    }

    public static void reconcile(List<Shipment__c> shipments) {
        // FLAGGED: Local variable is never mentioned after its declaration
        Decimal variance;
        for (Shipment__c s : shipments) {
            s.Status__c = 'Reconciled';
        }
        update shipments;
    }

    public static String summariseFixed(List<Shipment__c> shipments) {
        String header = 'Shipment report';
        Integer total = shipments.size();
        return header + ' - total: ' + total;
    }

    public static String describe(Shipment__c s, Boolean verbose) {
        String label = s.Name;
        if (verbose) {
            label = label + ' (' + s.Status__c + ')';
        }
        return label;
    }

    public static Decimal weight(Shipment__c s) {
        Decimal total;
        total = s.Weight__c;
        return total;
    }

    public static void scopedUse(List<Shipment__c> shipments) {
        if (!shipments.isEmpty()) {
            Shipment__c first = shipments[0];
            System.debug(LoggingLevel.INFO, first.Name);
        }
    }

    public static Integer count(List<Shipment__c> shipments) {
        Integer _ignored = 0;
        return shipments.size();
    }
}

Remediation

Delete the declaration along with whatever only fed it. If the value was meant to be used, find the line that should have read it - a deleted assertion, a dropped field assignment - and restore it. Where a variable exists only to receive a value that must be discarded, prefix its name with an underscore to say so.

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved