Skip to content

Commit

Permalink
Merge branch 'drift_lint' of https://github.com/simolus3/drift into d…
Browse files Browse the repository at this point in the history
…rift_lint
  • Loading branch information
dickermoshe committed Jan 2, 2025
2 parents 0ebc163 + 3cc6cc6 commit 45b7499
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 8 deletions.
19 changes: 16 additions & 3 deletions docs/docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ adding a package to open database on the respective platform.
dev_dependencies:
drift_dev: ^{{ versions.drift_dev }}
build_runner: ^{{ versions.build_runner }}
custom_lint: ^{{ versions.custom_lint }}
```

Alternatively, you can achieve the same result using the following command:

```
dart pub add drift drift_flutter dev:drift_dev dev:build_runner
dart pub add drift drift_flutter dev:drift_dev dev:build_runner dev:custom_lint
```

Please note that `drift_flutter` depends on `sqlite3_flutter_libs`, which includes a compiled
Expand All @@ -62,12 +63,13 @@ adding a package to open database on the respective platform.
dev_dependencies:
drift_dev: ^{{ versions.drift_dev }}
build_runner: ^{{ versions.build_runner }}
custom_lint: ^{{ versions.custom_lint }}
```

Alternatively, you can achieve the same result using the following command:

```
dart pub add drift sqlite3 dev:drift_dev dev:build_runner
dart pub add drift sqlite3 dev:drift_dev dev:build_runner dev:custom_lint
```

=== "Dart (Postgres)"
Expand All @@ -81,12 +83,13 @@ adding a package to open database on the respective platform.
dev_dependencies:
drift_dev: ^{{ versions.drift_dev }}
build_runner: ^{{ versions.build_runner }}
custom_lint: ^{{ versions.custom_lint }}
```

Alternatively, you can achieve the same result using the following command:

```
dart pub add drift postgres drift_postgres dev:drift_dev dev:build_runner
dart pub add drift postgres drift_postgres dev:drift_dev dev:build_runner dev:custom_lint
```

Drift only generates code for sqlite3 by default. So, also create a `build.yaml`
Expand All @@ -105,6 +108,16 @@ adding a package to open database on the respective platform.
# - sqlite
```

## Linter setup

Drift comes with a built-in linter that helps you write better code. It checks for common mistakes
and enforces best practices. To enable it, add the following configuration to your `analysis_options.yaml`:

```yaml title="analysis_options.yaml"
analyzer:
plugins:
- custom_lint
```
## Database class
Expand Down
4 changes: 4 additions & 0 deletions drift_dev/lib/src/lints/custom_lint_plugin.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:drift_dev/src/lints/drift_backend_error_lint.dart';
import 'package:drift_dev/src/lints/non_null_insert_with_ignore_lint.dart';
import 'package:drift_dev/src/lints/offset_without_limit_lint.dart';
import 'package:drift_dev/src/lints/unawaited_futures_in_transaction_lint.dart';
import 'package:meta/meta.dart';
Expand All @@ -10,5 +12,7 @@ class DriftLinter extends PluginBase {
unawaitedFuturesInMigration,
unawaitedFuturesInTransaction,
OffsetWithoutLimit(),
DriftBuildErrors(),
NonNullInsertWithIgnore()
];
}
60 changes: 60 additions & 0 deletions drift_dev/lib/src/lints/non_null_insert_with_ignore_lint.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/error.dart' hide LintCode;
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';

final managerTypeChecker =
TypeChecker.fromName('BaseTableManager', packageName: 'drift');
final insertStatementChecker =
TypeChecker.fromName('InsertStatement', packageName: 'drift');
final insertOrIgnoreChecker =
TypeChecker.fromName('InsertMode', packageName: 'drift');

class NonNullInsertWithIgnore extends DartLintRule {
NonNullInsertWithIgnore() : super(code: _code);

static const _code = LintCode(
name: 'non_null_insert_with_ignore',
problemMessage:
'`insertReturning` and `createReturning` will throw an exception if a row isn\'t actually inserted. Use `createReturningOrNull` or `insertReturningOrNull` if you want to ignore conflicts.',
errorSeverity: ErrorSeverity.WARNING,
);

@override
void run(CustomLintResolver resolver, ErrorReporter reporter,
CustomLintContext context) async {
context.registry.addMethodInvocation(
(node) {
if (node.argumentList.arguments.isEmpty) return;
switch (node.function) {
case SimpleIdentifier func:
if (func.name == "insertReturning" ||
func.name == "createReturning") {
switch (func.parent) {
case MethodInvocation func:
final targetType = func.realTarget?.staticType;
if (targetType != null) {
if (managerTypeChecker.isSuperTypeOf(targetType) ||
insertStatementChecker.isExactlyType(targetType)) {
final namedArgs = func.argumentList.arguments
.whereType<NamedExpression>();
for (final arg in namedArgs) {
if (arg.name.label.name == "mode") {
switch (arg.expression) {
case PrefixedIdentifier mode:
if (mode.identifier.name == "insertOrIgnore") {
print("Found insertOrIgnore");
reporter.atNode(node, _code);
}
}
}
}
}
}
}
}
}
},
);
}
}
7 changes: 2 additions & 5 deletions drift_dev/test/lint/lint_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ void main() {
test('linter', () async {
final workingDir = p.join(p.current, 'test/lint/test_pkg');
expect(
await Process.run('dart', ['pub', 'get'], workingDirectory: workingDir)
.then((v) => v.exitCode),
0);
expect(
await Process.run('custom_lint', ['--fatal-infos', '--fatal-warnings'],
await Process.run('dart',
['run', 'custom_lint', '--fatal-infos', '--fatal-warnings'],
workingDirectory: workingDir)
.then((v) => v.exitCode),
0);
Expand Down
28 changes: 28 additions & 0 deletions drift_dev/test/lint/test_pkg/lib/db.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ class Users extends Table {
late final name = text()();
// expect_lint: drift_build_errors
late final age = integer();
// expect_lint: drift_build_errors
late final group = int64().references(Groups, #id)();
}

class Groups extends Table {
late final id = integer().autoIncrement()();
late final name = text()();
}

class BrokenTable extends Table {
Expand All @@ -26,7 +33,28 @@ class TestDatabase extends _$TestDatabase {
() async {
// expect_lint: unawaited_futures_in_transaction
into(users).insert(UsersCompanion.insert(name: 'name'));
await into(users).insert(UsersCompanion.insert(name: 'name'));
},
);
// expect_lint: non_null_insert_with_ignore
await into(users).insertReturning(UsersCompanion.insert(name: 'name'),
mode: InsertMode.insertOrIgnore);
// expect_lint: non_null_insert_with_ignore
await managers.users
.createReturning((o) => o(name: "hi"), mode: InsertMode.insertOrIgnore);
await into(users).insertReturningOrNull(UsersCompanion.insert(name: 'name'),
mode: InsertMode.insertOrIgnore);
await managers.users.createReturningOrNull((o) => o(name: "hi"),
mode: InsertMode.insertOrIgnore);
await into(users).insertReturning(UsersCompanion.insert(name: 'name'));
await managers.users.createReturning((o) => o(name: "hi"));
}

@override
MigrationStrategy get migration => MigrationStrategy(
onUpgrade: (m, from, to) async {
// expect_lint: unawaited_futures_in_migration
m.createTable(users);
},
);
}
1 change: 1 addition & 0 deletions drift_dev/test/lint/test_pkg/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: test_pkg
description: A starting point for Dart libraries or applications.
version: 1.0.0
publish_to: none

environment:
sdk: ^3.5.3
Expand Down

0 comments on commit 45b7499

Please sign in to comment.