mirror of https://github.com/AMT-Cheif/drift.git
Types2: Initial support for binary expressions
This commit is contained in:
parent
22525b24c3
commit
4484890609
|
@ -17,6 +17,33 @@ class CopyTypeFrom extends TypeRelationship {
|
|||
CopyTypeFrom(this.target, this.other);
|
||||
}
|
||||
|
||||
/// Dependency declaring that [target] has a type that matches all of [from].
|
||||
class CopyEncapsulating extends TypeRelationship {
|
||||
final Typeable target;
|
||||
final List<Typeable> from;
|
||||
|
||||
CopyEncapsulating(this.target, this.from);
|
||||
}
|
||||
|
||||
/// Dependency declaring that [first] and [second] have the same type. This is
|
||||
/// an optional dependency that will only be applied when one type is known and
|
||||
/// the other is not.
|
||||
class HaveSameType extends TypeRelationship {
|
||||
final Typeable first;
|
||||
final Typeable second;
|
||||
|
||||
HaveSameType(this.first, this.second);
|
||||
}
|
||||
|
||||
/// Dependency declaring that, if no better option is found, [target] should
|
||||
/// have the specified [defaultType].
|
||||
class DefaultType extends TypeRelationship {
|
||||
final Typeable target;
|
||||
final ResolvedType defaultType;
|
||||
|
||||
DefaultType(this.target, this.defaultType);
|
||||
}
|
||||
|
||||
enum CastMode { numeric, boolean }
|
||||
|
||||
/// Dependency declaring that [target] has the same type as [other] after
|
||||
|
|
|
@ -93,6 +93,69 @@ class TypeResolver extends RecursiveVisitor<TypeExpectation, void> {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitBinaryExpression(BinaryExpression e, TypeExpectation arg) {
|
||||
switch (e.operator.type) {
|
||||
case TokenType.and:
|
||||
case TokenType.or:
|
||||
session.checkAndResolve(e, const ResolvedType.bool(), arg);
|
||||
session.addRelationship(NullableIfSomeOtherIs(e, [e.left, e.right]));
|
||||
|
||||
// logic expressions, so children must be boolean
|
||||
visitChildren(e, const ExactTypeExpectation.laxly(ResolvedType.bool()));
|
||||
break;
|
||||
case TokenType.equal:
|
||||
case TokenType.exclamationEqual:
|
||||
case TokenType.lessMore:
|
||||
case TokenType.less:
|
||||
case TokenType.lessEqual:
|
||||
case TokenType.more:
|
||||
case TokenType.moreEqual:
|
||||
// comparison. Returns bool, copying nullability from children.
|
||||
session.checkAndResolve(e, const ResolvedType.bool(), arg);
|
||||
session.addRelationship(NullableIfSomeOtherIs(e, [e.left, e.right]));
|
||||
// Not technically a requirement, but assume lhs and rhs have the same
|
||||
// type.
|
||||
session.addRelationship(HaveSameType(e.left, e.right));
|
||||
visitChildren(e, arg);
|
||||
break;
|
||||
case TokenType.plus:
|
||||
case TokenType.minus:
|
||||
session.addRelationship(CopyEncapsulating(e, [e.left, e.right]));
|
||||
break;
|
||||
// all of those only really make sense for integers
|
||||
case TokenType.shiftLeft:
|
||||
case TokenType.shiftRight:
|
||||
case TokenType.pipe:
|
||||
case TokenType.ampersand:
|
||||
case TokenType.percent:
|
||||
const type = ResolvedType(type: BasicType.int);
|
||||
session.checkAndResolve(e, type, arg);
|
||||
session.addRelationship(NullableIfSomeOtherIs(e, [e.left, e.right]));
|
||||
visitChildren(e, const ExactTypeExpectation.laxly(type));
|
||||
break;
|
||||
case TokenType.doublePipe:
|
||||
// string concatenation.
|
||||
const stringType = ResolvedType(type: BasicType.text);
|
||||
session.checkAndResolve(e, stringType, arg);
|
||||
session.addRelationship(NullableIfSomeOtherIs(e, [e.left, e.right]));
|
||||
const childExpectation = ExactTypeExpectation.laxly(stringType);
|
||||
visit(e.left, childExpectation);
|
||||
visit(e.right, childExpectation);
|
||||
break;
|
||||
default:
|
||||
throw StateError('Binary operator ${e.operator.type} not recognized '
|
||||
'by types2. At $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitIsExpression(IsExpression e, TypeExpectation arg) {
|
||||
session.checkAndResolve(e, const ResolvedType.bool(), arg);
|
||||
session.hintNullability(e, false);
|
||||
visitChildren(e, const NoTypeExpectation());
|
||||
}
|
||||
|
||||
void _handleWhereClause(HasWhereClause stmt) {
|
||||
// assume that a where statement is a boolean expression. Sqlite internally
|
||||
// casts (https://www.sqlite.org/lang_expr.html#booleanexpr), so be lax
|
||||
|
|
|
@ -56,4 +56,9 @@ void main() {
|
|||
const ResolvedType.bool(),
|
||||
);
|
||||
});
|
||||
|
||||
test('infers type in a string concatenation', () {
|
||||
expect(_resolveFirstVariable("SELECT '' || :foo"),
|
||||
const ResolvedType(type: BasicType.text));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue