Add `fatal_warnings` build option

This commit is contained in:
Simon Binder 2023-06-13 22:08:07 +02:00
parent c72f2131f7
commit 4411e0c459
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
9 changed files with 92 additions and 5 deletions

View File

@ -76,6 +76,8 @@ At the moment, drift supports these options:
The possible values are `preserve`, `camelCase`, `CONSTANT_CASE`, `snake_case`, `PascalCase`, `lowercase` and `UPPERCASE` (default: `snake_case`).
* `write_to_columns_mixins`: Whether the `toColumns` method should be written as a mixin instead of being added directly to the data class.
This is useful when using [existing row classes]({{ 'custom_row_classes.md' | pageUrl }}), as the mixin is generated for those as well.
* `fatal_warnings`: When enabled (defaults to `false`), warnings found by `drift_dev` in the build process (like syntax errors in SQL queries or
unresolved references in your Dart tables) will cause the build to fail.
## Assumed SQL environment

View File

@ -18,6 +18,7 @@ targets:
write_from_json_string_constructor: true
raw_result_set_data: true
named_parameters: true
fatal_warnings: true
sql:
dialect: sqlite
options:
@ -33,6 +34,7 @@ targets:
builders:
drift_dev:
options:
fatal_warnings: true
store_date_time_values_as_text: true
# Dart doesn't support YAML merge tags yet, https://github.com/dart-lang/yaml/issues/121
override_hash_and_equals_in_result_sets: true

View File

@ -1,9 +1,7 @@
// Mocks generated by Mockito 5.4.1 from annotations
// Mocks generated by Mockito 5.4.2 from annotations
// in drift/test/test_utils/test_utils.dart.
// Do not manually edit this file.
// @dart=2.19
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i4;

View File

