use_existing_variable
v0.4.0 Warning Fix Pattern Matching
This rule flags an expression that repeats, character for character, the initializer of a final or const variable declared earlier in the same block. The quick fix replaces the expression with the variable’s name.
Why use this rule
Section titled “Why use this rule”The repeated copy is the one that gets missed. Change the rule for what counts as a valid order and you update the guard clause, ship it, and leave the same expression in the log line saying something else — a discrepancy nothing in the type system can catch.
See also: Dart patterns
class Order { Order(this.items, this.discount);
final List<String> items; final double discount;
double total() => items.length * 10.0;}
void submit(Order order) { final isEmpty = order.items.isEmpty;
if (order.items.isEmpty) { // LINT: use isEmpty print('nothing to submit'); return; }
final label = 'Order of ${order.items.length}'; print('Order of ${order.items.length}'); // LINT: use label}void submit(Order order) { final isEmpty = order.items.isEmpty;
if (isEmpty) { print('nothing to submit'); return; }
final label = 'Order of ${order.items.length}'; print(label);}More places it fires
Section titled “More places it fires”A second variable with the same initializer
Section titled “A second variable with the same initializer”// Don't — the second declaration recomputes what the first already holdsvoid report(Order order) { final subtotal = order.total() - order.discount; final displayed = order.total() - order.discount; // LINT: use subtotal print('$subtotal / $displayed');}
// Dovoid report(Order order) { final subtotal = order.total() - order.discount; final displayed = subtotal; print('$subtotal / $displayed');}Inside a return, a condition, or an argument
Section titled “Inside a return, a condition, or an argument”The scan covers the whole statement, not just top-level expressions:
// Don'tbool isDiscounted(Order order) { final rate = order.discount / order.total(); return order.discount / order.total() > 0.1; // LINT: use rate}
// Dobool isDiscounted(Order order) { final rate = order.discount / order.total(); return rate > 0.1;}Known limitations
Section titled “Known limitations”The rule compares source text, so it cannot tell a value that was already computed from one that is deliberately produced afresh. It stays deliberately silent in these cases:
| Not reported | Why |
|---|---|
var variables |
The value may have been reassigned between the declaration and the repeat, so the two are not interchangeable. |
Constructor calls — Database(file), List<int>.filled(10, 0) |
Each evaluation allocates a new instance. Reusing the variable could hand back an already-closed or already-consumed resource. |
await expressions |
Re-running async work is a separate operation, and the earlier result may be single-use. |
Cascades — []..add(1) |
Each evaluation builds and mutates a distinct object. |
| Literals and bare identifiers | final x = 42; print(42); is not worth a diagnostic. |
| A repeat that appears before the declaration | The variable does not exist yet at that point. |
| A repeat inside a nested function or closure | Different execution context; the value may be stale by the time it runs. |
| A repeat in a different block | Each block is scanned on its own. |
That first constructor exemption is what makes acquire-release-reacquire safe to write:
// Not reported — the second Database is a new connection, not the old onevoid migrate(String file) async { final old = Database(file); await old.close(); final upgraded = Database(file); print(upgraded);}An ordinary method call is still reported: order.total() is assumed to be a
computation you would rather do once. If yours is not — if calling it twice is
the point — silence the line with // ignore: many_lints/use_existing_variable.
Configuration
Section titled “Configuration”This rule is in the opinionated preset, so it is on with
preset: opinionated, or by name:
rules: use_existing_variable: trueTo turn it off again:
rules: use_existing_variable: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”use_existing_destructuring— Add properties to an existing destructuring instead of accessing them directly.avoid_single_field_destructuring— Avoid destructuring a single field when direct property access is simpler.avoid_wildcard_cases_with_enums— Keep exhaustiveness checking by listing enum cases explicitly.prefer_switch_with_enums— Use a switch instead of an if-else chain over enum constants.