prefer-contains

Same as prefer_contains in Dart lints

Path
lint/performance/prefer-contains
Default
On (recommended)
Origin
Dart lints
Rule
334 of 512
recommended

Prefers contains to comparing the result of indexOf with a constant.

The rule looks at a comparison (==, !=, <, <=, >, >=) of an Iterable or String receiver's indexOf(...) call (optionally behind as casts) with an integer constant <= 0 on the other side: an integer literal, a negated literal or identifier, or an identifier naming an int constant. When the call has exactly two arguments the second (the start index) must be the constant 0. Comparisons with -1 (==, !=, <= and > after normalizing the call to the left) and 0 (>=, <) are membership tests that contains states directly; comparisons that are always true or false (< -1, >= -1, or any comparison with a value below -1) are reported too. A constant on the right is preferred: when the right operand is an integer constant the left operand is not considered.

Invalid

example.dartdart
bool missing(List<String> values, String value) {
  return values.indexOf(value) == -1;
}
bool present(String text, String value) {
  return text.indexOf(value) >= 0;
}
bool defaultValue([bool found = 'abc'.indexOf('a') >= 0]) => found;
bool swappedPresent(List<String> values, String value) {
  return -1 < values.indexOf(value);
}
bool swappedMissing(String text, String value) {
  return 0 <= text.indexOf(value);
}
bool otherForms(String text, String value) {
  if (text.indexOf(value) < 0) return false;
  if (text.indexOf(value) <= -1) return false;
  if (-1 >= text.indexOf(value)) return false;
  return text.indexOf(value) != -1;
}
bool hexBounds(String text, String value) {
  if (text.indexOf(value) != -0x1) return true;
  return text.indexOf(value) >= 0x0;
}

class SearchState {
  final bool found;
  SearchState(String text) : found = text.indexOf('x') >= 0;

  bool get fromGetter {
    String text = 'abc';
    return text.indexOf('a') >= 0;
  }

  set values(List<String> values) {
    values.indexOf('x') >= 0;
  }

  bool operator ==(Object other) {
    String text = other.toString();
    return text.indexOf('x') >= 0;
  }
}

void scoped(List<String> values, List<String> texts) {
  bool local(String text) => text.indexOf('x') >= 0;
  for (String text in texts) {
    text.indexOf('x') >= 0;
  }
  try {
    throw 'x';
  } on String catch (text) {
    text.indexOf('x') >= 0;
  }
  final (String text,) = ('abc',);
  text.indexOf('x') >= 0;
}

const minusOne = -1;
const zero = 0;

bool startArguments(String text, String value) {
  if (text.indexOf(value, 0) >= 0) return true;
  if (text.indexOf(value, zero) >= 0) return true;
  return text.indexOf('x', 1, 2) >= 0;
}

bool constants(List<int> values, String text, List<int>? maybe, Object other) {
  if (values.indexOf(1) == minusOne) return true;
  if (text.indexOf('x') >= zero) return true;
  if (maybe?.indexOf(1) == -1) return true;
  if ((other as List<int>).indexOf(1) == -1) return true;
  return 0 > values.indexOf(1);
}