@ -102,6 +102,9 @@ class DriftOptions {
@JsonKey(name: 'write_to_columns_mixins', defaultValue: false)
final bool writeToColumnsMixins;
@JsonKey(name: 'fatal_warnings', defaultValue: false)
final bool fatalWarnings;
@internal
const DriftOptions.defaults({
this.generateFromJsonStringConstructor = false,
@ -124,6 +127,7 @@ class DriftOptions {
this.dialect = const DialectOptions(SqlDialect.sqlite, null),
this.caseFromDartToSql = CaseFromDartToSql.snake,
this.writeToColumnsMixins = false,
this.fatalWarnings = false,
});
DriftOptions({
@ -146,6 +150,7 @@ class DriftOptions {
required this.storeDateTimeValuesAsText,
required this.caseFromDartToSql,
required this.writeToColumnsMixins,
required this.fatalWarnings,
this.dialect,
}) {
// ignore: deprecated_member_use_from_same_package

View File

@ -9,6 +9,7 @@ import '../../analysis/options.dart';
import '../../writer/import_manager.dart';
import '../../writer/writer.dart';
import 'backend.dart';
import 'exception.dart';
class DriftAnalyzer extends Builder {
final DriftOptions options;
@ -34,15 +35,18 @@ class DriftAnalyzer extends Builder {
final driver = DriftAnalysisDriver(backend, options);
final results = await driver.resolveElements(buildStep.inputId.uri);
var hadWarnings = false;
for (final parseError in results.errorsDuringDiscovery) {
log.warning(parseError.toString());
hadWarnings = true;
}
if (results.analysis.isNotEmpty) {
for (final result in results.analysis.values) {
for (final error in result.errorsDuringAnalysis) {
log.warning(error.toString());
hadWarnings = true;
}
}
@ -86,6 +90,10 @@ class DriftAnalyzer extends Builder {
await buildStep.writeAsString(typesOutput, writer.writeGenerated());
}
}
if (hadWarnings && options.fatalWarnings) {
throw const FatalWarningException();
}
}
static final _languageVersionForGeneralizedTypedefs = LanguageVersion(2, 13);

View File

@ -1,6 +1,5 @@
import 'package:build/build.dart';
import 'package:dart_style/dart_style.dart';
import 'package:drift_dev/src/writer/tables/table_writer.dart';
import 'package:pub_semver/pub_semver.dart';
import '../../analysis/custom_result_class.dart';
@ -14,9 +13,11 @@ import '../../writer/drift_accessor_writer.dart';
import '../../writer/function_stubs_writer.dart';
import '../../writer/import_manager.dart';
import '../../writer/modules.dart';
import '../../writer/tables/table_writer.dart';
import '../../writer/tables/view_writer.dart';
import '../../writer/writer.dart';
import 'backend.dart';
import 'exception.dart';
class _BuilderFlags {
bool didWarnAboutDeprecatedOptions = false;
@ -125,6 +126,7 @@ class _DriftBuildRun {
late Writer writer;
Set<Uri> analyzedUris = {};
bool _didPrintWarning = false;
_DriftBuildRun(this.options, this.mode, this.buildStep)
: driver = DriftAnalysisDriver(DriftBuildBackend(buildStep), options)
@ -152,6 +154,10 @@ class _DriftBuildRun {
await _generateModular(fileResult);
}
await _emitCode();
if (_didPrintWarning && options.fatalWarnings) {
throw const FatalWarningException();
}
}
Future<FileState> _analyze(Uri uri, {bool isEntrypoint = false}) async {
@ -162,8 +168,11 @@ class _DriftBuildRun {
final printErrors =
isEntrypoint || (mode.isMonolithic && analyzedUris.add(result.ownUri));
if (printErrors) {
// Only printing errors from the fileAnalysis step here. The analyzer
// builder will print errors from earlier analysis steps.
for (final error in result.fileAnalysis?.analysisErrors ?? const []) {
log.warning(error);
_didPrintWarning = true;
}
}

View File

@ -0,0 +1,9 @@
class FatalWarningException implements Exception {
const FatalWarningException();
@override
String toString() {
return 'Drift emitted warnings and the `fatal_warnings` build option is '
'enabled.';
}
}

View File

@ -32,7 +32,8 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate(
'scoped_dart_components',
'store_date_time_values_as_text',
'case_from_dart_to_sql',
'write_to_columns_mixins'
'write_to_columns_mixins',
'fatal_warnings'
],
);
final val = DriftOptions(
@ -86,6 +87,8 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate(
CaseFromDartToSql.snake),
writeToColumnsMixins: $checkedConvert(
'write_to_columns_mixins', (v) => v as bool? ?? false),
fatalWarnings:
$checkedConvert('fatal_warnings', (v) => v as bool? ?? false),
dialect: $checkedConvert('sql',
(v) => v == null ? null : DialectOptions.fromJson(v as Map)),
);
@ -114,6 +117,7 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate(
'storeDateTimeValuesAsText': 'store_date_time_values_as_text',
'caseFromDartToSql': 'case_from_dart_to_sql',
'writeToColumnsMixins': 'write_to_columns_mixins',
'fatalWarnings': 'fatal_warnings',
'dialect': 'sql'
},
);
@ -147,6 +151,7 @@ Map<String, dynamic> _$DriftOptionsToJson(DriftOptions instance) =>
'case_from_dart_to_sql':
_$CaseFromDartToSqlEnumMap[instance.caseFromDartToSql]!,
'write_to_columns_mixins': instance.writeToColumnsMixins,
'fatal_warnings': instance.fatalWarnings,
};
const _$SqlModuleEnumMap = {

View File

@ -1,5 +1,6 @@
import 'package:build/build.dart';
import 'package:build_test/build_test.dart';
import 'package:drift_dev/src/backends/build/exception.dart';
import 'package:logging/logging.dart';
import 'package:test/test.dart';
@ -380,4 +381,52 @@ q: SELECT 1;
)
}, result.dartOutputs, result);
});
group('reports issues', () {
for (final fatalWarnings in [false, true]) {
group('fatalWarnings: $fatalWarnings', () {
final options = BuilderOptions(
{'fatal_warnings': fatalWarnings},
isRoot: true,
);
Future<void> runTest(String source, expectedMessage) async {
final build = emulateDriftBuild(
inputs: {'a|lib/a.drift': source},
logger: loggerThat(emits(isA<LogRecord>()
.having((e) => e.message, 'message', expectedMessage))),
modularBuild: true,
options: options,
);
if (fatalWarnings) {
await expectLater(build, throwsA(isA<FatalWarningException>()));
} else {
await build;
}
}
test('syntax', () async {
await runTest(
'foo: SELECT;', contains('Could not parse this expression'));
});
test('semantic in analysis', () async {
await runTest('''
CREATE TABLE foo (
id INTEGER NOT NULL PRIMARY KEY,
unknown INTEGER NOT NULL REFERENCES another ("table")
);
''', contains('could not be found in any import.'));
});
test('file analysis', () async {
await runTest(
r'a($x = 2): SELECT 1, 2, 3 ORDER BY $x;',
contains('This placeholder has a default value, which is only '
'supported for expressions.'));
});
});
}
});
}