-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Denis Bogatirov
committed
Sep 20, 2023
1 parent
3e2f9fc
commit 0674bc1
Showing
6 changed files
with
165 additions
and
2 deletions.
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
lib/lints/prefer_match_file_name/models/declaration_token_info.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/dart/ast/token.dart'; | ||
|
||
/// Data class represents declaration token and declaration parent node | ||
class DeclarationTokeInfo { | ||
/// Declaration token | ||
final Token token; | ||
|
||
/// Declaration parent node | ||
final AstNode parent; | ||
|
||
/// Creates instance of [DeclarationTokeInfo] | ||
const DeclarationTokeInfo(this.token, this.parent); | ||
} |
74 changes: 74 additions & 0 deletions
74
lib/lints/prefer_match_file_name/prefer_match_file_name_rule.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import 'package:analyzer/error/listener.dart'; | ||
import 'package:custom_lint_builder/custom_lint_builder.dart'; | ||
import 'package:path/path.dart' as p; | ||
import 'package:solid_lints/lints/prefer_match_file_name/prefer_match_file_name_visitor.dart'; | ||
import 'package:solid_lints/models/rule_config.dart'; | ||
import 'package:solid_lints/models/solid_lint_rule.dart'; | ||
import 'package:solid_lints/utils/node_utils.dart'; | ||
|
||
/// A `prefer-match-file-name` rule which warns about | ||
/// mismatch between file name and declared element inside | ||
class PreferMatchFileNameRule extends SolidLintRule { | ||
/// The [LintCode] of this lint rule that represents the error if iterable | ||
/// access can be simplified. | ||
static const String lintName = 'prefer-match-file-name'; | ||
static final _onlySymbolsRegex = RegExp('[^a-zA-Z0-9]'); | ||
|
||
PreferMatchFileNameRule._(super.config); | ||
|
||
/// Creates a new instance of [PreferMatchFileNameRule] | ||
/// based on the lint configuration. | ||
factory PreferMatchFileNameRule.createRule(CustomLintConfigs configs) { | ||
final config = RuleConfig( | ||
configs: configs, | ||
name: lintName, | ||
problemMessage: (value) => | ||
'File name does not match with first declared element name.', | ||
); | ||
|
||
return PreferMatchFileNameRule._(config); | ||
} | ||
|
||
@override | ||
void run( | ||
CustomLintResolver resolver, | ||
ErrorReporter reporter, | ||
CustomLintContext context, | ||
) { | ||
context.registry.addCompilationUnit((node) { | ||
final visitor = PreferMatchFileNameVisitor(); | ||
|
||
node.accept(visitor); | ||
|
||
if (visitor.declarations.isEmpty) return; | ||
|
||
final info = visitor.declarations.first; | ||
if (!_hasMatchName(resolver.source.fullName, info.token.lexeme)) { | ||
final nodeType = humanReadableNodeType(info.parent).toLowerCase(); | ||
|
||
reporter.reportErrorForToken( | ||
LintCode( | ||
name: lintName, | ||
problemMessage: | ||
'File name does not match with first $nodeType name.', | ||
), | ||
info.token, | ||
); | ||
} | ||
}); | ||
} | ||
|
||
bool _hasMatchName(String path, String identifierName) { | ||
final identifierNameFormatted = | ||
identifierName.replaceAll(_onlySymbolsRegex, '').toLowerCase(); | ||
|
||
final fileNameFormatted = p | ||
.basename(path) | ||
.split('.') | ||
.first | ||
.replaceAll(_onlySymbolsRegex, '') | ||
.toLowerCase(); | ||
|
||
return identifierNameFormatted == fileNameFormatted; | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
lib/lints/prefer_match_file_name/prefer_match_file_name_visitor.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/dart/ast/visitor.dart'; | ||
import 'package:solid_lints/lints/prefer_match_file_name/models/declaration_token_info.dart'; | ||
|
||
/// The AST visitor that will collect all Class, Enum, Extension and Mixin | ||
/// declarations | ||
class PreferMatchFileNameVisitor extends RecursiveAstVisitor<void> { | ||
final _declarations = <DeclarationTokeInfo>[]; | ||
|
||
/// List of all declarations | ||
Iterable<DeclarationTokeInfo> get declarations => | ||
_declarations..sort(_compareByPrivateType); | ||
|
||
@override | ||
void visitClassDeclaration(ClassDeclaration node) { | ||
super.visitClassDeclaration(node); | ||
|
||
_declarations.add(DeclarationTokeInfo(node.name, node)); | ||
} | ||
|
||
@override | ||
void visitExtensionDeclaration(ExtensionDeclaration node) { | ||
super.visitExtensionDeclaration(node); | ||
|
||
final name = node.name; | ||
if (name != null) { | ||
_declarations.add(DeclarationTokeInfo(name, node)); | ||
} | ||
} | ||
|
||
@override | ||
void visitMixinDeclaration(MixinDeclaration node) { | ||
super.visitMixinDeclaration(node); | ||
|
||
_declarations.add(DeclarationTokeInfo(node.name, node)); | ||
} | ||
|
||
@override | ||
void visitEnumDeclaration(EnumDeclaration node) { | ||
super.visitEnumDeclaration(node); | ||
|
||
_declarations.add(DeclarationTokeInfo(node.name, node)); | ||
} | ||
|
||
int _compareByPrivateType(DeclarationTokeInfo a, DeclarationTokeInfo b) { | ||
final isAPrivate = Identifier.isPrivateName(a.token.lexeme); | ||
final isBPrivate = Identifier.isPrivateName(b.token.lexeme); | ||
if (!isAPrivate && isBPrivate) { | ||
return -1; | ||
} else if (isAPrivate && !isBPrivate) { | ||
return 1; | ||
} | ||
|
||
return a.token.offset.compareTo(b.token.offset); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters