Report lints about invalid Dart Templates in SQL

This commit is contained in:
Simon Binder 2019-09-15 11:59:47 +02:00
parent 3abfbd5963
commit 25ceda3505
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
6 changed files with 68 additions and 6 deletions

View File

@ -17,8 +17,9 @@ class _FakeDb extends GeneratedDatabase {
}, },
beforeOpen: (details) async { beforeOpen: (details) async {
// this fake select query is verified via mocks // this fake select query is verified via mocks
await customSelect( await customSelectQuery(
'opened: ${details.versionBefore} to ${details.versionNow}'); 'opened: ${details.versionBefore} to ${details.versionNow}')
.get();
}, },
); );
} }

View File

@ -2,6 +2,8 @@ import 'package:sqlparser/sqlparser.dart';
import '../query_handler.dart'; import '../query_handler.dart';
/// Provides additional hints that aren't implemented in the sqlparser because
/// they're specific to moor.
class Linter { class Linter {
final QueryHandler handler; final QueryHandler handler;
final List<AnalysisError> lints = []; final List<AnalysisError> lints = [];
@ -18,6 +20,39 @@ class _LintingVisitor extends RecursiveVisitor<void> {
_LintingVisitor(this.linter); _LintingVisitor(this.linter);
@override
void visitResultColumn(ResultColumn e) {
super.visitResultColumn(e);
if (e is ExpressionResultColumn) {
// The generated code will be invalid if knowing the expression is needed
// to know the column name (e.g. it's a Dart template without an AS), or
// if the type is unknown.
final expression = e.expression;
final resolveResult = linter.handler.context.typeOf(expression);
if (resolveResult.type == null) {
linter.lints.add(AnalysisError(
type: AnalysisErrorType.other,
message: 'Expression has an unknown type, the generated code can be'
' inaccurate.',
relevantNode: expression,
));
}
final dependsOnPlaceholder = e.as == null &&
expression.allDescendants.whereType<DartPlaceholder>().isNotEmpty;
if (dependsOnPlaceholder) {
linter.lints.add(AnalysisError(
type: AnalysisErrorType.other,
message: 'The name of this column depends on a Dart template, which '
'breaks generated code. Try adding an `AS` alias to fix this.',
relevantNode: e,
));
}
}
}
@override @override
void visitInsertStatement(InsertStatement e) { void visitInsertStatement(InsertStatement e) {
final targeted = e.resolvedTargetColumns; final targeted = e.resolvedTargetColumns;

View File

@ -82,11 +82,11 @@ class QueryHandler {
final type = context.typeOf(column).type; final type = context.typeOf(column).type;
final moorType = mapper.resolvedToMoor(type); final moorType = mapper.resolvedToMoor(type);
UsedTypeConverter converter; UsedTypeConverter converter;
if (type.hint is TypeConverterHint) { if (type?.hint is TypeConverterHint) {
converter = (type.hint as TypeConverterHint).converter; converter = (type.hint as TypeConverterHint).converter;
} }
columns.add(ResultColumn(column.name, moorType, type.nullable, columns.add(ResultColumn(column.name, moorType, type?.nullable ?? true,
converter: converter)); converter: converter));
final table = _tableOfColumn(column); final table = _tableOfColumn(column);

View File

@ -113,7 +113,8 @@ class QueryWriter {
String _readingCode(ResultColumn column) { String _readingCode(ResultColumn column) {
final readMethod = readFromMethods[column.type]; final readMethod = readFromMethods[column.type];
var code = "row.$readMethod('${column.name}')"; final dartLiteral = asDartLiteral(column.name);
var code = 'row.$readMethod($dartLiteral)';
if (column.converter != null) { if (column.converter != null) {
final converter = column.converter; final converter = column.converter;

View File

@ -0,0 +1,25 @@
import 'package:moor_generator/src/analyzer/sql_queries/query_handler.dart';
import 'package:moor_generator/src/analyzer/sql_queries/type_mapping.dart';
import 'package:sqlparser/sqlparser.dart';
import 'package:test/test.dart';
void main() {
final engine = SqlEngine(useMoorExtensions: true);
final mapper = TypeMapper();
test('warns when a result column is unresolved', () {
final result = engine.analyze('SELECT ?;');
final moorQuery = QueryHandler('query', result, mapper).handle();
expect(moorQuery.lints,
anyElement((AnalysisError q) => q.message.contains('unknown type')));
});
test('warns when the result depends on a Dart template', () {
final result = engine.analyze(r"SELECT 'string' = $expr;");
final moorQuery = QueryHandler('query', result, mapper).handle();
expect(moorQuery.lints,
anyElement((AnalysisError q) => q.message.contains('Dart template')));
});
}

View File

@ -62,7 +62,7 @@ class TypeResolver {
// todo probably needs to be nullable when coming from a join? // todo probably needs to be nullable when coming from a join?
return ResolveResult(column.type); return ResolveResult(column.type);
} else if (column is ExpressionColumn) { } else if (column is ExpressionColumn) {
return resolveExpression(column.expression); return resolveOrInfer(column.expression);
} }
throw StateError('Unknown column $column'); throw StateError('Unknown column $column');