Skip to content

Commit 2904ecf

Browse files
authored
Extract evaluator from checker, more enum emit alignment (#607)
1 parent 1bf1967 commit 2904ecf

File tree

12 files changed

+332
-280
lines changed

12 files changed

+332
-280
lines changed

internal/checker/checker.go

+44-43
Large diffs are not rendered by default.

internal/checker/flow.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/microsoft/typescript-go/internal/binder"
1111
"github.com/microsoft/typescript-go/internal/compiler/diagnostics"
1212
"github.com/microsoft/typescript-go/internal/core"
13+
"github.com/microsoft/typescript-go/internal/evaluator"
1314
"github.com/microsoft/typescript-go/internal/scanner"
1415
)
1516

@@ -1729,7 +1730,7 @@ func tryGetNameFromType(t *Type) (string, bool) {
17291730
case t.flags&TypeFlagsUniqueESSymbol != 0:
17301731
return t.AsUniqueESSymbolType().name, true
17311732
case t.flags&TypeFlagsStringOrNumberLiteral != 0:
1732-
return anyToString(t.AsLiteralType().value), true
1733+
return evaluator.AnyToString(t.AsLiteralType().value), true
17331734
}
17341735
return "", false
17351736
}
@@ -1754,7 +1755,7 @@ func (c *Checker) getDestructuringPropertyName(node *ast.Node) (string, bool) {
17541755
func (c *Checker) getLiteralPropertyNameText(name *ast.Node) (string, bool) {
17551756
t := c.getLiteralTypeFromPropertyName(name)
17561757
if t.flags&(TypeFlagsStringLiteral|TypeFlagsNumberLiteral) != 0 {
1757-
return anyToString(t.AsLiteralType().value), true
1758+
return evaluator.AnyToString(t.AsLiteralType().value), true
17581759
}
17591760
return "", false
17601761
}

internal/checker/inference.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ func (c *Checker) inferToTemplateLiteralType(n *InferenceState, source *Type, ta
552552
case left.flags&TypeFlagsBigInt != 0:
553553
return left
554554
case right.flags&TypeFlagsBigInt != 0:
555-
return c.getBigIntLiteralType(PseudoBigInt{}) // !!!
555+
return c.getBigIntLiteralType(jsnum.PseudoBigInt{}) // !!!
556556
case left.flags&TypeFlagsBigIntLiteral != 0:
557557
return left
558558
case right.flags&TypeFlagsBigIntLiteral != 0 && pseudoBigIntToString(getBigIntLiteralValue(right)) == str:

internal/checker/printer.go

+3-6
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ func (p *Printer) printValue(value any) {
200200
p.printNumberLiteral(value)
201201
case bool:
202202
p.printBooleanLiteral(value)
203-
case PseudoBigInt:
203+
case jsnum.PseudoBigInt:
204204
p.printBigIntLiteral(value)
205205
}
206206
}
@@ -219,11 +219,8 @@ func (p *Printer) printBooleanLiteral(b bool) {
219219
p.print(core.IfElse(b, "true", "false"))
220220
}
221221

222-
func (p *Printer) printBigIntLiteral(b PseudoBigInt) {
223-
if b.negative {
224-
p.print("-")
225-
}
226-
p.print(b.base10Value)
222+
func (p *Printer) printBigIntLiteral(b jsnum.PseudoBigInt) {
223+
p.print(b.String())
227224
}
228225

229226
func (p *Printer) printUniqueESSymbolType(t *Type) {

internal/checker/relater.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,8 @@ func (c *Checker) isEnumTypeRelatedTo(source *ast.Symbol, target *ast.Symbol, er
304304
c.enumRelation[key] = RelationComparisonResultFailed
305305
return false
306306
}
307-
sourceValue := c.getEnumMemberValue(ast.GetDeclarationOfKind(sourceProperty, ast.KindEnumMember)).value
308-
targetValue := c.getEnumMemberValue(ast.GetDeclarationOfKind(targetProperty, ast.KindEnumMember)).value
307+
sourceValue := c.getEnumMemberValue(ast.GetDeclarationOfKind(sourceProperty, ast.KindEnumMember)).Value
308+
targetValue := c.getEnumMemberValue(ast.GetDeclarationOfKind(targetProperty, ast.KindEnumMember)).Value
309309
if sourceValue != targetValue {
310310
// If we have 2 enums with *known* values that differ, they are incompatible.
311311
if sourceValue != nil && targetValue != nil {

internal/checker/types.go

+2-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/microsoft/typescript-go/internal/ast"
77
"github.com/microsoft/typescript-go/internal/collections"
88
"github.com/microsoft/typescript-go/internal/core"
9+
"github.com/microsoft/typescript-go/internal/evaluator"
910
)
1011

1112
//go:generate go tool golang.org/x/tools/cmd/stringer -type=SignatureKind -output=stringer_generated.go
@@ -328,7 +329,7 @@ type TypeNodeLinks struct {
328329
// Links for enum members
329330

330331
type EnumMemberLinks struct {
331-
value EvaluatorResult // Constant value of enum member
332+
value evaluator.Result // Constant value of enum member
332333
}
333334

334335
// Links for assertion expressions
@@ -664,11 +665,6 @@ type LiteralType struct {
664665
regularType *Type // Regular version of type
665666
}
666667

667-
type PseudoBigInt struct {
668-
negative bool
669-
base10Value string
670-
}
671-
672668
// UniqueESSymbolTypeData
673669

674670
type UniqueESSymbolType struct {

internal/checker/utilities.go

+2-150
Original file line numberDiff line numberDiff line change
@@ -1210,21 +1210,6 @@ func isThisProperty(node *ast.Node) bool {
12101210
return (ast.IsPropertyAccessExpression(node) || ast.IsElementAccessExpression(node)) && node.Expression().Kind == ast.KindThisKeyword
12111211
}
12121212

1213-
func anyToString(v any) string {
1214-
// !!! This function should behave identically to the expression `"" + v` in JS
1215-
switch v := v.(type) {
1216-
case string:
1217-
return v
1218-
case jsnum.Number:
1219-
return v.String()
1220-
case bool:
1221-
return core.IfElse(v, "true", "false")
1222-
case PseudoBigInt:
1223-
return "(BigInt)" // !!!
1224-
}
1225-
panic("Unhandled case in anyToString")
1226-
}
1227-
12281213
func isValidNumberString(s string, roundTripOnly bool) bool {
12291214
if s == "" {
12301215
return false
@@ -1423,136 +1408,6 @@ func isObjectLiteralElementLike(node *ast.Node) bool {
14231408
return ast.IsObjectLiteralElement(node)
14241409
}
14251410

1426-
type EvaluatorResult struct {
1427-
value any
1428-
isSyntacticallyString bool
1429-
resolvedOtherFiles bool
1430-
hasExternalReferences bool
1431-
}
1432-
1433-
func evaluatorResult(value any, isSyntacticallyString bool, resolvedOtherFiles bool, hasExternalReferences bool) EvaluatorResult {
1434-
return EvaluatorResult{value, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences}
1435-
}
1436-
1437-
type Evaluator func(expr *ast.Node, location *ast.Node) EvaluatorResult
1438-
1439-
func createEvaluator(evaluateEntity Evaluator) Evaluator {
1440-
var evaluate Evaluator
1441-
evaluateTemplateExpression := func(expr *ast.Node, location *ast.Node) EvaluatorResult {
1442-
var sb strings.Builder
1443-
sb.WriteString(expr.AsTemplateExpression().Head.Text())
1444-
resolvedOtherFiles := false
1445-
hasExternalReferences := false
1446-
for _, span := range expr.AsTemplateExpression().TemplateSpans.Nodes {
1447-
spanResult := evaluate(span.Expression(), location)
1448-
if spanResult.value == nil {
1449-
return evaluatorResult(nil, true /*isSyntacticallyString*/, false, false)
1450-
}
1451-
sb.WriteString(anyToString(spanResult.value))
1452-
sb.WriteString(span.AsTemplateSpan().Literal.Text())
1453-
resolvedOtherFiles = resolvedOtherFiles || spanResult.resolvedOtherFiles
1454-
hasExternalReferences = hasExternalReferences || spanResult.hasExternalReferences
1455-
}
1456-
return evaluatorResult(sb.String(), true, resolvedOtherFiles, hasExternalReferences)
1457-
}
1458-
evaluate = func(expr *ast.Node, location *ast.Node) EvaluatorResult {
1459-
isSyntacticallyString := false
1460-
resolvedOtherFiles := false
1461-
hasExternalReferences := false
1462-
// It's unclear when/whether we should consider skipping other kinds of outer expressions.
1463-
// Type assertions intentionally break evaluation when evaluating literal types, such as:
1464-
// type T = `one ${"two" as any} three`; // string
1465-
// But it's less clear whether such an assertion should break enum member evaluation:
1466-
// enum E {
1467-
// A = "one" as any
1468-
// }
1469-
// SatisfiesExpressions and non-null assertions seem to have even less reason to break
1470-
// emitting enum members as literals. However, these expressions also break Babel's
1471-
// evaluation (but not esbuild's), and the isolatedModules errors we give depend on
1472-
// our evaluation results, so we're currently being conservative so as to issue errors
1473-
// on code that might break Babel.
1474-
expr = ast.SkipParentheses(expr)
1475-
switch expr.Kind {
1476-
case ast.KindPrefixUnaryExpression:
1477-
result := evaluate(expr.AsPrefixUnaryExpression().Operand, location)
1478-
resolvedOtherFiles = result.resolvedOtherFiles
1479-
hasExternalReferences = result.hasExternalReferences
1480-
if value, ok := result.value.(jsnum.Number); ok {
1481-
switch expr.AsPrefixUnaryExpression().Operator {
1482-
case ast.KindPlusToken:
1483-
return evaluatorResult(value, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1484-
case ast.KindMinusToken:
1485-
return evaluatorResult(-value, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1486-
case ast.KindTildeToken:
1487-
return evaluatorResult(value.BitwiseNOT(), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1488-
}
1489-
}
1490-
case ast.KindBinaryExpression:
1491-
left := evaluate(expr.AsBinaryExpression().Left, location)
1492-
right := evaluate(expr.AsBinaryExpression().Right, location)
1493-
operator := expr.AsBinaryExpression().OperatorToken.Kind
1494-
isSyntacticallyString = (left.isSyntacticallyString || right.isSyntacticallyString) && expr.AsBinaryExpression().OperatorToken.Kind == ast.KindPlusToken
1495-
resolvedOtherFiles = left.resolvedOtherFiles || right.resolvedOtherFiles
1496-
hasExternalReferences = left.hasExternalReferences || right.hasExternalReferences
1497-
leftNum, leftIsNum := left.value.(jsnum.Number)
1498-
rightNum, rightIsNum := right.value.(jsnum.Number)
1499-
if leftIsNum && rightIsNum {
1500-
switch operator {
1501-
case ast.KindBarToken:
1502-
return evaluatorResult(leftNum.BitwiseOR(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1503-
case ast.KindAmpersandToken:
1504-
return evaluatorResult(leftNum.BitwiseAND(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1505-
case ast.KindGreaterThanGreaterThanToken:
1506-
return evaluatorResult(leftNum.SignedRightShift(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1507-
case ast.KindGreaterThanGreaterThanGreaterThanToken:
1508-
return evaluatorResult(leftNum.UnsignedRightShift(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1509-
case ast.KindLessThanLessThanToken:
1510-
return evaluatorResult(leftNum.LeftShift(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1511-
case ast.KindCaretToken:
1512-
return evaluatorResult(leftNum.BitwiseXOR(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1513-
case ast.KindAsteriskToken:
1514-
return evaluatorResult(leftNum*rightNum, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1515-
case ast.KindSlashToken:
1516-
return evaluatorResult(leftNum/rightNum, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1517-
case ast.KindPlusToken:
1518-
return evaluatorResult(leftNum+rightNum, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1519-
case ast.KindMinusToken:
1520-
return evaluatorResult(leftNum-rightNum, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1521-
case ast.KindPercentToken:
1522-
return evaluatorResult(leftNum.Remainder(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1523-
case ast.KindAsteriskAsteriskToken:
1524-
return evaluatorResult(leftNum.Exponentiate(rightNum), isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1525-
}
1526-
}
1527-
leftStr, leftIsStr := left.value.(string)
1528-
rightStr, rightIsStr := right.value.(string)
1529-
if (leftIsStr || leftIsNum) && (rightIsStr || rightIsNum) && operator == ast.KindPlusToken {
1530-
if leftIsNum {
1531-
leftStr = leftNum.String()
1532-
}
1533-
if rightIsNum {
1534-
rightStr = rightNum.String()
1535-
}
1536-
return evaluatorResult(leftStr+rightStr, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1537-
}
1538-
case ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral:
1539-
return evaluatorResult(expr.Text(), true /*isSyntacticallyString*/, false, false)
1540-
case ast.KindTemplateExpression:
1541-
return evaluateTemplateExpression(expr, location)
1542-
case ast.KindNumericLiteral:
1543-
return evaluatorResult(jsnum.FromString(expr.Text()), false, false, false)
1544-
case ast.KindIdentifier, ast.KindElementAccessExpression:
1545-
return evaluateEntity(expr, location)
1546-
case ast.KindPropertyAccessExpression:
1547-
if ast.IsEntityNameExpression(expr) {
1548-
return evaluateEntity(expr, location)
1549-
}
1550-
}
1551-
return evaluatorResult(nil, isSyntacticallyString, resolvedOtherFiles, hasExternalReferences)
1552-
}
1553-
return evaluate
1554-
}
1555-
15561411
func isInfinityOrNaNString(name string) bool {
15571412
return name == "Infinity" || name == "-Infinity" || name == "NaN"
15581413
}
@@ -1844,11 +1699,8 @@ func expressionResultIsUnused(node *ast.Node) bool {
18441699
}
18451700
}
18461701

1847-
func pseudoBigIntToString(value PseudoBigInt) string {
1848-
if value.negative && value.base10Value != "0" {
1849-
return "-" + value.base10Value
1850-
}
1851-
return value.base10Value
1702+
func pseudoBigIntToString(value jsnum.PseudoBigInt) string {
1703+
return value.String()
18521704
}
18531705

18541706
func getSuperContainer(node *ast.Node, stopOnFunctions bool) *ast.Node {

0 commit comments

Comments
 (0)