bool alwaysTrueOrFalse(String text) {
  if (text.indexOf('x') < -1) return true;
  if (text.indexOf('x') >= -1) return true;
  if (text.indexOf('x') == -2) return true;
  return text.indexOf('x') != -2;
}
Use `contains` instead of comparing the result of `indexOf`.
1bool missing(List<String> values, String value) {
2 return values.indexOf(value) == -1;
Use `contains` instead of comparing the result of `indexOf`.
4bool present(String text, String value) {
5 return text.indexOf(value) >= 0;
Use `contains` instead of comparing the result of `indexOf`.
6}
7bool defaultValue([bool found = 'abc'.indexOf('a') >= 0]) => found;
Use `contains` instead of comparing the result of `indexOf`.
8bool swappedPresent(List<String> values, String value) {
9 return -1 < values.indexOf(value);
Use `contains` instead of comparing the result of `indexOf`.
11bool swappedMissing(String text, String value) {
12 return 0 <= text.indexOf(value);
Use `contains` instead of comparing the result of `indexOf`.
14bool otherForms(String text, String value) {
15 if (text.indexOf(value) < 0) return false;
Use `contains` instead of comparing the result of `indexOf`.
15 if (text.indexOf(value) < 0) return false;
16 if (text.indexOf(value) <= -1) return false;
Use `contains` instead of comparing the result of `indexOf`.
16 if (text.indexOf(value) <= -1) return false;
17 if (-1 >= text.indexOf(value)) return false;
Use `contains` instead of comparing the result of `indexOf`.
17 if (-1 >= text.indexOf(value)) return false;
18 return text.indexOf(value) != -1;
Use `contains` instead of comparing the result of `indexOf`.
20bool hexBounds(String text, String value) {
21 if (text.indexOf(value) != -0x1) return true;
Use `contains` instead of comparing the result of `indexOf`.
21 if (text.indexOf(value) != -0x1) return true;
22 return text.indexOf(value) >= 0x0;
Use `contains` instead of comparing the result of `indexOf`.
26 final bool found;
27 SearchState(String text) : found = text.indexOf('x') >= 0;
Use `contains` instead of comparing the result of `indexOf`.
30 String text = 'abc';
31 return text.indexOf('a') >= 0;
Use `contains` instead of comparing the result of `indexOf`.
34 set values(List<String> values) {
35 values.indexOf('x') >= 0;
Use `contains` instead of comparing the result of `indexOf`.
39 String text = other.toString();
40 return text.indexOf('x') >= 0;
Use `contains` instead of comparing the result of `indexOf`.
44void scoped(List<String> values, List<String> texts) {
45 bool local(String text) => text.indexOf('x') >= 0;
Use `contains` instead of comparing the result of `indexOf`.
46 for (String text in texts) {
47 text.indexOf('x') >= 0;
Use `contains` instead of comparing the result of `indexOf`.
51 } on String catch (text) {
52 text.indexOf('x') >= 0;
Use `contains` instead of comparing the result of `indexOf`.
54 final (String text,) = ('abc',);
55 text.indexOf('x') >= 0;
Use `contains` instead of comparing the result of `indexOf`.
61bool startArguments(String text, String value) {
62 if (text.indexOf(value, 0) >= 0) return true;
Use `contains` instead of comparing the result of `indexOf`.
62 if (text.indexOf(value, 0) >= 0) return true;
63 if (text.indexOf(value, zero) >= 0) return true;
Use `contains` instead of comparing the result of `indexOf`.
63 if (text.indexOf(value, zero) >= 0) return true;
64 return text.indexOf('x', 1, 2) >= 0;
Use `contains` instead of comparing the result of `indexOf`.
67bool constants(List<int> values, String text, List<int>? maybe, Object other) {
68 if (values.indexOf(1) == minusOne) return true;
Use `contains` instead of comparing the result of `indexOf`.
68 if (values.indexOf(1) == minusOne) return true;
69 if (text.indexOf('x') >= zero) return true;
Use `contains` instead of comparing the result of `indexOf`.
69 if (text.indexOf('x') >= zero) return true;
70 if (maybe?.indexOf(1) == -1) return true;
Use `contains` instead of comparing the result of `indexOf`.
70 if (maybe?.indexOf(1) == -1) return true;
71 if ((other as List<int>).indexOf(1) == -1) return true;
Use `contains` instead of comparing the result of `indexOf`.
71 if ((other as List<int>).indexOf(1) == -1) return true;
72 return 0 > values.indexOf(1);
This comparison of `indexOf` is always false.
75bool alwaysTrueOrFalse(String text) {
76 if (text.indexOf('x') < -1) return true;
This comparison of `indexOf` is always true.
76 if (text.indexOf('x') < -1) return true;
77 if (text.indexOf('x') >= -1) return true;
This comparison of `indexOf` is always false.
77 if (text.indexOf('x') >= -1) return true;
78 if (text.indexOf('x') == -2) return true;
This comparison of `indexOf` is always true.
78 if (text.indexOf('x') == -2) return true;
79 return text.indexOf('x') != -2;

Valid

example.dartdart
bool present(List<String> values, String value) => values.contains(value);
class Search { int indexOf(Object value) => 0; }
bool custom(Search value) => value.indexOf('x') == -1;
bool listStart(List<String> values, String value) => values.indexOf(value, 1) >= 0;
bool stringNonzeroStart(String text, String value) => text.indexOf(value, 2) >= 0;

const minusOneDouble = -1.0;
bool doubleConstant(String text) => text.indexOf('x') == minusOneDouble;
bool assigned(String text) {
  int index;
  return (index = text.indexOf('x')) != -1;
}
bool positive(String text) => text.indexOf('x') > 0;

class List<E> {
  int indexOf(E value) => 0;
}
bool ambiguousList(List<String> values, String value) => values.indexOf(value) >= 0;

Configure prefer-contains

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

falcon.jsonjson
{
  "linter": {
    "rules": {
      "performance": {
        "prefer-contains": "error"
      }
    }
  }
}

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

example.dartdart
// falcon-ignore lint/performance/prefer-contains: <reason>
  • prefer-correct-edge-insets-constructorprefer-correct-edge-insets-constructorrecommendedflutter

    Flags an EdgeInsets.only(...) that a more specific constructor expresses better.

  • prefer-declaring-const-constructorprefer_declaring_const_constructorflutter

    Flags a generative constructor that is legally eligible to be const.

  • prefer-foreachprefer_foreach

    Prefers forEach to a for-in loop that only passes each element to a function.

  • prefer-return-await

    Await returned SDK Futures inside a try body protected by a catch clause.

  • prefer-single-setstateflutter

    Merge consecutive Flutter State.setState statements in the same block.

  • prefer-use-callbackflutter_hooks

    Prefer useCallback for synchronous memoized function literals.

  • prefer-use-memoflutter_hooks

    Prefer memoizing freshly allocated collection values used as hook keys.

  • prefer-using-list-viewflutter

    Suggests considering ListView for a SingleChildScrollView containing a plain Flutter Column with at least two unconditional children in a list literal.