-
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.
Merge branch 'master' into use_reverse_if_to_reduce_nesting
- Loading branch information
Showing
20 changed files
with
413 additions
and
11 deletions.
There are no files selected for viewing
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
67 changes: 67 additions & 0 deletions
67
lib/src/lints/avoid_final_with_getter/avoid_final_with_getter_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,67 @@ | ||
import 'package:analyzer/error/listener.dart'; | ||
import 'package:custom_lint_builder/custom_lint_builder.dart'; | ||
import 'package:solid_lints/src/lints/avoid_final_with_getter/visitors/avoid_final_with_getter_visitor.dart'; | ||
import 'package:solid_lints/src/models/rule_config.dart'; | ||
import 'package:solid_lints/src/models/solid_lint_rule.dart'; | ||
|
||
/// Avoid using final private fields with getters. | ||
/// | ||
/// Final private variables used in a pair with a getter | ||
/// must be changed to a final public type without a getter | ||
/// because it is the same as a public field. | ||
/// | ||
/// ### Example | ||
/// | ||
/// #### BAD: | ||
/// | ||
/// ```dart | ||
/// class MyClass { | ||
/// final int _myField = 0; | ||
/// | ||
/// int get myField => _myField; | ||
/// } | ||
/// ``` | ||
/// | ||
/// #### GOOD: | ||
/// | ||
/// ```dart | ||
/// class MyClass { | ||
/// final int myField = 0; | ||
/// } | ||
/// ``` | ||
/// | ||
class AvoidFinalWithGetterRule extends SolidLintRule { | ||
/// The [LintCode] of this lint rule that represents | ||
/// the error whether we use final private fields with getters. | ||
static const lintName = 'avoid_final_with_getter'; | ||
|
||
AvoidFinalWithGetterRule._(super.config); | ||
|
||
/// Creates a new instance of [AvoidFinalWithGetterRule] | ||
/// based on the lint configuration. | ||
factory AvoidFinalWithGetterRule.createRule(CustomLintConfigs configs) { | ||
final rule = RuleConfig( | ||
configs: configs, | ||
name: lintName, | ||
problemMessage: (_) => 'Avoid final private fields with getters.', | ||
); | ||
|
||
return AvoidFinalWithGetterRule._(rule); | ||
} | ||
|
||
@override | ||
void run( | ||
CustomLintResolver resolver, | ||
ErrorReporter reporter, | ||
CustomLintContext context, | ||
) { | ||
context.registry.addCompilationUnit((node) { | ||
final visitor = AvoidFinalWithGetterVisitor(); | ||
node.accept(visitor); | ||
|
||
for (final getter in visitor.getters) { | ||
reporter.reportErrorForNode(code, getter); | ||
} | ||
}); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
lib/src/lints/avoid_final_with_getter/visitors/avoid_final_with_getter_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,33 @@ | ||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/dart/ast/visitor.dart'; | ||
import 'package:analyzer/dart/element/element.dart'; | ||
import 'package:solid_lints/src/lints/avoid_final_with_getter/visitors/getter_variable_visitor.dart'; | ||
|
||
/// A visitor that checks for final private fields with getters. | ||
/// If a final private field has a getter, it is considered as a public field. | ||
class AvoidFinalWithGetterVisitor extends RecursiveAstVisitor<void> { | ||
final _getters = <MethodDeclaration>[]; | ||
|
||
/// List of getters | ||
Iterable<MethodDeclaration> get getters => _getters; | ||
|
||
@override | ||
void visitMethodDeclaration(MethodDeclaration node) { | ||
if (node | ||
case MethodDeclaration( | ||
isGetter: true, | ||
declaredElement: ExecutableElement( | ||
isAbstract: false, | ||
isPublic: true, | ||
) | ||
)) { | ||
final visitor = GetterVariableVisitor(node); | ||
node.parent?.accept(visitor); | ||
|
||
if (visitor.hasVariable) { | ||
_getters.add(node); | ||
} | ||
} | ||
super.visitMethodDeclaration(node); | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
lib/src/lints/avoid_final_with_getter/visitors/getter_variable_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,46 @@ | ||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/dart/ast/visitor.dart'; | ||
import 'package:analyzer/dart/element/element.dart'; | ||
|
||
/// A visitor that checks the association of the getter with | ||
/// the final private variable | ||
class GetterVariableVisitor extends RecursiveAstVisitor<void> { | ||
final int? _getterId; | ||
VariableDeclaration? _variable; | ||
|
||
/// Creates a new instance of [GetterVariableVisitor] | ||
GetterVariableVisitor(MethodDeclaration getter) | ||
: _getterId = getter.getterReferenceId; | ||
|
||
/// Is there a variable associated with the getter | ||
bool get hasVariable => _variable != null; | ||
|
||
@override | ||
void visitVariableDeclaration(VariableDeclaration node) { | ||
if (node | ||
case VariableDeclaration( | ||
isFinal: true, | ||
declaredElement: VariableElement(id: final id, isPrivate: true) | ||
) when id == _getterId) { | ||
_variable = node; | ||
} | ||
|
||
super.visitVariableDeclaration(node); | ||
} | ||
} | ||
|
||
extension on MethodDeclaration { | ||
int? get getterReferenceId => switch (body) { | ||
ExpressionFunctionBody( | ||
expression: SimpleIdentifier( | ||
staticElement: Element( | ||
declaration: PropertyAccessorElement( | ||
variable: PropertyInducingElement(:final id) | ||
) | ||
) | ||
) | ||
) => | ||
id, | ||
_ => null, | ||
}; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import 'package:analyzer/dart/analysis/utilities.dart'; | ||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/dart/ast/visitor.dart'; | ||
|
||
/// Parses the provided type string to extract a [NamedType]. | ||
NamedType parseNamedTypeFromString(String typeString) { | ||
try { | ||
final namedTypeFinder = _NamedTypeFinder(); | ||
|
||
final parseResult = parseString(content: "$typeString _;"); | ||
parseResult.unit.visitChildren(namedTypeFinder); | ||
|
||
return namedTypeFinder.foundNamedType!; | ||
} catch (_) { | ||
throw Exception("No NamedType could be parsed from the input " | ||
"typeString: '$typeString'. Ensure it's a valid Dart " | ||
"type declaration."); | ||
} | ||
} | ||
|
||
class _NamedTypeFinder extends GeneralizingAstVisitor<void> { | ||
NamedType? _foundNamedType; | ||
|
||
NamedType? get foundNamedType => _foundNamedType; | ||
|
||
@override | ||
void visitNamedType(NamedType namedType) { | ||
_foundNamedType ??= namedType; | ||
} | ||
} | ||
|
||
/// | ||
extension ChildNamedTypes on NamedType { | ||
/// Retrieves child [NamedType] instances from type arguments. | ||
List<NamedType> get childNamedTypes => | ||
typeArguments?.arguments.whereType<NamedType>().toList() ?? []; | ||
|
||
/// Gets the token name of this type instance. | ||
String get tokenName => name2.toString(); | ||
|
||
/// Checks if the current token name is 'dynamic'. | ||
bool get isDynamic => tokenName == "dynamic"; | ||
|
||
/// Checks if the current token name is 'Object'. | ||
bool get isObject => tokenName == "Object"; | ||
|
||
/// Checks if this node is a subtype of the specified node | ||
/// based on their structures. | ||
bool isSubtypeOf({required NamedType node}) { | ||
if (isDynamic || isObject) return true; | ||
|
||
if (tokenName != node.tokenName) return false; | ||
|
||
if (childNamedTypes.isEmpty) return true; | ||
|
||
if (childNamedTypes.length != node.childNamedTypes.length) return false; | ||
|
||
for (int i = 0; i < childNamedTypes.length; i++) { | ||
if (!childNamedTypes[i].isSubtypeOf(node: node.childNamedTypes[i])) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// ignore_for_file: type_annotate_public_apis, prefer_match_file_name, unused_local_variable | ||
|
||
/// Check final private field with getter fail | ||
/// `avoid_final_with_getter` | ||
class Fail { | ||
final int _myField = 0; | ||
|
||
// expect_lint: avoid_final_with_getter | ||
int get myField => _myField; | ||
} | ||
|
||
class FailOtherName { | ||
final int _myField = 0; | ||
|
||
// expect_lint: avoid_final_with_getter | ||
int get myFieldInt => _myField; | ||
} | ||
|
||
class FailStatic { | ||
static final int _myField = 0; | ||
|
||
// expect_lint: avoid_final_with_getter | ||
static int get myField => _myField; | ||
} | ||
|
||
class Skip { | ||
final int _myField = 0; | ||
|
||
int get myField => _myField + 1; // it is not a getter for the field | ||
} | ||
|
||
class Good { | ||
final int myField = 0; | ||
} |
File renamed without changes.
File renamed without changes.
Oops, something went wrong.