avoid_default_tostring
This rule flags string interpolation of a value whose class does not override toString.
Object.toString returns Instance of 'Foo' — the one thing the reader of a log already knows. It bites hardest in the error message written to diagnose a failure, or the log line captured from production, which turns out to say nothing.
This rule is in the opinionated preset and works with its defaults; no configuration is needed.
class User { const User(this.id, this.email);
final String id; final String email;}
void logFailure(User user, void Function(String) log) { // Logs: "upload failed for Instance of 'User'" log('upload failed for $user');}Either give the class a toString, so every interpolation of it reads:
class User { const User(this.id, this.email);
final String id; final String email;
@override String toString() => 'User(id: $id, email: $email)';}
void logFailure(User user, void Function(String) log) { // Logs: "upload failed for User(id: u_17, email: a@b.com)" log('upload failed for $user');}…or interpolate the fields the message actually needs:
void logFailure(User user, void Function(String) log) { log('upload failed for ${user.id}');}More examples
Section titled “More examples”A thrown exception, interpolated into its own message
Section titled “A thrown exception, interpolated into its own message”The most expensive version: the message is built precisely so a human can read it.
class ParseFailure implements Exception { const ParseFailure(this.line);
final int line;}
// Don't — the throw site says "Instance of 'ParseFailure'".Never rethrowWithContext(ParseFailure failure) => throw StateError('import aborted: $failure');class ParseFailure implements Exception { const ParseFailure(this.line);
final int line;
@override String toString() => 'ParseFailure(line $line)';}
Never rethrowWithContext(ParseFailure failure) => throw StateError('import aborted: $failure');Inherited toString is enough
Section titled “Inherited toString is enough”The check walks the whole supertype chain, so a base class that overrides toString covers its subclasses:
abstract class Failure { const Failure(this.code);
final String code;
@override String toString() => '$runtimeType($code)';}
class TimeoutFailure extends Failure { const TimeoutFailure() : super('timeout');}
// Not reported — TimeoutFailure inherits Failure.toString.String describe(TimeoutFailure failure) => 'failed: $failure';Enums are exempt unless you opt in
Section titled “Enums are exempt unless you opt in”Status.active already reads well, so enums are skipped by default. Turn report_enums: true on if you want them spelled out:
rules: avoid_default_tostring: report_enums: trueenum Status { active, archived }
// Reported only with `report_enums: true`.String label(Status status) => 'status: $status';Known limitations
Section titled “Known limitations”Only classes declared in the analysed code are reported. An SDK type without a toString override is not the user’s to fix, so it is skipped.
Enums, records, and core types render usefully by default and are never reported. report_enums: true widens the rule to enums only.
Options
Section titled “Options”many_lints: rules: avoid_default_tostring: report_enums: truerules: avoid_default_tostring: report_enums: true| Option | Type | Default | Description |
|---|---|---|---|
report_enums |
bool | false |
Also report interpolated enums without a toString override. Off by default because Status.active already reads well |
Turning this rule off
Section titled “Turning this rule off”This rule is in the opinionated preset, so it is on with
preset: opinionated, or by name:
rules: avoid_default_tostring: trueTo turn it off again:
rules: avoid_default_tostring: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”avoid_accessing_other_classes_private_members— Make the underscore mean what everyone reads it as.avoid_commented_out_code— Detect and flag commented-out code.avoid_complex_conditions— Keep boolean conditions within an operand budget.avoid_deep_nesting— Keep control flow within a nesting budget.