avoid-missing-interpolation

Path
lint/correctness/avoid-missing-interpolation
Default
On (recommended)
Origin
Falcon
Rule
88 of 512
recommended

Flags missing interpolation around visible String variables or member accesses and calls.

A plain string equal to an in-scope variable name may print the name instead of its value. This check requires a positional argument of an unqualified call and a local binding with a declared nonnullable dart:core String type. Constructors, receiver methods, annotations, named arguments, raw/escaped/adjacent strings, and calls whose arguments already read that variable are excluded. Keys in maps, indexes, comparisons, and patterns are not positional arguments.

In '$object.property' and '$function(value)', only the identifier is interpolated. The rule requires the receiver to have the named readable member, or a known callable type, before suggesting ${...}. Unknown members, dynamic receivers, filenames such as 'assets/$name.png', and string-valued method names in logs are left alone. Raw and escaped dollars and braced interpolations are never flagged. Reports the first incomplete interpolation in each segment, rather than an entire multiline literal.

Invalid

example.dartdart
class Label {
  final String value;
  const Label(this.value);
}

String render(String value) => value;

void example(Label label, String value) {
  print('value: $label.value');
  print("empty: $label.value.isEmpty");
  print('$render(value)');
  print('rendered: $render()');
  print('''$label.value''');
  print('prefix ' '$label.value');
  print('$value.length and $label.value');
  print('backslash: \\ $label.value');
  print("${'$label.value'}");
  print('''prefix
$label.value''');
  print('value');
}

void localNames(String parameter) {
  print('parameter');
  final String title = 'Heading';
  print('title');
  String mutable = 'Current';
  print("mutable");
  const String constant = 'Fixed';
  print('constant');
  {
    final String nested = 'Inner';
    print('nested');
  }
}
Interpolation leaves a member access or call as literal text; wrap the complete expression in `${...}`.
8void example(Label label, String value) {
9 print('value: $label.value');
Interpolation leaves a member access or call as literal text; wrap the complete expression in `${...}`.
9 print('value: $label.value');
10 print("empty: $label.value.isEmpty");
Interpolation leaves a member access or call as literal text; wrap the complete expression in `${...}`.
10 print("empty: $label.value.isEmpty");
11 print('$render(value)');
Interpolation leaves a member access or call as literal text; wrap the complete expression in `${...}`.
11 print('$render(value)');
12 print('rendered: $render()');
Interpolation leaves a member access or call as literal text; wrap the complete expression in `${...}`.
12 print('rendered: $render()');
13 print('''$label.value''');
Interpolation leaves a member access or call as literal text; wrap the complete expression in `${...}`.
13 print('''$label.value''');
14 print('prefix ' '$label.value');
Interpolation leaves a member access or call as literal text; wrap the complete expression in `${...}`.
14 print('prefix ' '$label.value');
15 print('$value.length and $label.value');
Interpolation leaves a member access or call as literal text; wrap the complete expression in `${...}`.
15 print('$value.length and $label.value');
16 print('backslash: \\ $label.value');
Interpolation leaves a member access or call as literal text; wrap the complete expression in `${...}`.
16 print('backslash: \\ $label.value');
17 print("${'$label.value'}");
Interpolation leaves a member access or call as literal text; wrap the complete expression in `${...}`.
18 print('''prefix
19$label.value''');
This string is a visible `String` variable's name; use the variable or interpolate its value.
19$label.value''');
20 print('value');
This string is a visible `String` variable's name; use the variable or interpolate its value.
23void localNames(String parameter) {
24 print('parameter');
This string is a visible `String` variable's name; use the variable or interpolate its value.
25 final String title = 'Heading';
26 print('title');
This string is a visible `String` variable's name; use the variable or interpolate its value.
27 String mutable = 'Current';
28 print("mutable");
This string is a visible `String` variable's name; use the variable or interpolate its value.
29 const String constant = 'Fixed';
30 print('constant');
This string is a visible `String` variable's name; use the variable or interpolate its value.
32 final String nested = 'Inner';
33 print('nested');

Valid

example.dartdart
class Label {
  final String value;
  const Label(this.value);
}

String render(String value) => value;

void example(Label label, String value) {
  print('value: ${label.value}');
  print('${render(value)}');
  print(r'$label.value');
  print(r'$render(value)');
  print('\$label.value');
  print('\$render(value)');
  print('$label.');
  print('$label...');
  print('$value.42');
  print('$label .value');
  print('$render (value)');
  print('${label}.value');
  print('${render}(value)');
  print('$value');
  print('literal label');
  print('''literal \$label.value''');
  print('$label' '.value');
  print(r'$label.value' '$value');
  print("${'$label'}.value");
  print('${{"closing": "}"}}.value');
  print('$label\u002evalue');
  print('$render\u0028value)');
  final String name = 'avatar';
  final String domain = 'example';
  final String methodName = 'render';
  print('assets/$name.png');
  print('user@$domain.com');
  print('logs $methodName()');
  print(r'value');
  print('\u0076alue');
  print('value' ' suffix');
  print('$label.unknown');
  print('$value.png');
  print('''
${label.value}.length
''');
}

void shadowing(String title, String? optional, dynamic unknown) {
  {
    final int title = 42;
    print('title');
  }
  print('optional');
  print('unknown');
  print('notInScope');
}

class Key {
  final String value;
  const Key(this.value);
}

class Preferences {
  void setString(String key, String value) {}
}

void named({required String key}) {}
void consume(Object first, Object second) {}

void keys(Preferences prefs, Map<String, String> json, String key) {
  final String email = 'address';
  final String token = 'secret';
  final String name = 'person';
  final fields = {'email': email};
  prefs.setString('token', token);
  print(json['name']);
  print(key == 'name');
  switch (key) {
    case 'name':
      break;
  }
  print(Key('email'));
  print(const Key('email'));
  named(key: 'email');
  consume('email', email.length);
  consume('email', '$email');
  final String assignment = 'name';
  print(fields);
  print(assignment);
  @Key('email')
  void annotated() {}
}

String returnName(String name) => 'name';

class Cache {
  String? get(String key) => null;
  void remove(String key) {}
}
void storageKeys(Cache prefs, String token, String id) {
  prefs.remove('token');
  prefs.get('id');
}

Configure avoid-missing-interpolation

Set its severity in falcon.json. See the configuration guide.

falcon.jsonjson
{
  "linter": {
    "rules": {
      "correctness": {
        "avoid-missing-interpolation": "error"
      }
    }
  }
}

Or silence one occurrence with a suppression comment; the reason is required.

example.dartdart
// falcon-ignore lint/correctness/avoid-missing-interpolation: <reason>