match_pattern
Write your own lint as a regular expression, with a quick fix, in many_lints.yaml.
Use it for the conventions no published lint will ever ship: “call our Clock, not DateTime.now()”, “use context.theme, not Theme.of(context)”, “that helper moved”.
This rule is in no preset and reports nothing until you add patterns:. Every example below shows the configuration that produces it.
many_lints: rules: match_pattern: patterns: - find: '^unawaited\((.+)\)$' replace: '$1.unawaited()'rules: match_pattern: patterns: - find: '^unawaited\((.+)\)$' replace: '$1.unawaited()'replace is optional — omit it to report without offering a rewrite.
find is matched against the source text of one call, anchored end to end. $1…$9 in replace are its capture groups.
Examples
Section titled “Examples”Swap a free function for your extension
Section titled “Swap a free function for your extension”- find: '^unawaited\((.+)\)$' replace: '$1.unawaited()' message: 'Use the trailing form from lib/src/extensions.'// Don'tunawaited(cart.refresh());
// Do — offered as a quick fixcart.refresh().unawaited();Force a testable clock
Section titled “Force a testable clock”- find: '^DateTime\.now\(\)$' replace: 'clock.now()' message: 'Inject the Clock seam so tests can freeze time.'// Don'tfinal startedAt = DateTime.now();
// Dofinal startedAt = clock.now();Report without rewriting
Section titled “Report without rewriting”Omit replace when the fix needs a human decision:
- find: '^jsonDecode\(.+\)$' message: 'Parse through ApiCodec so errors are typed.'// Reported, but nothing is auto-appliedfinal data = jsonDecode(body);Rewrite a trailing property
Section titled “Rewrite a trailing property”A call match ends at the closing bracket. To reach .textTheme, match the
property instead:
- node: propertyAccess find: '^Theme\.of\(context\)\.textTheme$' replace: 'context.textTheme'// Don'tfinal titleStyle = Theme.of(context).textTheme;
// Dofinal titleStyle = context.textTheme;Apply a rule to part of the tree
Section titled “Apply a rule to part of the tree”in: takes globs, so a convention can be scoped to the layer it belongs to:
- find: '^print\((.+)\)$' replace: 'logger.debug($1)' in: ['lib/**'] # leave tool/ and test/ aloneMigrate a renamed helper
Section titled “Migrate a renamed helper”- find: '^formatMoney\((.+), (.+)\)$' replace: 'Money($1).format($2)' message: 'formatMoney moved to Money.format in 2.0.'// Don'tfinal label = formatMoney(total, locale);
// Dofinal label = Money(total).format(locale);Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
patterns |
list of entries | [] |
The patterns to match |
Per entry:
| Key | Type | Default | Description |
|---|---|---|---|
find |
string | required | Regular expression, anchored to the whole node’s source text |
replace |
string | — | Quick-fix template. $1…$9 are capture groups, $0 the whole match, $$ a literal $. Omit to report only |
node |
string | methodInvocation |
methodInvocation (a call) or propertyAccess (a property read) |
message |
string | — | Appended to the diagnostic |
in |
list of globs | everywhere | Limits where the entry applies |
Known limitations
Section titled “Known limitations”Text, not types. unawaited from any library looks identical, so a pattern cannot tell the SDK function from your own helper of the same name.
Formatting counts. A call split across lines will not match a pattern written on one.
Imports are yours. A replacement naming a symbol that is not imported produces code that parses but does not compile.
The fix is never bulk-applied — dart fix --apply will not sweep a hand-written regex across the project — and a replacement that would not parse is dropped rather than offered. A malformed entry (bad regex, unknown node:) is skipped silently, since a plugin cannot report against a YAML file.
Related rules
Section titled “Related rules”match_class_name_pattern— Match class names against a regular expression.banned_usage— Ban specific members, such as DateTime.now, optionally scoped by directory.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.