Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Add an `analysis_options.yaml` file under the `test/` directory, and include the

```yaml
include: package:solid_lints/analysis_options_test.yaml

analyzer:
plugins:
- custom_lint
```

Then you can see suggestions in your IDE or you can run checks manually:
Expand Down
3 changes: 3 additions & 0 deletions lib/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,6 @@ linter:
- use_test_throws_matchers
- valid_regexps
- void_checks

formatter:
trailing_commas: preserve

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The require_trailing_commas lint rule is enabled in this file, which is great for consistency. However, setting trailing_commas to preserve in the formatter configuration might lead to an inconsistent developer experience. preserve will not automatically add trailing commas, so developers might see lints from require_trailing_commas that dart format doesn't fix.

To align the formatter with the lint rule, consider changing preserve to always. This will ensure dart format automatically adds trailing commas, resolving the lint and providing a smoother workflow.

  trailing_commas: always

4 changes: 4 additions & 0 deletions lib/analysis_options_test.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
include: package:solid_lints/analysis_options.yaml

analyzer:
plugins:
- custom_lint

custom_lint:
rules:
# Tests usually organized in one large main() function making this rule not applicable.
Expand Down
102 changes: 102 additions & 0 deletions lib/src/lints/avoid_using_api/avoid_using_api_linter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ class AvoidUsingApiLinter {
required this.config,
});

/// The identifier for the default constructor
static const String _defaultConstructorIdentifier = '()';

/// Access to the resolver for this lint context
final CustomLintResolver resolver;

