Skip to content

Commit

Permalink
Merge branch 'master' into 149-new-lint-avoid_final_with_getter
Browse files Browse the repository at this point in the history
  • Loading branch information
4akloon committed Apr 18, 2024
2 parents 653a7d1 + 133f96c commit 680359c
Show file tree
Hide file tree
Showing 14 changed files with 223 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## 0.2.0

- Added `avoid_final_with_getter` rule
- Improve `avoid_late_keyword` - `ignored_types` to support ignoring subtype of the node type (https://github.com/solid-software/solid_lints/issues/157)


## 0.1.5

Expand Down
8 changes: 3 additions & 5 deletions lib/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,8 @@ class AvoidLateKeywordRule extends SolidLintRule<AvoidLateKeywordParameters> {
final variableType = node.declaredElement?.type;
if (variableType == null) return false;

final checkedTypes = [variableType, ...variableType.supertypes]
.map((t) => t.getDisplayString(withNullability: false))
.toSet();

return checkedTypes.intersection(ignoredTypes).isNotEmpty;
return variableType.hasIgnoredType(
ignoredTypes: ignoredTypes,
);
}
}
6 changes: 4 additions & 2 deletions lib/src/lints/proper_super_calls/proper_super_calls_rule.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,11 @@ class ProperSuperCallsRule extends SolidLintRule {
context.registry.addMethodDeclaration(
(node) {
final methodName = node.name.toString();
final body = node.body;

if (methodName == _initState || methodName == _dispose) {
final statements = (node.body as BlockFunctionBody).block.statements;
if (methodName case _initState || _dispose
when body is BlockFunctionBody) {
final statements = body.block.statements;

_checkSuperCalls(
node,
Expand Down
66 changes: 66 additions & 0 deletions lib/src/utils/named_type_utils.dart
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;
}
}
49 changes: 49 additions & 0 deletions lib/src/utils/types_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,65 @@
// SOFTWARE.
// ignore_for_file: public_member_api_docs

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:collection/collection.dart';
import 'package:solid_lints/src/utils/named_type_utils.dart';

extension Subtypes on DartType {
Iterable<DartType> get supertypes {
final element = this.element;
return element is InterfaceElement ? element.allSupertypes : [];
}

/// Formats the type string based on nullability and presence of generics.
String getTypeString({
required bool withGenerics,
required bool withNullability,
}) {
final displayString = getDisplayString(withNullability: withNullability);

return withGenerics ? displayString : displayString.replaceGenericString();
}

/// Parses a [NamedType] instance from current type.
NamedType getNamedType() {
final typeString = getTypeString(
withGenerics: true,
withNullability: false,
);

return parseNamedTypeFromString(typeString);
}

/// Checks if a variable type is among the ignored types.
bool hasIgnoredType({required Set<String> ignoredTypes}) {
if (ignoredTypes.isEmpty) return false;

final checkedTypeNodes = [this, ...supertypes].map(
(type) => type.getNamedType(),
);

final ignoredTypeNodes = ignoredTypes.map(parseNamedTypeFromString);

for (final ignoredTypeNode in ignoredTypeNodes) {
for (final checkedTypeNode in checkedTypeNodes) {
if (ignoredTypeNode.isSubtypeOf(node: checkedTypeNode)) {
return true;
}
}
}

return false;
}
}

extension TypeString on String {
static final _genericRegex = RegExp('<.*>');

String replaceGenericString() => replaceFirst(_genericRegex, '');
}

bool hasWidgetType(DartType type) =>
Expand Down
3 changes: 0 additions & 3 deletions lint_test/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ custom_lint:
- avoid_non_null_assertion
- avoid_late_keyword:
allow_initialized: true
ignored_types:
- ColorTween
- AnimationController
- avoid_global_state
- avoid_returning_widgets
- avoid_unnecessary_setstate
Expand Down
10 changes: 10 additions & 0 deletions lint_test/avoid_late_keyword/no_generics/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
analyzer:
plugins:
- ../custom_lint

custom_lint:
rules:
- avoid_late_keyword:
allow_initialized: false
ignored_types:
- Subscription
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// ignore_for_file: prefer_const_declarations, unused_local_variable, prefer_match_file_name
// ignore_for_file: avoid_global_state

class Subscription<T> {}

class ConcreteTypeWithNoGenerics {}

class NotAllowed {}

/// Check "late" keyword fail
///
/// `avoid_late_keyword`
/// allow_initialized option disabled
class AvoidLateKeyword {
/// expect_lint: avoid_late_keyword
late final NotAllowed na1;

late final Subscription subscription1;

late final Subscription<ConcreteTypeWithNoGenerics> subscription2;

late final Subscription<List<int>> subscription3;

late final Subscription<List<List<int>>> subscription4;

late final Subscription<Map<dynamic, String>> subscription5;

void test() {
/// expect_lint: avoid_late_keyword
late final NotAllowed na1;

late final Subscription subscription1;

late final Subscription<ConcreteTypeWithNoGenerics> subscription2;

late final Subscription<List<int>> subscription3;

late final Subscription<List<List<int>>> subscription4;

late final Subscription<Map<dynamic, String>> subscription5;
}
}
14 changes: 14 additions & 0 deletions lint_test/avoid_late_keyword/with_generics/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
analyzer:
plugins:
- ../custom_lint

custom_lint:
rules:
- avoid_late_keyword:
allow_initialized: true
ignored_types:
- ColorTween
- AnimationController
- Subscription<List<Object?>>
- Subscription<Map<dynamic, String>>
- Subscription<ConcreteTypeWithNoGenerics>
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ class SubAnimationController extends AnimationController {}

class NotAllowed {}

class Subscription<T> {}

class ConcreteTypeWithNoGenerics {}

/// Check "late" keyword fail
///
/// `avoid_late_keyword`
Expand Down Expand Up @@ -37,6 +41,22 @@ class AvoidLateKeyword {

late final na2 = NotAllowed();

/// expect_lint: avoid_late_keyword
late final Subscription<String> subscription1;

late final Subscription<ConcreteTypeWithNoGenerics> subscription2;

late final Subscription<List<String>> subscription3;

late final Subscription<List<List<int>>> subscription4;

late final Subscription<Map<dynamic, String>> subscription5;

late final Subscription<Map<String, String>> subscription6;

/// expect_lint: avoid_late_keyword
late final Subscription<Map<String, dynamic>> subscription7;

void test() {
late final ColorTween colorTween;

Expand All @@ -60,5 +80,12 @@ class AvoidLateKeyword {
late final NotAllowed na1;

late final na2 = NotAllowed();

/// expect_lint: avoid_late_keyword
late final Subscription<String> subscription1;

late final Subscription<ConcreteTypeWithNoGenerics> subscription2;

late final Subscription<List<String>> subscription3;
}
}
5 changes: 5 additions & 0 deletions lint_test/proper_super_calls_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,8 @@ class MyClass {
dispose() {}
initState() {}
}

abstract interface class Disposable {
/// Abstract methods should be omitted by `proper_super_calls`
void dispose();
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: solid_lints
description:
Lints for Dart and Flutter based on software industry standards and best
practices.
version: 0.1.5
version: 0.2.0
homepage: https://github.com/solid-software/solid_lints/
documentation: https://solid-software.github.io/solid_lints/docs/intro
topics: [lints, linter, lint, analysis, analyzer]
Expand Down

0 comments on commit 680359c

Please sign in to comment.