Skip to content

Commit c439a9e

Browse files
committed
feat: add drift_lint to docs project
1 parent 9ccc51f commit c439a9e

File tree

5 files changed

+164
-6
lines changed

5 files changed

+164
-6
lines changed

docs/analysis_options.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
include: package:lints/recommended.yaml
22

33
analyzer:
4+
plugins:
5+
- custom_lint
46
language:
57
strict-casts: true
68
exclude:
79
- "**/*.g.dart"
10+
11+
custom_lint:
12+
debug: true
13+
# Optional, will cause custom_lint to log its internal debug information
14+
verbose: true

docs/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ dev_dependencies:
4646
hosted: https://pub-simonbinder-eu.fsn1.your-objectstorage.com
4747
version: ^0.0.15
4848
sass_builder: ^2.2.1
49+
custom_lint: ^0.6.7
4950

5051
dependency_overrides:
5152
drift_flutter:

drift_dev/lib/drift_dev.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:custom_lint_builder/custom_lint_builder.dart';
2+
import 'package:drift_dev/src/lints/unawaited_futures_in_transaction.dart';
23

34
import 'src/lints/column_builder_on_table.dart';
45

@@ -8,7 +9,6 @@ PluginBase createPlugin() {
89

910
class _DriftLinter extends PluginBase {
1011
@override
11-
List<LintRule> getLintRules(CustomLintConfigs configs) => [
12-
ColumnBuilderOnTable(),
13-
];
12+
List<LintRule> getLintRules(CustomLintConfigs configs) =>
13+
[ColumnBuilderOnTable(), UnawaitedFuturesInTransaction()];
1414
}

drift_dev/lib/src/lints/column_builder_on_table.dart

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,45 @@
11
// import 'package:analyzer/dart/ast/ast.dart';
22
// import 'package:analyzer/dart/element/nullability_suffix.dart';
33
// import 'package:analyzer/dart/element/type.dart';
4+
import 'package:analyzer/dart/element/element.dart';
45
import 'package:analyzer/error/error.dart' hide LintCode;
56
import 'package:analyzer/error/listener.dart';
67
import 'package:custom_lint_builder/custom_lint_builder.dart';
78

9+
final tableChecker = TypeChecker.fromName('Table', packageName: 'drift');
10+
final columnBuilderChecker =
11+
TypeChecker.fromName('ColumnBuilder', packageName: 'drift');
12+
813
class ColumnBuilderOnTable extends DartLintRule {
914
ColumnBuilderOnTable() : super(code: _code);
1015

1116
static const _code = LintCode(
1217
name: 'column_builder_on_table',
1318
problemMessage:
14-
'This column declaration is missing an extra set of parentheses at the end'
19+
'This column declaration is missing a set of parentheses at the end'
1520
' of the column builder. This is likely a mistake.'
1621
' Add a pair of parentheses to the end of the column builder.',
1722
errorSeverity: ErrorSeverity.ERROR,
1823
);
1924
@override
2025
void run(CustomLintResolver resolver, ErrorReporter reporter,
2126
CustomLintContext context) {
22-
print("Hi");
2327
context.registry.addVariableDeclaration(
2428
(node) {
25-
reporter.atNode(node, _code);
29+
// Extract the element from the node
30+
final element = node.declaredElement;
31+
32+
// Extract the type of the declared element
33+
final type = element?.type;
34+
35+
if (type == null || element is! FieldElement) {
36+
return;
37+
}
38+
// Check if the field element is a field of a class that extends Table and has a type of ColumnBuilder
39+
if (tableChecker.isSuperOf(element.enclosingElement) &&
40+
columnBuilderChecker.isExactlyType(type)) {
41+
reporter.atNode(node, _code);
42+
}
2643
},
2744
);
2845
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// import 'package:analyzer/dart/ast/ast.dart';
2+
// import 'package:analyzer/dart/element/nullability_suffix.dart';
3+
// import 'package:analyzer/dart/element/type.dart';
4+
import 'package:analyzer/dart/ast/ast.dart';
5+
import 'package:custom_lint_core/src/node_lint_visitor.dart';
6+
import 'package:analyzer/dart/element/element.dart';
7+
import 'package:analyzer/error/error.dart' hide LintCode;
8+
import 'package:analyzer/error/listener.dart';
9+
import 'package:custom_lint_builder/custom_lint_builder.dart';
10+
11+
final tableChecker = TypeChecker.fromName('Table', packageName: 'drift');
12+
final databaseConnectionUserChecker =
13+
TypeChecker.fromName('DatabaseConnectionUser', packageName: 'drift');
14+
final columnBuilderChecker =
15+
TypeChecker.fromName('ColumnBuilder', packageName: 'drift');
16+
17+
bool inTransactionBlock(AstNode node) {
18+
return node.thisOrAncestorMatching(
19+
(method) {
20+
if (method is! MethodInvocation) return false;
21+
22+
final methodElement = method.methodName.staticElement;
23+
if (methodElement is! MethodElement ||
24+
methodElement.name != 'transaction') return false;
25+
26+
final enclosingElement = methodElement.enclosingElement;
27+
if (enclosingElement is! ClassElement ||
28+
!databaseConnectionUserChecker.isExactly(enclosingElement)) {
29+
return false;
30+
}
31+
return true;
32+
},
33+
) !=
34+
null;
35+
}
36+
37+
class UnawaitedFuturesInTransaction extends DartLintRule {
38+
UnawaitedFuturesInTransaction() : super(code: _code);
39+
40+
static const _code = LintCode(
41+
name: 'unawaited_futures_in_transaction',
42+
problemMessage:
43+
'All futures in a transaction should be awaited to ensure that all operations are completed before the transaction is closed.',
44+
errorSeverity: ErrorSeverity.ERROR,
45+
);
46+
@override
47+
void run(CustomLintResolver resolver, ErrorReporter reporter,
48+
CustomLintContext context) {
49+
context.registry.addVariableDeclarationStatement((node) {
50+
// If this variable declaration is not inside a transaction block, return
51+
if (inTransactionBlock(node)) {
52+
for (var variable in node.variables.variables) {
53+
final type = variable.declaredElement?.type;
54+
if (type == null || !type.isDartAsyncFuture) continue;
55+
reporter.atNode(variable, _code);
56+
}
57+
}
58+
});
59+
context.registry.addExpressionStatement((statement) {
60+
// If this variable declaration is not inside a transaction block, return
61+
if (inTransactionBlock(statement)) {
62+
final expression = statement.expression;
63+
if (expression is! MethodInvocation) return;
64+
final element = expression.methodName.staticElement;
65+
if (element is! MethodElement) return;
66+
if (element.returnType.isDartAsyncFuture) {
67+
reporter.atNode(expression, _code);
68+
}
69+
}
70+
});
71+
// context.registry.addInstanceCreationExpression((statement) {
72+
// if (inTransactionBlock(statement)) {
73+
// if (statement
74+
// .constructorName.staticElement?.returnType.isDartAsyncFuture ??
75+
// false) {
76+
// reporter.atNode(statement, _code);
77+
// }
78+
// }
79+
// If this variable declaration is not inside a transaction block, return
80+
// if (inTransactionBlock(statement)) {
81+
// final expression = statement.expression;
82+
// if (expression is! MethodInvocation) return;
83+
// final element = expression.methodName.staticElement;
84+
// if (element is! MethodElement) return;
85+
// if (element.returnType.isDartAsyncFuture) {
86+
// reporter.atNode(expression, _code);
87+
// }
88+
// }
89+
// });
90+
// context.registry.addArgumentList(
91+
// (node) {
92+
// final method = node.parent;
93+
// if (method is! MethodInvocation) return;
94+
95+
// final methodElement = method.methodName.staticElement;
96+
// if (methodElement is! MethodElement) return;
97+
98+
// // Verify that the method is a transaction method
99+
// if (methodElement.name != 'transaction') return;
100+
101+
// // Verify that the method is called on a DatabaseConnectionUser
102+
// final enclosingElement = methodElement.enclosingElement;
103+
// if (enclosingElement is! ClassElement) return;
104+
105+
// // Get the 1st argument of the transaction method
106+
// final classback = node.arguments.firstOrNull;
107+
// if (classback is! FunctionExpression) return;
108+
109+
// // Get the body of the function
110+
// final body = classback.body;
111+
// if (body is! BlockFunctionBody) return;
112+
// for (var statement in body.block.statements) {
113+
// if (statement is ExpressionStatement) {
114+
// final expression = statement.expression;
115+
// if (expression is! MethodInvocation) continue;
116+
// // If the return type of the method is a Future, then the method should have been awaited
117+
// final element = expression.methodName.staticElement;
118+
// if (element is! MethodElement) continue;
119+
// if (element.returnType.isDartAsyncFuture) {
120+
// reporter.atNode(expression, _code);
121+
// }
122+
// } else if (statement is VariableDeclarationStatement) {
123+
// for (var variable in statement.variables.variables) {
124+
// final type = variable.declaredElement?.type;
125+
// if (type == null || !type.isDartAsyncFuture) continue;
126+
// reporter.atNode(variable, _code);
127+
// }
128+
// }
129+
// }
130+
// },
131+
// );
132+
}
133+
}

0 commit comments

Comments
 (0)