Expand Down Expand Up @@ -191,6 +194,11 @@ class AvoidUsingApiLinter {
String className,
String source,
) {
if (identifier == _defaultConstructorIdentifier) {
_banDefaultConstructor(className, source, entryCode);
return;
}

context.registry.addSimpleIdentifier((node) {
final name = node.name;
if (name != identifier) {
Expand Down Expand Up @@ -281,4 +289,98 @@ class AvoidUsingApiLinter {
reporter.atNode(node.identifier, entryCode);
});
}

void _banDefaultConstructor(
String className,
String source,
LintCode entryCode,
) {
context.registry.addInstanceCreationExpression((node) {
final constructorName = node.constructorName.type.name2.lexeme;
if (constructorName != className || node.constructorName.name != null) {
return;
}

final sourcePath =
node.constructorName.type.element2?.library2?.uri.toString();
if (sourcePath == null || !_matchesSource(sourcePath, source)) {
return;
}

reporter.atNode(node, entryCode);
});
}

/// Lints usages of a named parameter from a given source
void banUsageWithSpecificNamedParameter(
LintCode entryCode,
String identifier,
String namedParameter,
String className,
String source,
) {
context.registry.addMethodInvocation((node) {
final methodName = node.methodName.name;
if (methodName != identifier) return;

final enclosingElement = node.methodName.element?.enclosingElement2;
if (enclosingElement == null || enclosingElement.name3 != className) {
return;
}

if (!_containsNamedParameter(node.argumentList, namedParameter)) {
return;
}

final libSource = enclosingElement.library2;
if (libSource == null) {
return;
}

final sourcePath = libSource.uri.toString();
if (!_matchesSource(sourcePath, source)) {
return;
}

reporter.atNode(node.methodName, entryCode);
});

context.registry.addInstanceCreationExpression((node) {
String? expectedConstructorName;

if (identifier != _defaultConstructorIdentifier) {
expectedConstructorName = identifier;
}

final actualClassName = node.constructorName.type.name2.lexeme;
if (actualClassName != className) {
return;
}

if (node.constructorName.name?.name != expectedConstructorName) {
return;
}

if (!_containsNamedParameter(node.argumentList, namedParameter)) {
return;
}

final sourcePath =
node.constructorName.type.element2?.library2?.uri.toString();
if (sourcePath == null || !_matchesSource(sourcePath, source)) {
return;
}

reporter.atNode(node, entryCode);
});
}

bool _containsNamedParameter(
ArgumentList argumentList,
String namedParameter,
) =>
argumentList.arguments.any(
(arg) =>
arg is NamedExpression && arg.name.label.name == namedParameter,
);
}
13 changes: 13 additions & 0 deletions lib/src/lints/avoid_using_api/avoid_using_api_rule.dart
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,19 @@ class AvoidUsingApiRule extends SolidLintRule<AvoidUsingApiParameters> {
switch (entry) {
case AvoidUsingApiEntryParameters(:final source) when source == null:
break;
case AvoidUsingApiEntryParameters(
:final identifier?,
:final namedParameter?,
:final className?,
:final source?
):
linter.banUsageWithSpecificNamedParameter(
entryCode,
identifier,
namedParameter,
className,
source,
);
case AvoidUsingApiEntryParameters(
:final identifier?,
:final className?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:solid_lints/src/utils/parameter_utils.dart';
///
/// Parameters:
/// * identifier: Variable/method name
/// * named_parameter: Named parameter of the constructor/method
/// * class_name: Name of the class containing the variable/method
/// * source: Package (e.g., dart:async or package:example)
/// * severity: The default severity of the lint for each entry.
Expand Down Expand Up @@ -35,6 +36,9 @@ class AvoidUsingApiEntryParameters {
/// Variable/method name
final String? identifier;

/// Named parameter of the constrcutor/method
final String? namedParameter;

/// Name of the class containing the variable/method
final String? className;

Expand All @@ -56,6 +60,7 @@ class AvoidUsingApiEntryParameters {
/// Constructor for [AvoidUsingApiEntryParameters] model
const AvoidUsingApiEntryParameters({
this.identifier,
this.namedParameter,
this.className,
this.source,
this.severity,
Expand All @@ -70,6 +75,7 @@ class AvoidUsingApiEntryParameters {
) =>
AvoidUsingApiEntryParameters(
identifier: json['identifier'] as String?,
namedParameter: json['named_parameter'] as String?,
className: json['class_name'] as String?,
source: json['source'] as String?,
severity: decodeErrorSeverity(json['severity'] as String?),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ custom_lint:
- avoid_using_api:
severity: warning
entries:
- identifier: ()
class_name: BannedCodeUsage
source: package:external_source
reason: "BannedCodeUsage() from package:external_source is not allowed"
- identifier: test4
class_name: BannedCodeUsage
source: package:external_source
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'dart:collection';
import 'package:external_source/external_source.dart';

void testingBannedCodeLint() async {
// expect_lint: avoid_using_api
final bannedCodeUsage = BannedCodeUsage();
// expect_lint: avoid_using_api
BannedCodeUsage.test2();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
analyzer:
plugins:
- ../../custom_lint

custom_lint:
rules:
- avoid_using_api:
severity: warning
entries:
- identifier: ()
named_parameter: badParameter
class_name: NamedParameterBan
source: package:named_parameter_ban
reason: "Use goodParameter instead"
- identifier: namedConstructor
named_parameter: badParameter
class_name: NamedParameterBan
source: package:named_parameter_ban
reason: "Use goodParameter instead"
- identifier: staticMethod
named_parameter: badParameter
class_name: NamedParameterBan
source: package:named_parameter_ban
reason: "Use goodParameter instead"
- identifier: method
named_parameter: badParameter
class_name: NamedParameterBan
source: package:named_parameter_ban
reason: "Use goodParameter instead"
- identifier: extensionMethod
named_parameter: badParameter
class_name: NamedParameterBanExtension
source: package:named_parameter_ban
reason: "Use goodParameter instead"
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class NamedParameterBan {
NamedParameterBan({this.badParameter, this.goodParameter});

String? badParameter;
String? goodParameter;

NamedParameterBan.namedConstructor(
{String? badParameter, String? goodParameter}) {}

void method({String? badParameter, String? goodParameter}) {}
static void staticMethod({String? badParameter, String? goodParameter}) {}
}

extension NamedParameterBanExtension on int {
String extensionMethod({String? badParameter, String? goodParameter}) => '';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:named_parameter_ban/named_parameter_ban.dart';

void testingBannedCodeLint() async {
// expect_lint: avoid_using_api
NamedParameterBan(badParameter: '');
NamedParameterBan(goodParameter: '');

// expect_lint: avoid_using_api
NamedParameterBan.namedConstructor(badParameter: '');
NamedParameterBan.namedConstructor(goodParameter: '');

// expect_lint: avoid_using_api
NamedParameterBan.staticMethod(
badParameter: 'test',
);
NamedParameterBan.staticMethod(goodParameter: '');

final obj = NamedParameterBan();
// expect_lint: avoid_using_api
obj.method(badParameter: '');
obj.method(goodParameter: '');

// expect_lint: avoid_using_api
0.extensionMethod(badParameter: '');
0.extensionMethod(goodParameter: '');
}
17 changes: 17 additions & 0 deletions lint_test/avoid_using_api/named_parameter_ban/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: named_parameter_ban
description: A sample command-line application.
version: 1.0.0
publish_to: none

environment:
sdk: ^3.1.3

dependencies:
external_source:
path: ../external_source

dev_dependencies:
lints: ^3.0.0
test: ^1.21.0
solid_lints:
path: ../../../