Skip to content

Commit 887237c

Browse files
authored
More type checking (#233)
1 parent 1030abf commit 887237c

File tree

11 files changed

+308
-82
lines changed

11 files changed

+308
-82
lines changed

internal/ast/ast.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ func (n *Node) Type() *Node {
334334
return n.AsJSDocNonNullableType().Type
335335
case KindJSDocOptionalType:
336336
return n.AsJSDocOptionalType().Type
337-
case KindEnumMember, KindBindingElement, KindExportAssignment:
337+
case KindEnumMember, KindBindingElement, KindExportAssignment, KindBinaryExpression:
338338
return nil
339339
default:
340340
funcLike := n.FunctionLikeData()
@@ -5637,6 +5637,19 @@ type PatternAmbientModule struct {
56375637
Symbol *Symbol
56385638
}
56395639

5640+
type CommentDirectiveKind int32
5641+
5642+
const (
5643+
CommentDirectiveKindUnknown CommentDirectiveKind = iota
5644+
CommentDirectiveKindExpectError
5645+
CommentDirectiveKindIgnore
5646+
)
5647+
5648+
type CommentDirective struct {
5649+
Loc core.TextRange
5650+
Kind CommentDirectiveKind
5651+
}
5652+
56405653
// SourceFile
56415654

56425655
type SourceFile struct {
@@ -5670,6 +5683,7 @@ type SourceFile struct {
56705683
ModuleAugmentations []*ModuleName // []ModuleName
56715684
PatternAmbientModules []PatternAmbientModule
56725685
AmbientModuleNames []string
5686+
CommentDirectives []CommentDirective
56735687
jsdocCache map[*Node][]*Node
56745688
Pragmas []Pragma
56755689
ReferencedFiles []*FileReference

internal/ast/utilities.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,7 +1231,7 @@ func GetNameOfDeclaration(declaration *Node) *Node {
12311231
func getNonAssignedNameOfDeclaration(declaration *Node) *Node {
12321232
switch declaration.Kind {
12331233
case KindBinaryExpression:
1234-
if isFunctionPropertyAssignment(declaration) {
1234+
if IsFunctionPropertyAssignment(declaration) {
12351235
return getElementOrPropertyAccessArgumentExpressionOrName(declaration.AsBinaryExpression().Left)
12361236
}
12371237
return nil
@@ -1278,7 +1278,7 @@ func getAssignedName(node *Node) *Node {
12781278
return nil
12791279
}
12801280

1281-
func isFunctionPropertyAssignment(node *Node) bool {
1281+
func IsFunctionPropertyAssignment(node *Node) bool {
12821282
if node.Kind == KindBinaryExpression {
12831283
expr := node.AsBinaryExpression()
12841284
if expr.OperatorToken.Kind == KindEqualsToken {

internal/binder/binder.go

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ func (b *Binder) bindWorker(node *ast.Node) bool {
595595
setFlowNode(node, b.currentFlow)
596596
}
597597
case ast.KindBinaryExpression:
598-
if isFunctionPropertyAssignment(node) {
598+
if ast.IsFunctionPropertyAssignment(node) {
599599
b.bindFunctionPropertyAssignment(node)
600600
}
601601
b.checkStrictModeBinaryExpression(node)
@@ -631,7 +631,7 @@ func (b *Binder) bindWorker(node *ast.Node) bool {
631631
case ast.KindCallSignature, ast.KindConstructSignature, ast.KindIndexSignature:
632632
b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsSignature, ast.SymbolFlagsNone)
633633
case ast.KindMethodDeclaration, ast.KindMethodSignature:
634-
b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsMethod|core.IfElse(getPostfixTokenFromNode(node) != nil, ast.SymbolFlagsOptional, ast.SymbolFlagsNone), core.IfElse(ast.IsObjectLiteralMethod(node), ast.SymbolFlagsPropertyExcludes, ast.SymbolFlagsMethodExcludes))
634+
b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsMethod|getOptionalSymbolFlagForNode(node), core.IfElse(ast.IsObjectLiteralMethod(node), ast.SymbolFlagsPropertyExcludes, ast.SymbolFlagsMethodExcludes))
635635
case ast.KindFunctionDeclaration:
636636
b.bindFunctionDeclaration(node)
637637
case ast.KindConstructor:
@@ -716,7 +716,7 @@ func (b *Binder) bindPropertyWorker(node *ast.Node) {
716716
isAutoAccessor := ast.IsAutoAccessorPropertyDeclaration(node)
717717
includes := core.IfElse(isAutoAccessor, ast.SymbolFlagsAccessor, ast.SymbolFlagsProperty)
718718
excludes := core.IfElse(isAutoAccessor, ast.SymbolFlagsAccessorExcludes, ast.SymbolFlagsPropertyExcludes)
719-
b.bindPropertyOrMethodOrAccessor(node, includes|core.IfElse(getPostfixTokenFromNode(node) != nil, ast.SymbolFlagsOptional, ast.SymbolFlagsNone), excludes)
719+
b.bindPropertyOrMethodOrAccessor(node, includes|getOptionalSymbolFlagForNode(node), excludes)
720720
}
721721

722722
func (b *Binder) bindSourceFileIfExternalModule() {
@@ -1082,7 +1082,7 @@ func addLateBoundAssignmentDeclarationToSymbol(node *ast.Node, symbol *ast.Symbo
10821082

10831083
func (b *Binder) bindFunctionPropertyAssignment(node *ast.Node) {
10841084
expr := node.AsBinaryExpression()
1085-
parentName := expr.Left.Expression().AsIdentifier().Text
1085+
parentName := expr.Left.Expression().Text()
10861086
parentSymbol := b.lookupName(parentName, b.blockScopeContainer)
10871087
if parentSymbol == nil {
10881088
parentSymbol = b.lookupName(parentName, b.container)
@@ -1235,7 +1235,7 @@ func (b *Binder) lookupName(name string, container *ast.Node) *ast.Symbol {
12351235
if localsContainer != nil {
12361236
local := localsContainer.Locals[name]
12371237
if local != nil {
1238-
return local
1238+
return core.OrElse(local.ExportSymbol, local)
12391239
}
12401240
}
12411241
if ast.IsSourceFile(container) {
@@ -2792,21 +2792,9 @@ func isSignedNumericLiteral(node *ast.Node) bool {
27922792
return false
27932793
}
27942794

2795-
func isFunctionPropertyAssignment(node *ast.Node) bool {
2796-
if node.Kind == ast.KindBinaryExpression {
2797-
expr := node.AsBinaryExpression()
2798-
if expr.OperatorToken.Kind == ast.KindEqualsToken {
2799-
switch expr.Left.Kind {
2800-
case ast.KindPropertyAccessExpression:
2801-
// F.id = expr
2802-
return ast.IsIdentifier(expr.Left.Expression()) && ast.IsIdentifier(expr.Left.Name())
2803-
case ast.KindElementAccessExpression:
2804-
// F[xxx] = expr
2805-
return ast.IsIdentifier(expr.Left.Expression())
2806-
}
2807-
}
2808-
}
2809-
return false
2795+
func getOptionalSymbolFlagForNode(node *ast.Node) ast.SymbolFlags {
2796+
postfixToken := getPostfixTokenFromNode(node)
2797+
return core.IfElse(postfixToken != nil && postfixToken.Kind == ast.KindQuestionToken, ast.SymbolFlagsOptional, ast.SymbolFlagsNone)
28102798
}
28112799

28122800
func getPostfixTokenFromNode(node *ast.Node) *ast.Node {

internal/compiler/checker.go

Lines changed: 86 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ type EnumLiteralKey struct {
9393
value any
9494
}
9595

96+
// EnumRelationKey
97+
98+
type EnumRelationKey struct {
99+
sourceId ast.SymbolId
100+
targetId ast.SymbolId
101+
}
102+
96103
// TypeCacheKind
97104

98105
type CachedTypeKind int32
@@ -149,11 +156,12 @@ type CachedSignatureKey struct {
149156
}
150157

151158
const (
152-
SignatureKeyErased string = "-"
153-
SignatureKeyCanonical string = "*"
154-
SignatureKeyBase string = "#"
155-
SignatureKeyInner string = "<"
156-
SignatureKeyOuter string = ">"
159+
SignatureKeyErased string = "-"
160+
SignatureKeyCanonical string = "*"
161+
SignatureKeyBase string = "#"
162+
SignatureKeyInner string = "<"
163+
SignatureKeyOuter string = ">"
164+
SignatureKeyImplementation string = "+"
157165
)
158166

159167
// StringMappingKey
@@ -705,7 +713,7 @@ type Checker struct {
705713
assignableRelation *Relation
706714
comparableRelation *Relation
707715
identityRelation *Relation
708-
enumRelation *Relation
716+
enumRelation map[EnumRelationKey]RelationComparisonResult
709717
getGlobalNonNullableTypeAliasOrNil func() *ast.Symbol
710718
getGlobalExtractSymbol func() *ast.Symbol
711719
getGlobalDisposableType func() *Type
@@ -889,7 +897,7 @@ func NewChecker(program *Program) *Checker {
889897
c.assignableRelation = &Relation{}
890898
c.comparableRelation = &Relation{}
891899
c.identityRelation = &Relation{}
892-
c.enumRelation = &Relation{}
900+
c.enumRelation = make(map[EnumRelationKey]RelationComparisonResult)
893901
c.getGlobalNonNullableTypeAliasOrNil = c.getGlobalTypeAliasResolver("NonNullable", 1 /*arity*/, false /*reportErrors*/)
894902
c.getGlobalExtractSymbol = c.getGlobalTypeAliasResolver("Extract", 2 /*arity*/, true /*reportErrors*/)
895903
c.getGlobalDisposableType = c.getGlobalTypeResolver("Disposable", 0 /*arity*/, true /*reportErrors*/)
@@ -3213,8 +3221,12 @@ func (c *Checker) checkExpressionCachedEx(node *ast.Node, checkMode CheckMode) *
32133221
// and requesting the contextual type might cause a circularity or other bad behaviour.
32143222
// It sets the contextual type of the node to any before calling getTypeOfExpression.
32153223
func (c *Checker) getContextFreeTypeOfExpression(node *ast.Node) *Type {
3224+
if cached := c.contextFreeTypes[node]; cached != nil {
3225+
return cached
3226+
}
32163227
c.pushContextualType(node, c.anyType, false /*isCache*/)
32173228
t := c.checkExpressionEx(node, CheckModeSkipContextSensitive)
3229+
c.contextFreeTypes[node] = t
32183230
c.popContextualType()
32193231
return t
32203232
}
@@ -4609,16 +4621,20 @@ func (c *Checker) chooseOverload(s *CallState, relation *Relation) *Signature {
46094621
var checkCandidate *Signature
46104622
var inferenceContext *InferenceContext
46114623
if len(candidate.typeParameters) != 0 {
4612-
// !!!
4613-
// // If we are *inside the body of candidate*, we need to create a clone of `candidate` with differing type parameter identities,
4614-
// // so our inference results for this call doesn't pollute expression types referencing the outer type parameter!
4615-
// paramLocation := candidate.typeParameters[0].symbol.Declarations[0]. /* ? */ parent
4616-
// candidateParameterContext := paramLocation || (ifElse(candidate.declaration != nil && isConstructorDeclaration(candidate.declaration), candidate.declaration.Parent, candidate.declaration))
4617-
// if candidateParameterContext != nil && findAncestor(node, func(a *ast.Node) bool {
4618-
// return a == candidateParameterContext
4619-
// }) != nil {
4620-
// candidate = c.getImplementationSignature(candidate)
4621-
// }
4624+
// If we are *inside the body of candidate*, we need to create a clone of `candidate` with differing type parameter identities,
4625+
// so our inference results for this call doesn't pollute expression types referencing the outer type parameter!
4626+
var candidateParameterContext *ast.Node
4627+
typeParamDeclaration := core.FirstOrNil(candidate.typeParameters[0].symbol.Declarations)
4628+
if typeParamDeclaration != nil {
4629+
candidateParameterContext = typeParamDeclaration.Parent
4630+
} else if candidate.declaration != nil && ast.IsConstructorDeclaration(candidate.declaration) {
4631+
candidateParameterContext = candidate.declaration.Parent
4632+
} else {
4633+
candidateParameterContext = candidate.declaration
4634+
}
4635+
if candidateParameterContext != nil && ast.FindAncestor(s.node, func(a *ast.Node) bool { return a == candidateParameterContext }) != nil {
4636+
candidate = c.getImplementationSignature(candidate)
4637+
}
46224638
var typeArgumentTypes []*Type
46234639
if len(s.typeArguments) != 0 {
46244640
typeArgumentTypes = c.checkTypeArguments(candidate, s.typeArguments, false /*reportErrors*/, nil)
@@ -4681,6 +4697,16 @@ func (c *Checker) chooseOverload(s *CallState, relation *Relation) *Signature {
46814697
return nil
46824698
}
46834699

4700+
func (c *Checker) getImplementationSignature(signature *Signature) *Signature {
4701+
key := CachedSignatureKey{sig: signature, key: SignatureKeyImplementation}
4702+
if cached := c.cachedSignatures[key]; cached != nil {
4703+
return cached
4704+
}
4705+
result := c.instantiateSignature(signature, newTypeMapper(nil, nil))
4706+
c.cachedSignatures[key] = result
4707+
return result
4708+
}
4709+
46844710
func (c *Checker) hasCorrectArity(node *ast.Node, args []*ast.Node, signature *Signature, signatureHelpTrailingComma bool) bool {
46854711
var argCount int
46864712
callIsIncomplete := false
@@ -10599,7 +10625,7 @@ func (c *Checker) getTypeOfVariableOrParameterOrPropertyWorker(symbol *ast.Symbo
1059910625
case ast.KindExportAssignment:
1060010626
result = c.widenTypeForVariableLikeDeclaration(c.checkExpressionCached(declaration.AsExportAssignment().Expression), declaration, false /*reportErrors*/)
1060110627
case ast.KindBinaryExpression:
10602-
result = c.getWidenedTypeForAssignmentDeclaration(symbol, nil)
10628+
result = c.getWidenedTypeForAssignmentDeclaration(symbol)
1060310629
case ast.KindJsxAttribute:
1060410630
result = c.checkJsxAttribute(declaration.AsJsxAttribute(), CheckModeNormal)
1060510631
case ast.KindEnumMember:
@@ -11842,8 +11868,14 @@ func (c *Checker) getTypeOfPrototypeProperty(prototype *ast.Symbol) *Type {
1184211868
return c.anyType // !!!
1184311869
}
1184411870

11845-
func (c *Checker) getWidenedTypeForAssignmentDeclaration(symbol *ast.Symbol, resolvedSymbol *ast.Symbol) *Type {
11846-
return c.anyType // !!!
11871+
func (c *Checker) getWidenedTypeForAssignmentDeclaration(symbol *ast.Symbol) *Type {
11872+
var types []*Type
11873+
for _, declaration := range symbol.Declarations {
11874+
if ast.IsBinaryExpression(declaration) {
11875+
types = core.AppendIfUnique(types, c.getWidenedLiteralType(c.checkExpressionCached(declaration.AsBinaryExpression().Right)))
11876+
}
11877+
}
11878+
return c.getWidenedType(c.getUnionType(types))
1184711879
}
1184811880

1184911881
func (c *Checker) widenTypeForVariableLikeDeclaration(t *Type, declaration *ast.Node, reportErrors bool) *Type {
@@ -13147,7 +13179,7 @@ func (c *Checker) getTypeWithThisArgument(t *Type, thisArgument *Type, needAppar
1314713179
func (c *Checker) addInheritedMembers(symbols ast.SymbolTable, baseSymbols []*ast.Symbol) ast.SymbolTable {
1314813180
for _, base := range baseSymbols {
1314913181
if !isStaticPrivateIdentifierProperty(base) {
13150-
if _, ok := symbols[base.Name]; !ok {
13182+
if s, ok := symbols[base.Name]; !ok || s.Flags&ast.SymbolFlagsValue == 0 {
1315113183
if symbols == nil {
1315213184
symbols = make(ast.SymbolTable)
1315313185
}
@@ -13655,18 +13687,13 @@ func (c *Checker) getReturnTypeFromBody(fn *ast.Node, checkMode CheckMode) *Type
1365513687
} else if len(returnTypes) != 0 {
1365613688
returnType = c.getUnionTypeEx(returnTypes, UnionReductionSubtype, nil, nil)
1365713689
}
13658-
// !!!
13659-
// TODO_IDENTIFIER := c.checkAndAggregateYieldOperandTypes(fn, checkMode)
13660-
// if core.Some(yieldTypes) {
13661-
// yieldType = c.getUnionType(yieldTypes, UnionReductionSubtype)
13662-
// } else {
13663-
// yieldType = nil
13664-
// }
13665-
// if core.Some(nextTypes) {
13666-
// nextType = c.getIntersectionType(nextTypes)
13667-
// } else {
13668-
// nextType = nil
13669-
// }
13690+
yieldTypes, nextTypes := c.checkAndAggregateYieldOperandTypes(fn, checkMode)
13691+
if len(yieldTypes) != 0 {
13692+
yieldType = c.getUnionTypeEx(yieldTypes, UnionReductionSubtype, nil, nil)
13693+
}
13694+
if len(nextTypes) != 0 {
13695+
nextType = c.getIntersectionType(nextTypes)
13696+
}
1367013697
default:
1367113698
types, isNeverReturning := c.checkAndAggregateReturnExpressionTypes(fn, checkMode)
1367213699
if isNeverReturning {
@@ -13821,6 +13848,28 @@ func mayReturnNever(fn *ast.Node) bool {
1382113848
return false
1382213849
}
1382313850

13851+
func (c *Checker) checkAndAggregateYieldOperandTypes(fn *ast.Node, checkMode CheckMode) (yieldTypes []*Type, nextTypes []*Type) {
13852+
isAsync := (getFunctionFlags(fn) & FunctionFlagsAsync) != 0
13853+
forEachYieldExpression(getBodyOfNode(fn), func(yieldExpr *ast.Node) {
13854+
yieldExprType := c.undefinedWideningType
13855+
if yieldExpr.Expression() != nil {
13856+
yieldExprType = c.checkExpressionEx(yieldExpr.Expression(), checkMode)
13857+
}
13858+
yieldTypes = core.AppendIfUnique(yieldTypes, c.getYieldedTypeOfYieldExpression(yieldExpr, yieldExprType, c.anyType, isAsync))
13859+
var nextType *Type
13860+
if yieldExpr.AsYieldExpression().AsteriskToken != nil {
13861+
iterationTypes := c.getIterationTypesOfIterable(yieldExprType, core.IfElse(isAsync, IterationUseAsyncYieldStar, IterationUseYieldStar), yieldExpr.Expression())
13862+
nextType = iterationTypes.nextType
13863+
} else {
13864+
nextType = c.getContextualType(yieldExpr, ContextFlagsNone)
13865+
}
13866+
if nextType != nil {
13867+
nextTypes = core.AppendIfUnique(nextTypes, nextType)
13868+
}
13869+
})
13870+
return
13871+
}
13872+
1382413873
func (c *Checker) createPromiseType(promisedType *Type) *Type {
1382513874
// creates a `Promise<T>` type where `T` is the promisedType argument
1382613875
globalPromiseType := c.getGlobalPromiseTypeChecked()
@@ -22011,7 +22060,8 @@ func (c *Checker) getContextualTypeForBinaryOperand(node *ast.Node, contextFlags
2201122060
switch binary.OperatorToken.Kind {
2201222061
case ast.KindEqualsToken, ast.KindAmpersandAmpersandEqualsToken, ast.KindBarBarEqualsToken, ast.KindQuestionQuestionEqualsToken:
2201322062
// In an assignment expression, the right operand is contextually typed by the type of the left operand.
22014-
if node == binary.Right {
22063+
// If the binary operator has a symbol, this is an assignment declaration and there is no contextual type.
22064+
if node == binary.Right && binary.Symbol == nil {
2201522065
return c.getTypeOfExpression(binary.Left)
2201622066
}
2201722067
case ast.KindBarBarToken, ast.KindQuestionQuestionToken:
@@ -22021,10 +22071,8 @@ func (c *Checker) getContextualTypeForBinaryOperand(node *ast.Node, contextFlags
2202122071
// by the type of the left operand, except for the special case of Javascript declarations of the form
2202222072
// `namespace.prop = namespace.prop || {}`.
2202322073
t := c.getContextualType(binary.AsNode(), contextFlags)
22024-
if t != nil && node == binary.Right {
22025-
if pattern := c.patternForType[t]; pattern != nil {
22026-
return c.getTypeOfExpression(binary.Left)
22027-
}
22074+
if node == binary.Right && (t == nil || c.patternForType[t] != nil) {
22075+
return c.getTypeOfExpression(binary.Left)
2202822076
}
2202922077
return t
2203022078
case ast.KindAmpersandAmpersandToken, ast.KindCommaToken:

0 commit comments

Comments
 (0)