Skip to content

prefer_match_file_name

v1.0.0WarningConfigurableCode Organization

Flags a file whose name does not match the first public declaration in it: user_repository.dart should declare class UserRepository.

The SDK’s file_names rule validates the spelling of a file name but never checks whether it describes the contents. Matching them is what lets a reader find a type from a directory listing, and what makes renaming a type show up in review as a rename of its file rather than as an unrelated edit buried in a diff.

This rule is in the pedantic preset. It works out of the box; enable it in any other preset by name:

many_lints.yaml
rules:
prefer_match_file_name:
enabled: true

See also: file_names, Effective Dart: naming

// In a file named repo.dart:
class UserRepository {}
// In a file named user_repository.dart:
class UserRepository {}

Only the first public declaration is checked

Section titled “Only the first public declaration is checked”

A file legitimately holds several — a class plus its extension, a sealed hierarchy — and only one of them can name the file, so the rest are not evidence of a problem:

// user_repository.dart — accepted
class UserRepository {}
// Not reported: the first public declaration already named the file.
extension UserRepositoryCaching on UserRepository {}
class UserRepositoryException implements Exception {}

The scan looks for the first public declaration, so a private helper at the top does not become the file’s name:

// user_repository.dart — accepted
class _CacheEntry {}
class UserRepository {}

HTTPClient maps to http_client.dart, not h_t_t_p_client.dart:

// http_client.dart — accepted
class HTTPClient {}
// api_v2_gateway.dart — accepted
class APIV2Gateway {}

Entrypoint functions are skipped, not reported

Section titled “Entrypoint functions are skipped, not reported”

main, onRequest and middleware are names the language or a framework demands, which therefore cannot name the file. They are skipped and the scan moves to the next declaration:

// user_repository_test.dart — accepted; `main` is skipped, and there is
// no other public declaration to name the file.
void main() {
// ...
}

Add your own with additional_entrypoints, which extends the defaults instead of replacing them:

analysis_options.yaml
many_lints:
rules:
prefer_match_file_name:
additional_entrypoints: [buildTransaction]
analysis_options.yaml
many_lints:
rules:
prefer_match_file_name:
ignored_suffixes: ['.g.dart', '.freezed.dart']

Generated files are usually better handled with the global analyzer: exclude:, which stops them being analyzed at all. Reach for ignored_suffixes when you still want other rules to see them.

Option Type Default Description
ignored_suffixes list of strings [] File-name suffixes the rule skips entirely, matched with the extension included
entrypoints list of strings [main, onRequest, middleware] Function names a framework demands, which never name a file. Setting this replaces the defaults
additional_entrypoints list of strings [] Entrypoints to add to the defaults, instead of restating them

A part file is never reported. Its name belongs to the composite it is part of (_header.dart inside profile_page/) rather than to its own declaration.

A file with no public declaration is never reported. A private-only file has no name to match, and a barrel of export directives declares nothing at all.

An anonymous extension cannot name a file, so it is skipped and the scan continues to the next declaration.

Only top-level declarations count. A class nested in nothing but a public variable or a top-level const is not a declaration this rule reads — fields and variables are not scanned.

No quick fix. Fixing this means renaming either the file or the declaration, and which one is right is the question the diagnostic is asking.

To disable this rule:

many_lints.yaml
rules:
prefer_match_file_name: false

To keep the rule on but skip certain paths, use per-rule exclude.