diff --git a/client/package-lock.json b/client/package-lock.json index 9c2de6bff..5a0e04071 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1010,9 +1010,9 @@ } }, "typescript": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz", - "integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz", + "integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==", "dev": true }, "uc.micro": { diff --git a/client/package.json b/client/package.json index b715030d1..b1f22efc7 100644 --- a/client/package.json +++ b/client/package.json @@ -104,7 +104,7 @@ "postinstall": "node ./node_modules/vscode/bin/install" }, "devDependencies": { - "typescript": "^3.5.2", + "typescript": "^3.6.2", "vsce": "^1.64.0", "vscode": "^1.1.35" }, diff --git a/package-lock.json b/package-lock.json index 8e7af775b..eb38094b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,9 +17,9 @@ "dev": true }, "typescript": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz", - "integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz", + "integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==", "dev": true } } diff --git a/package.json b/package.json index d9d41506a..dc69d6018 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "devDependencies": { "@types/mocha": "^5.2.7", "@types/node": "^11.13.15", - "typescript": "^3.5.2" + "typescript": "^3.6.2" }, "main": "index.js", "bin": { diff --git a/server/package-lock.json b/server/package-lock.json index 0c5d05012..707c7865b 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -6387,9 +6387,9 @@ "dev": true }, "typescript": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz", - "integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz", + "integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==", "dev": true }, "typescript-char": { diff --git a/server/package.json b/server/package.json index 642e7b88e..74b70cbf8 100644 --- a/server/package.json +++ b/server/package.json @@ -38,7 +38,7 @@ "ts-loader": "^5.4.5", "tslint": "^5.18.0", "tslint-microsoft-contrib": "^6.2.0", - "typescript": "^3.5.2", + "typescript": "^3.6.2", "webpack": "^4.35.0", "webpack-cli": "^3.3.5" }, diff --git a/server/src/analyzer/expressionEvaluator.ts b/server/src/analyzer/expressionEvaluator.ts index 1b5679ac7..8a115347d 100644 --- a/server/src/analyzer/expressionEvaluator.ts +++ b/server/src/analyzer/expressionEvaluator.ts @@ -16,16 +16,13 @@ import { TextRangeDiagnosticSink } from '../common/diagnosticSink'; import { convertOffsetsToRange } from '../common/positionUtils'; import StringMap from '../common/stringMap'; import { TextRange } from '../common/textRange'; -import { ArgumentCategory, AssignmentNode, AugmentedAssignmentExpressionNode, - AwaitExpressionNode, BinaryExpressionNode, CallExpressionNode, ClassNode, - ConstantNode, DecoratorNode, DictionaryExpandEntryNode, DictionaryKeyEntryNode, - DictionaryNode, EllipsisNode, ErrorExpressionNode, ExpressionNode, - IndexExpressionNode, IndexItemsNode, LambdaNode, ListComprehensionForNode, - ListComprehensionIfNode, ListComprehensionNode, ListNode, MemberAccessExpressionNode, - NameNode, NumberNode, ParameterCategory, ParseNode, SetNode, SliceExpressionNode, - StatementListNode, StringListNode, TernaryExpressionNode, TupleExpressionNode, - TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode, - YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes'; +import { ArgumentCategory, AugmentedAssignmentExpressionNode, BinaryExpressionNode, + CallExpressionNode, ClassNode, ConstantNode, DecoratorNode, DictionaryNode, + ExpressionNode, IndexExpressionNode, IndexItemsNode, isExpressionNode, LambdaNode, + ListComprehensionNode, ListNode, MemberAccessExpressionNode, NameNode, ParameterCategory, + ParseNode, ParseNodeType, SetNode, SliceExpressionNode, TernaryExpressionNode, + TupleExpressionNode, UnaryExpressionNode, YieldExpressionNode, + YieldFromExpressionNode } from '../parser/parseNodes'; import { KeywordToken, KeywordType, OperatorType, StringTokenFlags, TokenType } from '../parser/tokenizerTypes'; import { ScopeUtils } from '../scopeUtils'; @@ -232,7 +229,7 @@ export class ExpressionEvaluator { // Gets a member type from an object and if it's a function binds // it to the object. - getTypeFromObjectMember(errorNode: ParseNode, objectType: ObjectType, memberName: string, + getTypeFromObjectMember(errorNode: ExpressionNode, objectType: ObjectType, memberName: string, usage: EvaluatorUsage, memberAccessFlags = MemberAccessFlags.None): Type | undefined { const memberInfo = this._getTypeFromClassMemberName(errorNode, @@ -250,7 +247,7 @@ export class ExpressionEvaluator { // Gets a member type from a class and if it's a function binds // it to the object. - getTypeFromClassMember(errorNode: ParseNode, classType: ClassType, memberName: string, + getTypeFromClassMember(errorNode: ExpressionNode, classType: ClassType, memberName: string, usage: EvaluatorUsage, memberAccessFlags = MemberAccessFlags.None): Type | undefined { const memberInfo = this._getTypeFromClassMemberName(errorNode, @@ -445,19 +442,19 @@ export class ExpressionEvaluator { }); node.suite.statements.forEach(statementList => { - if (statementList instanceof StatementListNode) { + if (statementList.nodeType === ParseNodeType.StatementList) { statementList.statements.forEach(statement => { let variableNameNode: NameNode | undefined; let variableType: Type | undefined; let hasDefaultValue = false; - if (statement instanceof AssignmentNode) { - if (statement.leftExpression instanceof NameNode) { + if (statement.nodeType === ParseNodeType.Assignment) { + if (statement.leftExpression.nodeType === ParseNodeType.Name) { variableNameNode = statement.leftExpression; variableType = TypeUtils.stripLiteralValue( this.getType(statement.rightExpression, { method: 'get' })); - } else if (statement.leftExpression instanceof TypeAnnotationExpressionNode && - statement.leftExpression.valueExpression instanceof NameNode) { + } else if (statement.leftExpression.nodeType === ParseNodeType.TypeAnnotation && + statement.leftExpression.valueExpression.nodeType === ParseNodeType.Name) { variableNameNode = statement.leftExpression.valueExpression; variableType = TypeUtils.convertClassToObject( @@ -466,8 +463,8 @@ export class ExpressionEvaluator { } hasDefaultValue = true; - } else if (statement instanceof TypeAnnotationExpressionNode) { - if (statement.valueExpression instanceof NameNode) { + } else if (statement.nodeType === ParseNodeType.TypeAnnotation) { + if (statement.valueExpression.nodeType === ParseNodeType.Name) { variableNameNode = statement.valueExpression; variableType = TypeUtils.convertClassToObject( this.getType(statement.typeAnnotation, { method: 'get' }, @@ -609,21 +606,21 @@ export class ExpressionEvaluator { let typeResult: TypeResult | undefined; - if (node instanceof NameNode) { + if (node.nodeType === ParseNodeType.Name) { typeResult = this._getTypeFromName(node, usage, flags); - } else if (node instanceof MemberAccessExpressionNode) { + } else if (node.nodeType === ParseNodeType.MemberAccess) { typeResult = this._getTypeFromMemberAccessExpression(node, usage, flags); - } else if (node instanceof IndexExpressionNode) { + } else if (node.nodeType === ParseNodeType.Index) { typeResult = this._getTypeFromIndexExpression(node, usage); - } else if (node instanceof CallExpressionNode) { + } else if (node.nodeType === ParseNodeType.Call) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromCallExpression(node, flags); - } else if (node instanceof TupleExpressionNode) { + } else if (node.nodeType === ParseNodeType.Tuple) { typeResult = this._getTypeFromTupleExpression(node, usage); - } else if (node instanceof ConstantNode) { + } else if (node.nodeType === ParseNodeType.Constant) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromConstantExpression(node); - } else if (node instanceof StringListNode) { + } else if (node.nodeType === ParseNodeType.StringList) { this._reportUsageErrorForReadOnly(node, usage); if (node.typeAnnotation && !AnalyzerNodeInfo.getIgnoreTypeAnnotation(node)) { let typeResult: TypeResult = { node, type: UnknownType.create() }; @@ -639,12 +636,12 @@ export class ExpressionEvaluator { const isBytes = (node.strings[0].token.flags & StringTokenFlags.Bytes) !== 0; typeResult = { node, type: this._cloneBuiltinTypeWithLiteral( - isBytes ? 'bytes' : 'str', node.getValue()) }; - } else if (node instanceof NumberNode) { + isBytes ? 'bytes' : 'str', node.strings.map(s => s.value).join('')) }; + } else if (node.nodeType === ParseNodeType.Number) { this._reportUsageErrorForReadOnly(node, usage); typeResult = { node, type: this._cloneBuiltinTypeWithLiteral( node.token.isInteger ? 'int' : 'float', node.token.value) }; - } else if (node instanceof EllipsisNode) { + } else if (node.nodeType === ParseNodeType.Ellipsis) { this._reportUsageErrorForReadOnly(node, usage); if ((flags & EvaluatorFlags.ConvertEllipsisToAny) !== 0) { typeResult = { type: AnyType.create(true), node }; @@ -653,61 +650,61 @@ export class ExpressionEvaluator { AnyType.create(); typeResult = { type: ellipsisType, node }; } - } else if (node instanceof UnaryExpressionNode) { + } else if (node.nodeType === ParseNodeType.UnaryOperation) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromUnaryExpression(node); - } else if (node instanceof BinaryExpressionNode) { + } else if (node.nodeType === ParseNodeType.BinaryOperation) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromBinaryExpression(node); - } else if (node instanceof AugmentedAssignmentExpressionNode) { + } else if (node.nodeType === ParseNodeType.AugmentedAssignment) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromAugmentedExpression(node); - } else if (node instanceof ListNode) { + } else if (node.nodeType === ParseNodeType.List) { typeResult = this._getTypeFromListExpression(node); - } else if (node instanceof SliceExpressionNode) { + } else if (node.nodeType === ParseNodeType.Slice) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromSliceExpression(node); - } else if (node instanceof AwaitExpressionNode) { + } else if (node.nodeType === ParseNodeType.Await) { typeResult = this._getTypeFromExpression( node.expression, { method: 'get' }, flags); typeResult = { type: this.getTypeFromAwaitable(typeResult.type, node.expression), node }; - } else if (node instanceof TernaryExpressionNode) { + } else if (node.nodeType === ParseNodeType.Ternary) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromTernaryExpression(node, flags); - } else if (node instanceof ListComprehensionNode) { + } else if (node.nodeType === ParseNodeType.ListComprehension) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromListComprehensionExpression(node); - } else if (node instanceof DictionaryNode) { + } else if (node.nodeType === ParseNodeType.Dictionary) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromDictionaryExpression(node); - } else if (node instanceof LambdaNode) { + } else if (node.nodeType === ParseNodeType.Lambda) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromLambdaExpression(node); - } else if (node instanceof SetNode) { + } else if (node.nodeType === ParseNodeType.Set) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromSetExpression(node); - } else if (node instanceof AssignmentNode) { + } else if (node.nodeType === ParseNodeType.Assignment) { this._reportUsageErrorForReadOnly(node, usage); // Don't validate the type match for the assignment here. Simply // return the type result of the RHS. typeResult = this._getTypeFromExpression(node.rightExpression); - } else if (node instanceof YieldExpressionNode) { + } else if (node.nodeType === ParseNodeType.Yield) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromYieldExpression(node); - } else if (node instanceof YieldFromExpressionNode) { + } else if (node.nodeType === ParseNodeType.YieldFrom) { this._reportUsageErrorForReadOnly(node, usage); typeResult = this._getTypeFromYieldFromExpression(node); - } else if (node instanceof UnpackExpressionNode) { + } else if (node.nodeType === ParseNodeType.Unpack) { const iterType = this._getTypeFromExpression(node.expression, usage).type; const type = this.getTypeFromIterable(iterType, false, node, false); typeResult = { type, node }; - } else if (node instanceof TypeAnnotationExpressionNode) { + } else if (node.nodeType === ParseNodeType.TypeAnnotation) { typeResult = this._getTypeFromExpression(node.typeAnnotation); - } else if (node instanceof ErrorExpressionNode) { + } else if (node.nodeType === ParseNodeType.Error) { // Evaluate the child expression as best we can so the // type information is cached for the completion handler. this._silenceDiagnostics(() => { @@ -971,7 +968,7 @@ export class ExpressionEvaluator { return undefined; } - private _getTypeFromClassMemberName(errorNode: ParseNode, classType: ClassType, memberName: string, + private _getTypeFromClassMemberName(errorNode: ExpressionNode, classType: ClassType, memberName: string, usage: EvaluatorUsage, flags: MemberAccessFlags): ClassMemberLookup | undefined { // If this is a special type (like "List") that has an alias @@ -1270,8 +1267,8 @@ export class ExpressionEvaluator { baseTypeClass.getClassName() === 'Tuple' && baseTypeClass.getTypeArguments()) { - if (node.items.items[0] instanceof NumberNode) { - const numberToken = (node.items.items[0] as NumberNode).token; + if (node.items.items[0].nodeType === ParseNodeType.Number) { + const numberToken = node.items.items[0].token; const baseClassTypeArgs = baseTypeClass.getTypeArguments()!; if (numberToken.isInteger && numberToken.value >= 0 && @@ -1324,7 +1321,7 @@ export class ExpressionEvaluator { private _getTypeArg(node: ExpressionNode): TypeResult { let typeResult: TypeResult; - if (node instanceof ListNode) { + if (node.nodeType === ParseNodeType.List) { typeResult = { type: UnknownType.create(), typeList: node.entries.map(entry => this._getTypeFromExpression(entry)), @@ -1361,7 +1358,7 @@ export class ExpressionEvaluator { { method: 'get' }, EvaluatorFlags.DoNotSpecialize); // Handle the built-in "super" call specially. - if (node.leftExpression instanceof NameNode && node.leftExpression.nameToken.value === 'super') { + if (node.leftExpression.nodeType === ParseNodeType.Name && node.leftExpression.nameToken.value === 'super') { return { type: this._getTypeFromSuperCall(node), node @@ -1447,8 +1444,9 @@ export class ExpressionEvaluator { // Python docs indicate that super() isn't valid for // operations other than member accesses. - if (node.parent! instanceof MemberAccessExpressionNode) { - const memberName = node.parent.memberName.nameToken.value; + const parentNode = node.parent!; + if (parentNode.nodeType === ParseNodeType.MemberAccess) { + const memberName = parentNode.memberName.nameToken.value; const lookupResults = TypeUtils.lookUpClassMember( targetClassType, memberName, ClassMemberLookupFlags.SkipOriginalClass); if (lookupResults && lookupResults.classType instanceof ClassType) { @@ -2052,8 +2050,8 @@ export class ExpressionEvaluator { } const firstArg = argList[0]; - if (firstArg.valueExpression instanceof StringListNode) { - typeVarName = firstArg.valueExpression.getValue(); + if (firstArg.valueExpression && firstArg.valueExpression.nodeType === ParseNodeType.StringList) { + typeVarName = firstArg.valueExpression.strings.map(s => s.value).join(''); } else { this._addError('Expected name of type var as first parameter', firstArg.valueExpression || errorNode); @@ -2134,7 +2132,7 @@ export class ExpressionEvaluator { } private _getBooleanValue(node: ExpressionNode): boolean { - if (node instanceof ConstantNode) { + if (node.nodeType === ParseNodeType.Constant) { if (node.token instanceof KeywordToken) { if (node.token.keywordType === KeywordType.False) { return false; @@ -2160,8 +2158,8 @@ export class ExpressionEvaluator { if (nameArg.argumentCategory !== ArgumentCategory.Simple) { this._addError('Expected enum class name as first parameter', argList[0].valueExpression || errorNode); - } else if (nameArg.valueExpression instanceof StringListNode) { - className = nameArg.valueExpression.getValue(); + } else if (nameArg.valueExpression && nameArg.valueExpression.nodeType === ParseNodeType.StringList) { + className = nameArg.valueExpression.strings.map(s => s.value).join(''); } } @@ -2192,11 +2190,12 @@ export class ExpressionEvaluator { } else { const entriesArg = argList[1]; if (entriesArg.argumentCategory !== ArgumentCategory.Simple || - !(entriesArg.valueExpression instanceof StringListNode)) { + !entriesArg.valueExpression || + entriesArg.valueExpression.nodeType !== ParseNodeType.StringList) { this._addError('Expected enum item string as second parameter', errorNode); } else { - const entries = entriesArg.valueExpression.getValue().split(' '); + const entries = entriesArg.valueExpression.strings.map(s => s.value).join('').split(' '); entries.forEach(entryName => { entryName = entryName.trim(); if (entryName) { @@ -2215,7 +2214,8 @@ export class ExpressionEvaluator { path: this._fileInfo.filePath, declaredType: entryType, range: convertOffsetsToRange( - stringNode.start, stringNode.end, this._fileInfo.lines) + stringNode.start, TextRange.getEnd(stringNode), + this._fileInfo.lines) }; newSymbol.addDeclaration(declaration); setSymbolPreservingAccess(classFields, entryName, newSymbol); @@ -2237,8 +2237,8 @@ export class ExpressionEvaluator { if (argList.length >= 1) { const nameArg = argList[0]; if (nameArg.argumentCategory === ArgumentCategory.Simple) { - if (nameArg.valueExpression instanceof StringListNode) { - className = nameArg.valueExpression.getValue(); + if (nameArg.valueExpression && nameArg.valueExpression.nodeType === ParseNodeType.StringList) { + className = nameArg.valueExpression.strings.map(s => s.value).join(''); } } } @@ -2286,8 +2286,8 @@ export class ExpressionEvaluator { if (nameArg.argumentCategory !== ArgumentCategory.Simple) { this._addError('Expected named tuple class name as first parameter', argList[0].valueExpression || errorNode); - } else if (nameArg.valueExpression instanceof StringListNode) { - className = nameArg.valueExpression.getValue(); + } else if (nameArg.valueExpression && nameArg.valueExpression.nodeType === ParseNodeType.StringList) { + className = nameArg.valueExpression.strings.map(s => s.value).join(''); } } @@ -2342,8 +2342,10 @@ export class ExpressionEvaluator { if (entriesArg.argumentCategory !== ArgumentCategory.Simple) { addGenericGetAttribute = true; } else { - if (!includesTypes && entriesArg.valueExpression instanceof StringListNode) { - const entries = entriesArg.valueExpression.getValue().split(' '); + if (!includesTypes && entriesArg.valueExpression && + entriesArg.valueExpression.nodeType === ParseNodeType.StringList) { + + const entries = entriesArg.valueExpression.strings.map(s => s.value).join('').split(' '); entries.forEach(entryName => { entryName = entryName.trim(); if (entryName) { @@ -2368,13 +2370,13 @@ export class ExpressionEvaluator { path: this._fileInfo.filePath, declaredType: entryType, range: convertOffsetsToRange( - stringNode.start, stringNode.end, this._fileInfo.lines) + stringNode.start, TextRange.getEnd(stringNode), this._fileInfo.lines) }; newSymbol.addDeclaration(declaration); setSymbolPreservingAccess(instanceFields, entryName, newSymbol); } }); - } else if (entriesArg.valueExpression instanceof ListNode) { + } else if (entriesArg.valueExpression && entriesArg.valueExpression.nodeType === ParseNodeType.List) { const entryList = entriesArg.valueExpression; const entryMap: { [name: string]: string } = {}; @@ -2385,7 +2387,7 @@ export class ExpressionEvaluator { if (includesTypes) { // Handle the variant that includes name/type tuples. - if (entry instanceof TupleExpressionNode && entry.expressions.length === 2) { + if (entry.nodeType === ParseNodeType.Tuple && entry.expressions.length === 2) { entryNameNode = entry.expressions[0]; const entryTypeInfo = this._getTypeFromExpression(entry.expressions[1]); if (entryTypeInfo) { @@ -2400,8 +2402,8 @@ export class ExpressionEvaluator { entryType = UnknownType.create(); } - if (entryNameNode instanceof StringListNode) { - entryName = entryNameNode.getValue(); + if (entryNameNode && entryNameNode.nodeType === ParseNodeType.StringList) { + entryName = entryNameNode.strings.map(s => s.value).join(''); if (!entryName) { this._addError( 'Names within a named tuple cannot be empty', entryNameNode); @@ -2443,7 +2445,8 @@ export class ExpressionEvaluator { path: this._fileInfo.filePath, declaredType: entryType, range: convertOffsetsToRange( - entryNameNode.start, entryNameNode.end, this._fileInfo.lines) + entryNameNode.start, TextRange.getEnd(entryNameNode), + this._fileInfo.lines) }; newSymbol.addDeclaration(declaration); } @@ -2878,7 +2881,7 @@ export class ExpressionEvaluator { // Infer the set type based on the entries. node.entries.forEach(entryNode => { - if (entryNode instanceof ListComprehensionNode) { + if (entryNode.nodeType === ParseNodeType.ListComprehension) { const setEntryType = this._getElementTypeFromListComprehensionExpression(entryNode); entryTypes.push(setEntryType); } else { @@ -2906,15 +2909,14 @@ export class ExpressionEvaluator { node.entries.forEach(entryNode => { let addUnknown = true; - if (entryNode instanceof DictionaryKeyEntryNode) { - + if (entryNode.nodeType === ParseNodeType.DictionaryKeyEntry) { keyTypes.push(TypeUtils.stripLiteralValue( this.getType(entryNode.keyExpression))); valueTypes.push(TypeUtils.stripLiteralValue( this.getType(entryNode.valueExpression))); addUnknown = false; - } else if (entryNode instanceof DictionaryExpandEntryNode) { + } else if (entryNode.nodeType === ParseNodeType.DictionaryExpandEntry) { const unexpandedType = this.getType(entryNode.expandExpression); if (unexpandedType.isAny()) { addUnknown = false; @@ -2936,9 +2938,9 @@ export class ExpressionEvaluator { } } } - } else if (entryNode instanceof ListComprehensionNode) { + } else if (entryNode.nodeType === ParseNodeType.ListComprehension) { const dictEntryType = this._getElementTypeFromListComprehensionExpression( - node.entries[0] as ListComprehensionNode); + node.entries[0] as ListComprehensionNode); // The result should be a Tuple if (dictEntryType instanceof ObjectType) { @@ -2985,9 +2987,8 @@ export class ExpressionEvaluator { private _getTypeFromListExpression(node: ListNode): TypeResult { let listEntryType: Type = AnyType.create(); - if (node.entries.length === 1 && node.entries[0] instanceof ListComprehensionNode) { - listEntryType = this._getElementTypeFromListComprehensionExpression( - node.entries[0] as ListComprehensionNode); + if (node.entries.length === 1 && node.entries[0].nodeType === ParseNodeType.ListComprehension) { + listEntryType = this._getElementTypeFromListComprehensionExpression(node.entries[0]); } else { const entryTypes = node.entries.map( entry => TypeUtils.stripLiteralValue(this.getType(entry))); @@ -3106,9 +3107,9 @@ export class ExpressionEvaluator { private _assignTypeToExpression(targetExpr: ExpressionNode, type: Type, srcExpr: ExpressionNode): boolean { let understoodType = true; - if (targetExpr instanceof NameNode) { + if (targetExpr.nodeType === ParseNodeType.Name) { this._assignTypeToNameNode(targetExpr, type); - } else if (targetExpr instanceof TupleExpressionNode) { + } else if (targetExpr.nodeType === ParseNodeType.Tuple) { // Initialize the array of target types, one for each target. const targetTypes: Type[][] = new Array(targetExpr.expressions.length); for (let i = 0; i < targetExpr.expressions.length; i++) { @@ -3186,7 +3187,7 @@ export class ExpressionEvaluator { for (let i = 0; i < node.comprehensions.length; i++) { const comprehension = node.comprehensions[i]; - if (comprehension instanceof ListComprehensionForNode) { + if (comprehension.nodeType === ParseNodeType.ListComprehensionFor) { const iterableType = TypeUtils.stripLiteralValue( this.getType(comprehension.iterableExpression)); const itemType = this.getTypeFromIterable(iterableType, !!comprehension.isAsync, @@ -3197,7 +3198,7 @@ export class ExpressionEvaluator { understoodType = false; break; } - } else if (comprehension instanceof ListComprehensionIfNode) { + } else if (comprehension.nodeType === ParseNodeType.ListComprehensionIf) { // Use the if node (if present) to create a type constraint. typeConstraints = TypeConstraintBuilder.buildTypeConstraintsForConditional( comprehension.testExpression, expr => TypeUtils.stripLiteralValue( @@ -3208,7 +3209,7 @@ export class ExpressionEvaluator { let type = UnknownType.create(); this._useExpressionTypeConstraint(typeConstraints, true, () => { if (understoodType) { - if (node.expression instanceof DictionaryKeyEntryNode) { + if (node.expression.nodeType === ParseNodeType.DictionaryKeyEntry) { // Create a tuple with the key/value types. const keyType = TypeUtils.stripLiteralValue( this.getType(node.expression.keyExpression)); @@ -3217,12 +3218,12 @@ export class ExpressionEvaluator { type = ScopeUtils.getBuiltInObject( this._scope, 'Tuple', [keyType, valueType]); - } else if (node.expression instanceof DictionaryExpandEntryNode) { + } else if (node.expression.nodeType === ParseNodeType.DictionaryExpandEntry) { const unexpandedType = this.getType(node.expression.expandExpression); // TODO - need to implement - } else if (node.expression instanceof ExpressionNode) { - type = TypeUtils.stripLiteralValue(this.getType(node.expression)); + } else if (isExpressionNode(node)) { + type = TypeUtils.stripLiteralValue(this.getType(node.expression as ExpressionNode)); } } }); @@ -3315,7 +3316,7 @@ export class ExpressionEvaluator { } // Creates an Optional[X, Y, Z] type. - private _createOptionalType(errorNode: ExpressionNode, typeArgs?: TypeResult[]): Type { + private _createOptionalType(errorNode: ParseNode, typeArgs?: TypeResult[]): Type { if (!typeArgs || typeArgs.length !== 1) { this._addError(`Expected one type parameter after Optional`, errorNode); return UnknownType.create(); @@ -3356,23 +3357,24 @@ export class ExpressionEvaluator { for (const item of node.items.items) { let type: Type | undefined; - if (item instanceof StringListNode) { + if (item.nodeType === ParseNodeType.StringList) { // Note that the contents of the string should not be treated // as a type annotation, as they normally are for quoted type // arguments. AnalyzerNodeInfo.setIgnoreTypeAnnotation(item); const isBytes = (item.strings[0].token.flags & StringTokenFlags.Bytes) !== 0; + const value = item.strings.map(s => s.value).join(''); if (isBytes) { - type = this._cloneBuiltinTypeWithLiteral('bytes', item.getValue()); + type = this._cloneBuiltinTypeWithLiteral('bytes', value); } else { - type = this._cloneBuiltinTypeWithLiteral('str', item.getValue()); + type = this._cloneBuiltinTypeWithLiteral('str', value); } - } else if (item instanceof NumberNode) { + } else if (item.nodeType === ParseNodeType.Number) { if (item.token.isInteger) { type = this._cloneBuiltinTypeWithLiteral('int', item.token.value); } - } else if (item instanceof ConstantNode) { + } else if (item.nodeType === ParseNodeType.Constant) { if (item.token.keywordType === KeywordType.True) { type = this._cloneBuiltinTypeWithLiteral('bool', true); } else if (item.token.keywordType === KeywordType.False) { @@ -3393,7 +3395,7 @@ export class ExpressionEvaluator { } // Creates a ClassVar type. - private _createClassVarType(errorNode: ExpressionNode, typeArgs: TypeResult[] | undefined): Type { + private _createClassVarType(errorNode: ParseNode, typeArgs: TypeResult[] | undefined): Type { if (!typeArgs || typeArgs.length === 0) { this._addError(`Expected a type parameter after ClassVar`, errorNode); return UnknownType.create(); @@ -3481,7 +3483,7 @@ export class ExpressionEvaluator { // Creates a type that represents "Generic[T1, T2, ...]", used in the // definition of a generic class. - private _createGenericType(errorNode: ExpressionNode, classType: ClassType, + private _createGenericType(errorNode: ParseNode, classType: ClassType, typeArgs?: TypeResult[]): Type { // Make sure there's at least one type arg. @@ -3619,7 +3621,7 @@ export class ExpressionEvaluator { // Returns the specialized type and a boolean indicating whether // the type indicates a class type (true) or an object type (false). private _createSpecializeClassType(classType: ClassType, typeArgs: TypeResult[] | undefined, - errorNode: ExpressionNode): Type { + errorNode: ParseNode): Type { // Handle the special-case classes that are not defined // in the type stubs. diff --git a/server/src/analyzer/expressionUtils.ts b/server/src/analyzer/expressionUtils.ts index 3d72fb2fe..e8e2f4b45 100644 --- a/server/src/analyzer/expressionUtils.ts +++ b/server/src/analyzer/expressionUtils.ts @@ -9,9 +9,7 @@ */ import { ExecutionEnvironment } from '../common/configOptions'; -import { BinaryExpressionNode, ConstantNode, ExpressionNode, IndexExpressionNode, - MemberAccessExpressionNode, NameNode, NumberNode, StringListNode, - TupleExpressionNode } from '../parser/parseNodes'; +import { ExpressionNode, NumberNode, ParseNodeType, TupleExpressionNode } from '../parser/parseNodes'; import { KeywordType, OperatorType } from '../parser/tokenizerTypes'; export class ExpressionUtils { @@ -20,50 +18,50 @@ export class ExpressionUtils { static evaluateConstantExpression(node: ExpressionNode, execEnv: ExecutionEnvironment): boolean | undefined { - if (node instanceof BinaryExpressionNode) { + if (node.nodeType === ParseNodeType.BinaryOperation) { if (this._isSysVersionInfoExpression(node.leftExpression) && - node.rightExpression instanceof TupleExpressionNode) { + node.rightExpression.nodeType === ParseNodeType.Tuple) { // Handle the special case of "sys.version_info >= (3, x)" const comparisonVersion = this._convertTupleToVersion(node.rightExpression); return this._evaluateNumericBinaryOperation(node.operator, execEnv.pythonVersion, comparisonVersion); - } else if (node.leftExpression instanceof IndexExpressionNode && + } else if (node.leftExpression.nodeType === ParseNodeType.Index && this._isSysVersionInfoExpression(node.leftExpression.baseExpression) && node.leftExpression.items.items.length === 1 && - node.leftExpression.items.items[0] instanceof NumberNode && - (node.leftExpression.items.items[0] as NumberNode).token.value === 0 && - node.rightExpression instanceof NumberNode) { + node.leftExpression.items.items[0].nodeType === ParseNodeType.Number && + node.leftExpression.items.items[0].token.value === 0 && + node.rightExpression.nodeType === ParseNodeType.Number) { // Handle the special case of "sys.version_info[0] >= X" return this._evaluateNumericBinaryOperation(node.operator, Math.floor(execEnv.pythonVersion / 256), node.rightExpression.token.value); } else if (this._isSysPlatformInfoExpression(node.leftExpression) && - node.rightExpression instanceof StringListNode) { + node.rightExpression.nodeType === ParseNodeType.StringList) { // Handle the special case of "sys.platform != 'X'" - const comparisonPlatform = node.rightExpression.getValue(); + const comparisonPlatform = node.rightExpression.strings.map(s => s.value).join(''); if (execEnv.pythonPlatform !== undefined) { return this._evaluateStringBinaryOperation(node.operator, execEnv.pythonPlatform, comparisonPlatform); } } else if (this._isOsNameInfoExpression(node.leftExpression) && - node.rightExpression instanceof StringListNode) { + node.rightExpression.nodeType === ParseNodeType.StringList) { // Handle the special case of "os.name == 'X'" - const comparisonOsName = node.rightExpression.getValue(); + const comparisonOsName = node.rightExpression.strings.map(s => s.value).join(''); const expectedOsName = this._getExpectedOsNameFromPlatform(execEnv); if (expectedOsName !== undefined) { return this._evaluateStringBinaryOperation(node.operator, expectedOsName, comparisonOsName); } } - } else if (node instanceof ConstantNode) { + } else if (node.nodeType === ParseNodeType.Constant) { if (node.token.keywordType === KeywordType.True) { return true; } else if (node.token.keywordType === KeywordType.False) { return false; } - } else if (node instanceof NameNode) { + } else if (node.nodeType === ParseNodeType.Name) { if (node.nameToken.value === 'TYPE_CHECKING') { return true; } @@ -75,10 +73,10 @@ export class ExpressionUtils { private static _convertTupleToVersion(node: TupleExpressionNode): number | undefined { let comparisonVersion: number | undefined; if (node.expressions.length === 2) { - if (node.expressions[0] instanceof NumberNode && - node.expressions[1] instanceof NumberNode) { - const majorVersion = node.expressions[0] as NumberNode; - const minorVersion = node.expressions[1] as NumberNode; + if (node.expressions[0].nodeType === ParseNodeType.Number && + node.expressions[1].nodeType === ParseNodeType.Number) { + const majorVersion = node.expressions[0]; + const minorVersion = node.expressions[1]; comparisonVersion = majorVersion.token.value * 256 + minorVersion.token.value; } } else if (node.expressions.length === 1) { @@ -124,8 +122,8 @@ export class ExpressionUtils { } private static _isSysVersionInfoExpression(node: ExpressionNode): boolean { - if (node instanceof MemberAccessExpressionNode) { - if (node.leftExpression instanceof NameNode && + if (node.nodeType === ParseNodeType.MemberAccess) { + if (node.leftExpression.nodeType === ParseNodeType.Name && node.leftExpression.nameToken.value === 'sys' && node.memberName.nameToken.value === 'version_info') { return true; @@ -136,8 +134,8 @@ export class ExpressionUtils { } private static _isSysPlatformInfoExpression(node: ExpressionNode): boolean { - if (node instanceof MemberAccessExpressionNode) { - if (node.leftExpression instanceof NameNode && + if (node.nodeType === ParseNodeType.MemberAccess) { + if (node.leftExpression.nodeType === ParseNodeType.Name && node.leftExpression.nameToken.value === 'sys' && node.memberName.nameToken.value === 'platform') { return true; @@ -148,8 +146,8 @@ export class ExpressionUtils { } private static _isOsNameInfoExpression(node: ExpressionNode): boolean { - if (node instanceof MemberAccessExpressionNode) { - if (node.leftExpression instanceof NameNode && + if (node.nodeType === ParseNodeType.MemberAccess) { + if (node.leftExpression.nodeType === ParseNodeType.Name && node.leftExpression.nameToken.value === 'os' && node.memberName.nameToken.value === 'name') { return true; diff --git a/server/src/analyzer/importStatementUtils.ts b/server/src/analyzer/importStatementUtils.ts index 4ca865d7a..7b8d50e6e 100644 --- a/server/src/analyzer/importStatementUtils.ts +++ b/server/src/analyzer/importStatementUtils.ts @@ -11,9 +11,9 @@ import { DiagnosticTextPosition } from '../common/diagnostic'; import { TextEditAction } from '../common/editAction'; import { convertOffsetToPosition } from '../common/positionUtils'; -import { AssignmentNode, ImportAsNode, ImportFromAsNode, ImportFromNode, - ImportNode, ModuleNameNode, ModuleNode, NameNode, StatementListNode, - StringListNode } from '../parser/parseNodes'; +import { TextRange } from '../common/textRange'; +import { ImportAsNode, ImportFromAsNode, ImportFromNode, ImportNode, + ModuleNameNode, ModuleNode, ParseNodeType } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; import { AnalyzerNodeInfo } from './analyzerNodeInfo'; import { ImportResult, ImportType } from './importResult'; @@ -46,13 +46,13 @@ export class ImportStatementUtils { let foundFirstImportStatement = false; parseTree.statements.forEach(statement => { - if (statement instanceof StatementListNode) { + if (statement.nodeType === ParseNodeType.StatementList) { statement.statements.forEach(subStatement => { - if (subStatement instanceof ImportNode) { + if (subStatement.nodeType === ParseNodeType.Import) { foundFirstImportStatement = true; this._processImportNode(subStatement, localImports, followsNonImportStatement); followsNonImportStatement = false; - } else if (subStatement instanceof ImportFromNode) { + } else if (subStatement.nodeType === ParseNodeType.ImportFrom) { foundFirstImportStatement = true; this._processImportFromNode(subStatement, localImports, followsNonImportStatement); followsNonImportStatement = false; @@ -77,7 +77,7 @@ export class ImportStatementUtils { // assuming we want to keep the imports alphebetized. let priorImport: ImportFromAsNode | undefined; - if (importStatement.node instanceof ImportFromNode) { + if (importStatement.node && importStatement.node.nodeType === ParseNodeType.ImportFrom) { for (const curImport of importStatement.node.imports) { if (priorImport && curImport.name.nameToken.value > symbolName) { break; @@ -87,7 +87,7 @@ export class ImportStatementUtils { } if (priorImport) { - const insertionOffset = priorImport.name.end; + const insertionOffset = TextRange.getEnd(priorImport.name); const insertionPosition = convertOffsetToPosition(insertionOffset, parseResults.lines); textEditList.push({ @@ -173,7 +173,7 @@ export class ImportStatementUtils { } insertionPosition = convertOffsetToPosition( - insertBefore ? insertionImport.node.start : insertionImport.node.end, + insertBefore ? insertionImport.node.start : TextRange.getEnd(insertionImport.node), parseResults.lines); } else { insertionPosition = { line: 0, column: 0 }; @@ -186,14 +186,14 @@ export class ImportStatementUtils { for (const statement of parseResults.parseTree.statements) { let stopHere = true; - if (statement instanceof StatementListNode && statement.statements.length === 1) { + if (statement.nodeType === ParseNodeType.StatementList && statement.statements.length === 1) { const simpleStatement = statement.statements[0]; - if (simpleStatement instanceof StringListNode) { + if (simpleStatement.nodeType === ParseNodeType.StringList) { // Assume that it's a file header doc string. stopHere = false; - } else if (simpleStatement instanceof AssignmentNode) { - if (simpleStatement.leftExpression instanceof NameNode) { + } else if (simpleStatement.nodeType === ParseNodeType.Assignment) { + if (simpleStatement.leftExpression.nodeType === ParseNodeType.Name) { if (SymbolUtils.isDunderName(simpleStatement.leftExpression.nameToken.value)) { // Assume that it's an assignment of __copyright__, __author__, etc. stopHere = false; @@ -208,7 +208,8 @@ export class ImportStatementUtils { addNewLineBefore = false; break; } else { - insertionPosition = convertOffsetToPosition(statement.end, + insertionPosition = convertOffsetToPosition( + statement.start + statement.length, parseResults.lines); addNewLineBefore = true; } @@ -292,7 +293,9 @@ export class ImportStatementUtils { // Overwrite existing import statements because we always want to prefer // 'import from' over 'import'. Also, overwrite existing 'import from' if // the module name is shorter. - if (!prevEntry || prevEntry.node instanceof ImportNode || prevEntry.moduleName.length > localImport.moduleName.length) { + if (!prevEntry || prevEntry.node.nodeType === ParseNodeType.Import || + prevEntry.moduleName.length > localImport.moduleName.length) { + localImports.mapByFilePath[resolvedPath] = localImport; } } diff --git a/server/src/analyzer/parseTreeCleaner.ts b/server/src/analyzer/parseTreeCleaner.ts index 840f817b2..2a775fa4b 100644 --- a/server/src/analyzer/parseTreeCleaner.ts +++ b/server/src/analyzer/parseTreeCleaner.ts @@ -28,8 +28,8 @@ export class ParseTreeCleanerWalker extends ParseTreeWalker { this.walk(this._parseTree); } - visitNode(node: ParseNode): boolean { + visitNode(node: ParseNode) { AnalyzerNodeInfo.cleanNodeAnalysisInfo(node); - return true; + return super.visitNode(node); } } diff --git a/server/src/analyzer/parseTreeUtils.ts b/server/src/analyzer/parseTreeUtils.ts index 71ce26346..497c1b0be 100644 --- a/server/src/analyzer/parseTreeUtils.ts +++ b/server/src/analyzer/parseTreeUtils.ts @@ -11,17 +11,10 @@ import { DiagnosticTextPosition } from '../common/diagnostic'; import { convertPositionToOffset } from '../common/positionUtils'; import { TextRange } from '../common/textRange'; import { TextRangeCollection } from '../common/textRangeCollection'; -import { ArgumentCategory, AssignmentNode, AugmentedAssignmentExpressionNode, - AwaitExpressionNode, BinaryExpressionNode, CallExpressionNode, ClassNode, - ConstantNode, DictionaryExpandEntryNode, DictionaryKeyEntryNode, DictionaryNode, - EllipsisNode, ExpressionNode, FunctionNode, IndexExpressionNode, LambdaNode, - ListComprehensionForNode, ListComprehensionNode, ListNode, MemberAccessExpressionNode, ModuleNode, - NameNode, NumberNode, ParameterCategory, ParseNode, SetNode, SliceExpressionNode, - StringListNode, StringNode, TernaryExpressionNode, TupleExpressionNode, - TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode, - YieldExpressionNode, - YieldFromExpressionNode } from '../parser/parseNodes'; +import { ArgumentCategory, ClassNode, ExpressionNode, FunctionNode, isExpressionNode, + ModuleNode, ParameterCategory, ParseNode, ParseNodeType } from '../parser/parseNodes'; import { KeywordType, OperatorType, StringTokenFlags } from '../parser/tokenizerTypes'; +import { ParseTreeWalker } from './parseTreeWalker'; export enum PrintExpressionFlags { None = 0, @@ -57,13 +50,15 @@ export class ParseTreeUtils { // Returns the deepest node that contains the specified offset. static findNodeByOffset(node: ParseNode, offset: number): ParseNode | undefined { - if (offset < node.start || offset > node.end) { + if (offset < node.start || offset > TextRange.getEnd(node)) { return undefined; } + const parseTreeWalker = new ParseTreeWalker(); + // The range is found within this node. See if we can localize it // further by checking its children. - const children = node.getChildren(); + const children = parseTreeWalker.visitNode(node); for (const child of children) { if (child) { const containingChild = ParseTreeUtils.findNodeByOffset(child, offset); @@ -77,12 +72,12 @@ export class ParseTreeUtils { } static printExpression(node: ExpressionNode, flags = PrintExpressionFlags.None): string { - if (node instanceof NameNode) { + if (node.nodeType === ParseNodeType.Name) { return node.nameToken.value; - } else if (node instanceof MemberAccessExpressionNode) { + } else if (node.nodeType === ParseNodeType.MemberAccess) { return ParseTreeUtils.printExpression(node.leftExpression, flags) + '.' + node.memberName.nameToken.value; - } else if (node instanceof CallExpressionNode) { + } else if (node.nodeType === ParseNodeType.Call) { return ParseTreeUtils.printExpression(node.leftExpression, flags) + '(' + node.arguments.map(arg => { let argStr = ''; @@ -98,20 +93,20 @@ export class ParseTreeUtils { return argStr; }).join(', ') + ')'; - } else if (node instanceof IndexExpressionNode) { + } else if (node.nodeType === ParseNodeType.Index) { return ParseTreeUtils.printExpression(node.baseExpression, flags) + '[' + node.items.items.map(item => this.printExpression(item, flags)).join(', ') + ']'; - } else if (node instanceof UnaryExpressionNode) { + } else if (node.nodeType === ParseNodeType.UnaryOperation) { return ParseTreeUtils.printOperator(node.operator) + ' ' + ParseTreeUtils.printExpression(node.expression, flags); - } else if (node instanceof BinaryExpressionNode) { + } else if (node.nodeType === ParseNodeType.BinaryOperation) { return ParseTreeUtils.printExpression(node.leftExpression, flags) + ' ' + ParseTreeUtils.printOperator(node.operator) + ' ' + ParseTreeUtils.printExpression(node.rightExpression, flags); - } else if (node instanceof NumberNode) { + } else if (node.nodeType === ParseNodeType.Number) { return node.token.value.toString(); - } else if (node instanceof StringListNode) { + } else if (node.nodeType === ParseNodeType.StringList) { if ((flags & PrintExpressionFlags.ForwardDeclarations) && node.typeAnnotation) { return ParseTreeUtils.printExpression(node.typeAnnotation, flags); } else { @@ -119,7 +114,7 @@ export class ParseTreeUtils { return ParseTreeUtils.printExpression(str, flags); }).join(' '); } - } else if (node instanceof StringNode) { + } else if (node.nodeType === ParseNodeType.String) { let exprString = ''; if (node.token.flags & StringTokenFlags.Raw) { exprString += 'r'; @@ -152,30 +147,30 @@ export class ParseTreeUtils { } return exprString; - } else if (node instanceof AssignmentNode) { + } else if (node.nodeType === ParseNodeType.Assignment) { return ParseTreeUtils.printExpression(node.leftExpression, flags) + ' = ' + ParseTreeUtils.printExpression(node.rightExpression, flags); - } else if (node instanceof TypeAnnotationExpressionNode) { + } else if (node.nodeType === ParseNodeType.TypeAnnotation) { return ParseTreeUtils.printExpression(node.valueExpression, flags) + ': ' + ParseTreeUtils.printExpression(node.typeAnnotation, flags); - } else if (node instanceof AugmentedAssignmentExpressionNode) { + } else if (node.nodeType === ParseNodeType.AugmentedAssignment) { return ParseTreeUtils.printExpression(node.leftExpression, flags) + ' ' + ParseTreeUtils.printOperator(node.operator) + ' ' + ParseTreeUtils.printExpression(node.rightExpression, flags); - } else if (node instanceof AwaitExpressionNode) { + } else if (node.nodeType === ParseNodeType.Await) { return 'await ' + ParseTreeUtils.printExpression(node.expression, flags); - } else if (node instanceof TernaryExpressionNode) { + } else if (node.nodeType === ParseNodeType.Ternary) { return ParseTreeUtils.printExpression(node.ifExpression, flags) + ' if ' + ParseTreeUtils.printExpression(node.testExpression, flags) + ' else ' + ParseTreeUtils.printExpression(node.elseExpression, flags); - } else if (node instanceof ListNode) { + } else if (node.nodeType === ParseNodeType.List) { const expressions = node.entries.map(expr => { return ParseTreeUtils.printExpression(expr, flags); }); return `[${ expressions.join(', ') }]`; - } else if (node instanceof UnpackExpressionNode) { + } else if (node.nodeType === ParseNodeType.Unpack) { return '*' + ParseTreeUtils.printExpression(node.expression, flags); - } else if (node instanceof TupleExpressionNode) { + } else if (node.nodeType === ParseNodeType.Tuple) { const expressions = node.expressions.map(expr => { return ParseTreeUtils.printExpression(expr, flags); }); @@ -183,18 +178,18 @@ export class ParseTreeUtils { return `(${ expressions[0] }, )`; } return `(${ expressions.join(', ') })`; - } else if (node instanceof YieldExpressionNode) { + } else if (node.nodeType === ParseNodeType.Yield) { return 'yield ' + ParseTreeUtils.printExpression(node.expression, flags); - } else if (node instanceof YieldFromExpressionNode) { + } else if (node.nodeType === ParseNodeType.YieldFrom) { return 'yield from ' + ParseTreeUtils.printExpression(node.expression, flags); - } else if (node instanceof EllipsisNode) { + } else if (node.nodeType === ParseNodeType.Ellipsis) { return '...'; - } else if (node instanceof ListComprehensionNode) { + } else if (node.nodeType === ParseNodeType.ListComprehension) { let listStr = ''; - if (node.expression instanceof ExpressionNode) { - listStr = ParseTreeUtils.printExpression(node.expression, flags); - } else if (node.expression instanceof DictionaryKeyEntryNode) { + if (isExpressionNode(node.expression)) { + listStr = ParseTreeUtils.printExpression(node.expression as ExpressionNode, flags); + } else if (node.expression.nodeType === ParseNodeType.DictionaryKeyEntry) { const keyStr = ParseTreeUtils.printExpression(node.expression.keyExpression, flags); const valueStr = ParseTreeUtils.printExpression(node.expression.valueExpression, flags); listStr = `${ keyStr }: ${ valueStr }`; @@ -202,7 +197,7 @@ export class ParseTreeUtils { return listStr + ' ' + node.comprehensions.map(expr => { - if (expr instanceof ListComprehensionForNode) { + if (expr.nodeType === ParseNodeType.ListComprehensionFor) { return `${ expr.isAsync ? 'async ' : '' }for ` + ParseTreeUtils.printExpression(expr.targetExpression, flags) + ` in ${ ParseTreeUtils.printExpression(expr.iterableExpression, flags) }`; @@ -210,7 +205,7 @@ export class ParseTreeUtils { return `if ${ ParseTreeUtils.printExpression(expr.testExpression, flags) }`; } }).join(' '); - } else if (node instanceof SliceExpressionNode) { + } else if (node.nodeType === ParseNodeType.Slice) { let result = ''; if (node.startValue) { result += ParseTreeUtils.printExpression(node.startValue, flags); @@ -222,7 +217,7 @@ export class ParseTreeUtils { result += ': ' + ParseTreeUtils.printExpression(node.stepValue, flags); } return result; - } else if (node instanceof LambdaNode) { + } else if (node.nodeType === ParseNodeType.Lambda) { return 'lambda ' + node.parameters.map(param => { let paramStr = ''; @@ -241,7 +236,7 @@ export class ParseTreeUtils { } return paramStr; }).join(', ') + ': ' + ParseTreeUtils.printExpression(node.expression, flags); - } else if (node instanceof ConstantNode) { + } else if (node.nodeType === ParseNodeType.Constant) { if (node.token.keywordType === KeywordType.True) { return 'True'; } else if (node.token.keywordType === KeywordType.False) { @@ -251,18 +246,18 @@ export class ParseTreeUtils { } else if (node.token.keywordType === KeywordType.None) { return 'None'; } - } else if (node instanceof DictionaryNode) { + } else if (node.nodeType === ParseNodeType.Dictionary) { return `{ ${ node.entries.map(entry => { - if (entry instanceof DictionaryKeyEntryNode) { + if (entry.nodeType === ParseNodeType.DictionaryKeyEntry) { return `${ ParseTreeUtils.printExpression(entry.keyExpression, flags) }: ` + `${ ParseTreeUtils.printExpression(entry.valueExpression, flags) }`; } else { return ParseTreeUtils.printExpression(entry, flags); } })} }`; - } else if (node instanceof DictionaryExpandEntryNode) { + } else if (node.nodeType === ParseNodeType.DictionaryExpandEntry) { return `**${ ParseTreeUtils.printExpression(node.expandExpression, flags) }`; - } else if (node instanceof SetNode) { + } else if (node.nodeType === ParseNodeType.Set) { return node.entries.map(entry => ParseTreeUtils.printExpression(entry, flags)).join(', '); } @@ -324,15 +319,15 @@ export class ParseTreeUtils { static getEnclosingClass(node: ParseNode, stopAtFunction = false): ClassNode | undefined { let curNode = node.parent; while (curNode) { - if (curNode instanceof ClassNode) { + if (curNode.nodeType === ParseNodeType.Class) { return curNode; } - if (curNode instanceof ModuleNode) { + if (curNode.nodeType === ParseNodeType.Module) { return undefined; } - if (curNode instanceof FunctionNode) { + if (curNode.nodeType === ParseNodeType.Function) { if (stopAtFunction) { return undefined; } @@ -349,15 +344,15 @@ export class ParseTreeUtils { let curNode = node.parent; while (curNode) { - if (curNode instanceof ClassNode) { + if (curNode.nodeType === ParseNodeType.Class) { return curNode; } - if (curNode instanceof ModuleNode) { + if (curNode.nodeType === ParseNodeType.Module) { return curNode; } - if (curNode instanceof FunctionNode) { + if (curNode.nodeType === ParseNodeType.Function) { if (stopAtFunction) { return undefined; } @@ -372,11 +367,11 @@ export class ParseTreeUtils { static getEnclosingFunction(node: ParseNode): FunctionNode | undefined { let curNode = node.parent; while (curNode) { - if (curNode instanceof FunctionNode) { + if (curNode.nodeType === ParseNodeType.Function) { return curNode; } - if (curNode instanceof ClassNode) { + if (curNode.nodeType === ParseNodeType.Class) { return undefined; } diff --git a/server/src/analyzer/parseTreeWalker.ts b/server/src/analyzer/parseTreeWalker.ts index 79458a1fd..10d950fb4 100644 --- a/server/src/analyzer/parseTreeWalker.ts +++ b/server/src/analyzer/parseTreeWalker.ts @@ -17,9 +17,9 @@ import { ArgumentNode, AssertNode, AssignmentNode, AugmentedAssignmentExpression ImportFromAsNode, ImportFromNode, ImportNode, IndexExpressionNode, IndexItemsNode, LambdaNode, ListComprehensionForNode, ListComprehensionIfNode, ListComprehensionNode, ListNode, MemberAccessExpressionNode, ModuleNameNode, ModuleNode, NameNode, NonlocalNode, - NumberNode, ParameterNode, ParseNode, ParseNodeArray, ParseNodeType, PassNode, RaiseNode, - ReturnNode, SetNode, SliceExpressionNode, StatementListNode, StringListNode, StringNode, - SuiteNode, TernaryExpressionNode, TryNode, TupleExpressionNode, + NumberNode, ParameterNode, ParseNode, ParseNodeArray, ParseNodeType, PassNode, + RaiseNode, ReturnNode, SetNode, SliceExpressionNode, StatementListNode, StringListNode, + StringNode, SuiteNode, TernaryExpressionNode, TryNode, TupleExpressionNode, TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode, WhileNode, WithItemNode, WithNode, YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes'; @@ -27,8 +27,9 @@ import { ArgumentNode, AssertNode, AssignmentNode, AugmentedAssignmentExpression // visitXXX methods that you want to handle. export class ParseTreeWalker { walk(node: ParseNode): void { - if (this.visitNode(node)) { - this.walkChildren(node); + const childrenToWalk = this.visitNode(node); + if (childrenToWalk.length > 0) { + this.walkMultiple(childrenToWalk); } } @@ -40,207 +41,392 @@ export class ParseTreeWalker { }); } - walkChildren(node: ParseNode) { - node.getChildren().forEach(node => { - if (node) { - this.walk(node); - } - }); - } - - visitNode(node: ParseNode): boolean { + // Calls the node-specific method (visitXXXX). If the method + // returns true, all child nodes for the node are returned. + // If the moethod returns false, we assume that the handler + // has already handled the child nodes, so an empty list is + // returned. + visitNode(node: ParseNode): ParseNodeArray { switch (node.nodeType) { case ParseNodeType.Argument: - return this.visitArgument(node as ArgumentNode); + if (this.visitArgument(node)) { + return [node.valueExpression]; + } + break; case ParseNodeType.Assert: - return this.visitAssert(node as AssertNode); + if (this.visitAssert(node)) { + return [node.testExpression, node.exceptionExpression]; + } + break; case ParseNodeType.Assignment: - return this.visitAssignment(node as AssignmentNode); + if (this.visitAssignment(node)) { + return [node.leftExpression, node.rightExpression, node.typeAnnotationComment]; + } + break; case ParseNodeType.AugmentedAssignment: - return this.visitAugmentedAssignment(node as AugmentedAssignmentExpressionNode); + if (this.visitAugmentedAssignment(node)) { + return [node.leftExpression, node.rightExpression]; + } + break; case ParseNodeType.Await: - return this.visitAwait(node as AwaitExpressionNode); + if (this.visitAwait(node)) { + return [node.expression]; + } + break; case ParseNodeType.BinaryOperation: - return this.visitBinaryOperation(node as BinaryExpressionNode); + if (this.visitBinaryOperation(node)) { + return [node.leftExpression, node.rightExpression]; + } + break; case ParseNodeType.Break: - return this.visitBreak(node as BreakNode); + if (this.visitBreak(node)) { + return []; + } + break; case ParseNodeType.Call: - return this.visitCall(node as CallExpressionNode); + if (this.visitCall(node)) { + return [node.leftExpression, ...node.arguments]; + } + break; case ParseNodeType.Class: - return this.visitClass(node as ClassNode); + if (this.visitClass(node)) { + return [...node.decorators, node.name, ...node.arguments, node.suite]; + } + break; case ParseNodeType.Ternary: - return this.visitTernary(node as TernaryExpressionNode); + if (this.visitTernary(node)) { + return [node.ifExpression, node.testExpression, node.elseExpression]; + } + break; case ParseNodeType.Constant: - return this.visitConstant(node as ConstantNode); + if (this.visitConstant(node)) { + return []; + } + break; case ParseNodeType.Continue: - return this.visitContinue(node as ContinueNode); + if (this.visitContinue(node)) { + return []; + } + break; case ParseNodeType.Decorator: - return this.visitDecorator(node as DecoratorNode); + if (this.visitDecorator(node)) { + return [node.leftExpression, ...(node.arguments || [])]; + } + break; case ParseNodeType.Del: - return this.visitDel(node as DelNode); + if (this.visitDel(node)) { + return node.expressions; + } + break; case ParseNodeType.Dictionary: - return this.visitDictionary(node as DictionaryNode); + if (this.visitDictionary(node)) { + return node.entries; + } + break; case ParseNodeType.DictionaryKeyEntry: - return this.visitDictionaryKeyEntry(node as DictionaryKeyEntryNode); + if (this.visitDictionaryKeyEntry(node)) { + return [node.keyExpression, node.valueExpression]; + } + break; case ParseNodeType.DictionaryExpandEntry: - return this.visitDictionaryExpandEntry(node as DictionaryExpandEntryNode); + if (this.visitDictionaryExpandEntry(node)) { + return [node.expandExpression]; + } + break; case ParseNodeType.Error: - return this.visitError(node as ErrorExpressionNode); + if (this.visitError(node)) { + return [node.child]; + } + break; case ParseNodeType.If: - return this.visitIf(node as IfNode); + if (this.visitIf(node)) { + return [node.testExpression, node.ifSuite, node.elseSuite]; + } + break; case ParseNodeType.Import: - return this.visitImport(node as ImportNode); + if (this.visitImport(node)) { + return node.list; + } + break; case ParseNodeType.ImportAs: - return this.visitImportAs(node as ImportAsNode); + if (this.visitImportAs(node)) { + return [node.module, node.alias]; + } + break; case ParseNodeType.ImportFrom: - return this.visitImportFrom(node as ImportFromNode); + if (this.visitImportFrom(node)) { + return [node.module, ...node.imports]; + } + break; case ParseNodeType.ImportFromAs: - return this.visitImportFromAs(node as ImportFromAsNode); + if (this.visitImportFromAs(node)) { + return [node.name, node.alias]; + } + break; case ParseNodeType.Index: - return this.visitIndex(node as IndexExpressionNode); + if (this.visitIndex(node)) { + return [node.baseExpression, node.items]; + } + break; case ParseNodeType.IndexItems: - return this.visitIndexItems(node as IndexItemsNode); + if (this.visitIndexItems(node)) { + return node.items; + } + break; case ParseNodeType.Ellipsis: - return this.visitEllipsis(node as EllipsisNode); + if (this.visitEllipsis(node)) { + return []; + } + break; case ParseNodeType.Except: - return this.visitExcept(node as ExceptNode); + if (this.visitExcept(node)) { + return [node.typeExpression, node.name, node.exceptSuite]; + } + break; case ParseNodeType.For: - return this.visitFor(node as ForNode); + if (this.visitFor(node)) { + return [node.targetExpression, node.iterableExpression, node.forSuite, node.elseSuite]; + } + break; case ParseNodeType.FormatString: - return this.visitFormatString(node as FormatStringNode); + if (this.visitFormatString(node)) { + return node.expressions; + } + break; case ParseNodeType.Function: - return this.visitFunction(node as FunctionNode); + if (this.visitFunction(node)) { + return [...node.decorators, node.name, ...node.parameters, + node.returnTypeAnnotation, node.suite]; + } + break; case ParseNodeType.Global: - return this.visitGlobal(node as GlobalNode); + if (this.visitGlobal(node)) { + return node.nameList; + } + break; case ParseNodeType.Lambda: - return this.visitLambda(node as LambdaNode); + if (this.visitLambda(node)) { + return [...node.parameters, node.expression]; + } + break; case ParseNodeType.List: - return this.visitList(node as ListNode); + if (this.visitList(node)) { + return node.entries; + } + break; case ParseNodeType.ListComprehension: - return this.visitListComprehension(node as ListComprehensionNode); + if (this.visitListComprehension(node)) { + return [node.expression, ...node.comprehensions]; + } + break; case ParseNodeType.ListComprehensionFor: - return this.visitListComprehensionFor(node as ListComprehensionForNode); + if (this.visitListComprehensionFor(node)) { + return [node.targetExpression, node.iterableExpression]; + } + break; case ParseNodeType.ListComprehensionIf: - return this.visitListComprehensionIf(node as ListComprehensionIfNode); + if (this.visitListComprehensionIf(node)) { + return [node.testExpression]; + } + break; case ParseNodeType.MemberAccess: - return this.visitMemberAccess(node as MemberAccessExpressionNode); + if (this.visitMemberAccess(node)) { + return [node.leftExpression, node.memberName]; + } + break; case ParseNodeType.Module: - return this.visitModule(node as ModuleNode); + if (this.visitModule(node)) { + return [...node.statements]; + } + break; case ParseNodeType.ModuleName: - return this.visitModuleName(node as ModuleNameNode); + if (this.visitModuleName(node)) { + return []; + } + break; case ParseNodeType.Name: - return this.visitName(node as NameNode); + if (this.visitName(node)) { + return []; + } + break; case ParseNodeType.Nonlocal: - return this.visitNonlocal(node as NonlocalNode); + if (this.visitNonlocal(node)) { + return node.nameList; + } + break; case ParseNodeType.Number: - return this.visitNumber(node as NumberNode); + if (this.visitNumber(node)) { + return []; + } + break; case ParseNodeType.Parameter: - return this.visitParameter(node as ParameterNode); + if (this.visitParameter(node)) { + return [node.name, node.typeAnnotation, node.defaultValue]; + } + break; case ParseNodeType.Pass: - return this.visitPass(node as PassNode); + if (this.visitPass(node)) { + return []; + } + break; case ParseNodeType.Raise: - return this.visitRaise(node as RaiseNode); + if (this.visitRaise(node)) { + return [node.typeExpression, node.valueExpression, node.tracebackExpression]; + } + break; case ParseNodeType.Return: - return this.visitReturn(node as ReturnNode); + if (this.visitReturn(node)) { + return [node.returnExpression]; + } + break; case ParseNodeType.Set: - return this.visitSet(node as SetNode); + if (this.visitSet(node)) { + return node.entries; + } + break; case ParseNodeType.Slice: - return this.visitSlice(node as SliceExpressionNode); + if (this.visitSlice(node)) { + return [node.startValue, node.endValue, node.stepValue]; + } + break; case ParseNodeType.StatementList: - return this.visitStatementList(node as StatementListNode); + if (this.visitStatementList(node)) { + return node.statements; + } + break; case ParseNodeType.String: - return this.visitString(node as StringNode); + if (this.visitString(node)) { + return []; + } + break; case ParseNodeType.StringList: - return this.visitStringList(node as StringListNode); + if (this.visitStringList(node)) { + return [node.typeAnnotation, ...node.strings]; + } + break; case ParseNodeType.Suite: - return this.visitSuite(node as SuiteNode); + if (this.visitSuite(node)) { + return [...node.statements]; + } + break; case ParseNodeType.Tuple: - return this.visitTuple(node as TupleExpressionNode); + if (this.visitTuple(node)) { + return node.expressions; + } + break; case ParseNodeType.Try: - return this.visitTry(node as TryNode); + if (this.visitTry(node)) { + return [node.trySuite, ...node.exceptClauses, node.elseSuite, node.finallySuite]; + } + break; case ParseNodeType.TypeAnnotation: - return this.visitTypeAnnotation(node as TypeAnnotationExpressionNode); + if (this.visitTypeAnnotation(node)) { + return [node.valueExpression, node.typeAnnotation]; + } + break; case ParseNodeType.UnaryOperation: - return this.visitUnaryOperation(node as UnaryExpressionNode); + if (this.visitUnaryOperation(node)) { + return [node.expression]; + } + break; case ParseNodeType.Unpack: - return this.visitUnpack(node as UnpackExpressionNode); + if (this.visitUnpack(node)) { + return [node.expression]; + } + break; case ParseNodeType.While: - return this.visitWhile(node as WhileNode); + if (this.visitWhile(node)) { + return [node.testExpression, node.whileSuite, node.elseSuite]; + } + break; case ParseNodeType.With: - return this.visitWith(node as WithNode); + if (this.visitWith(node)) { + return [...node.withItems, node.suite]; + } + break; case ParseNodeType.WithItem: - return this.visitWithItem(node as WithItemNode); + if (this.visitWithItem(node)) { + return [node.expression, node.target]; + } + break; case ParseNodeType.Yield: - return this.visitYield(node as YieldExpressionNode); + if (this.visitYield(node)) { + return [node.expression]; + } + break; case ParseNodeType.YieldFrom: - return this.visitYieldFrom(node as YieldFromExpressionNode); + if (this.visitYieldFrom(node)) { + return [node.expression]; + } + break; - case ParseNodeType.None: default: assert.fail('Unexpected node type'); - return true; + break; } + + return []; } // Override these methods as necessary. diff --git a/server/src/analyzer/postParseWalker.ts b/server/src/analyzer/postParseWalker.ts index 77d1e6eb9..67a43607f 100644 --- a/server/src/analyzer/postParseWalker.ts +++ b/server/src/analyzer/postParseWalker.ts @@ -18,9 +18,9 @@ import { TextRangeDiagnosticSink } from '../common/diagnosticSink'; import { NameBindings, NameBindingType } from '../parser/nameBindings'; import { AssignmentNode, AugmentedAssignmentExpressionNode, ClassNode, DelNode, ExpressionNode, ForNode, FunctionNode, GlobalNode, ImportAsNode, - ImportFromAsNode, ImportFromNode, LambdaNode, ListNode, ModuleNameNode, ModuleNode, - NameNode, NonlocalNode, ParseNode, TupleExpressionNode, - TypeAnnotationExpressionNode, UnpackExpressionNode, WithNode } from '../parser/parseNodes'; + ImportFromAsNode, ImportFromNode, LambdaNode, ModuleNameNode, ModuleNode, + NonlocalNode, ParseNode, ParseNodeArray, ParseNodeType, TypeAnnotationExpressionNode, + WithNode } from '../parser/parseNodes'; import { AnalyzerNodeInfo } from './analyzerNodeInfo'; import { ParseTreeWalker } from './parseTreeWalker'; @@ -62,17 +62,12 @@ export class PostParseWalker extends ParseTreeWalker { return this._importedModules; } - visitNode(node: ParseNode): boolean { - const children = node.getChildren(); + visitNode(node: ParseNode) { + const children = super.visitNode(node); - // Add the parent link to each of the child nodes. - children.forEach(child => { - if (child) { - child.parent = node; - } - }); + this._addParentLinks(node, children); - return super.visitNode(node); + return children; } visitImportAs(node: ImportAsNode): boolean { @@ -110,22 +105,17 @@ export class PostParseWalker extends ParseTreeWalker { this._addName(node.name.nameToken.value); } - return false; + return true; } visitWith(node: WithNode): boolean { - node.withItems.forEach(item => { - this.walk(item); - }); - node.withItems.forEach(item => { if (item.target) { this._addPossibleTupleNamedTarget(item.target); } }); - this.walk(node.suite); - return false; + return true; } visitFunction(node: FunctionNode): boolean { @@ -154,6 +144,12 @@ export class PostParseWalker extends ParseTreeWalker { this.walk(node.suite); }); + // Because we're returning false here, we need to + // call addParentLinks ourselves. + const children = [...node.decorators, node.name, ...node.parameters, + node.returnTypeAnnotation, node.suite]; + this._addParentLinks(node, children); + return false; } @@ -171,6 +167,11 @@ export class PostParseWalker extends ParseTreeWalker { this.walk(node.suite); }); + // Because we're returning false here, we need to + // call addParentLinks ourselves. + const children = [...node.decorators, node.name, ...node.arguments, node.suite]; + this._addParentLinks(node, children); + return false; } @@ -192,6 +193,11 @@ export class PostParseWalker extends ParseTreeWalker { this.walk(node.expression); }); + // Because we're returning false here, we need to + // call addParentLinks ourselves. + const children = [...node.parameters, node.expression]; + this._addParentLinks(node, children); + return false; } @@ -264,20 +270,29 @@ export class PostParseWalker extends ParseTreeWalker { return true; } + private _addParentLinks(parentNode: ParseNode, children: ParseNodeArray) { + // Add the parent link to each of the child nodes. + children.forEach(child => { + if (child) { + child.parent = parentNode; + } + }); + } + private _addPossibleTupleNamedTarget(node: ExpressionNode) { - if (node instanceof NameNode) { + if (node.nodeType === ParseNodeType.Name) { this._addName(node.nameToken.value); - } else if (node instanceof TupleExpressionNode) { + } else if (node.nodeType === ParseNodeType.Tuple) { node.expressions.forEach(expr => { this._addPossibleTupleNamedTarget(expr); }); - } else if (node instanceof ListNode) { + } else if (node.nodeType === ParseNodeType.List) { node.entries.forEach(expr => { this._addPossibleTupleNamedTarget(expr); }); - } else if (node instanceof TypeAnnotationExpressionNode) { + } else if (node.nodeType === ParseNodeType.TypeAnnotation) { this._addPossibleTupleNamedTarget(node.valueExpression); - } else if (node instanceof UnpackExpressionNode) { + } else if (node.nodeType === ParseNodeType.Unpack) { this._addPossibleTupleNamedTarget(node.expression); } } diff --git a/server/src/analyzer/semanticAnalyzer.ts b/server/src/analyzer/semanticAnalyzer.ts index 49c910d71..629888505 100644 --- a/server/src/analyzer/semanticAnalyzer.ts +++ b/server/src/analyzer/semanticAnalyzer.ts @@ -23,11 +23,11 @@ import { DiagnosticLevel } from '../common/configOptions'; import { CreateTypeStubFileAction } from '../common/diagnostic'; import { PythonVersion } from '../common/pythonVersion'; import { TextRange } from '../common/textRange'; -import { AwaitExpressionNode, ClassNode, ErrorExpressionNode, - ExpressionNode, FunctionNode, GlobalNode, IfNode, LambdaNode, ModuleNameNode, - ModuleNode, NonlocalNode, RaiseNode, StatementListNode, StatementNode, - StringListNode, SuiteNode, TryNode, TypeAnnotationExpressionNode, WhileNode, - YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes'; +import { AwaitExpressionNode, ClassNode, ErrorExpressionNode, ExpressionNode, FunctionNode, + GlobalNode, IfNode, LambdaNode, ModuleNameNode, ModuleNode, NonlocalNode, ParseNode, + ParseNodeType, RaiseNode, StatementNode, StringListNode, SuiteNode, TryNode, + TypeAnnotationExpressionNode, WhileNode, YieldExpressionNode, + YieldFromExpressionNode } from '../parser/parseNodes'; import { StringTokenUtils, UnescapeErrorType } from '../parser/stringTokenUtils'; import { StringTokenFlags } from '../parser/tokenizerTypes'; import { ScopeUtils } from '../scopeUtils'; @@ -283,10 +283,10 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker { // Find the function or module that contains this function and use its scope. // We can't simply use this._currentScope because functions within a class use // the scope of the containing function or module when they execute. - let functionOrModuleNode = node.parent; + let functionOrModuleNode: ParseNode | undefined = node.parent; while (functionOrModuleNode) { - if (functionOrModuleNode instanceof ModuleNode || - functionOrModuleNode instanceof FunctionNode) { + if (functionOrModuleNode.nodeType === ParseNodeType.Module || + functionOrModuleNode.nodeType === ParseNodeType.Function) { break; } @@ -399,7 +399,7 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker { unescapedResult.unescapeErrors.forEach(error => { const start = stringNode.token.start + stringNode.token.prefixLength + stringNode.token.quoteMarkLength + error.offset; - const textRange = new TextRange(start, error.length); + const textRange = { start, length: error.length }; if (error.errorType === UnescapeErrorType.InvalidEscapeSequence) { this._addDiagnostic(this._fileInfo.diagnosticSettings.reportInvalidStringEscapeSequence, @@ -503,19 +503,19 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker { return undefined; } - if (!(statemetns[0] instanceof StatementListNode)) { + if (statemetns[0].nodeType !== ParseNodeType.StatementList) { return undefined; } // If the first statement in the suite isn't a StringNode, // assume there is no docString. - const statementList = statemetns[0] as StatementListNode; + const statementList = statemetns[0]; if (statementList.statements.length === 0 || - !(statementList.statements[0] instanceof StringListNode)) { + statementList.statements[0].nodeType !== ParseNodeType.StringList) { return undefined; } - const docStringNode = statementList.statements[0] as StringListNode; + const docStringNode = statementList.statements[0]; const docStringToken = docStringNode.strings[0].token; // Ignore f-strings. @@ -532,7 +532,7 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker { if (!functionNode) { this._addError( `'yield' not allowed outside of a function`, node); - } else if (functionNode.isAsync && node instanceof YieldFromExpressionNode) { + } else if (functionNode.isAsync && node.nodeType === ParseNodeType.YieldFrom) { // PEP 525 indicates that 'yield from' is not allowed in an // async function. this._addError( @@ -605,7 +605,8 @@ export class ModuleScopeAnalyzer extends SemanticAnalyzer { assert(nameBindings !== undefined); this._addNamesToScope(nameBindings!.getGlobalNames()); - this.walkChildren(this._scopedNode); + const moduleNode = this._scopedNode as ModuleNode; + this.walkMultiple(moduleNode.statements); // Associate the module's scope with the module type. const moduleType = new ModuleType(this._currentScope.getSymbolTable(), diff --git a/server/src/analyzer/sourceFile.ts b/server/src/analyzer/sourceFile.ts index a9582521a..117be91c7 100644 --- a/server/src/analyzer/sourceFile.ts +++ b/server/src/analyzer/sourceFile.ts @@ -467,18 +467,14 @@ export class SourceFile { this._analysisJob.parseResults.tokens, configOptions.diagnosticSettings, useStrict); } catch (e) { - let message: string; - if (e instanceof Error) { - message = e.stack || e.message; - } else { - message = JSON.stringify(e); - } - + const message: string = (e.stack ? e.stack.toString() : undefined) || + (typeof e.message === 'string' ? e.message : undefined) || + JSON.stringify(e); this._console.log( `An internal error occurred while parsing ${ this.getFilePath() }: ` + message); this._analysisJob.parseResults = { - parseTree: new ModuleNode(new TextRange(0, 0)), + parseTree: ModuleNode.create({ start: 0, length: 0 }), futureImports: new StringMap(), tokens: new TextRangeCollection([]), lines: new TextRangeCollection([]), @@ -650,13 +646,9 @@ export class SourceFile { assert(moduleType instanceof ModuleType); this._analysisJob.moduleType = moduleType as ModuleType; } catch (e) { - let message: string; - if (e instanceof Error) { - message = e.stack || e.message; - } else { - message = JSON.stringify(e); - } - + const message: string = (e.stack ? e.stack.toString() : undefined) || + (typeof e.message === 'string' ? e.message : undefined) || + JSON.stringify(e); this._console.log( `An internal error occurred while performing semantic analysis for ${ this.getFilePath() }: ` + message); @@ -701,13 +693,9 @@ export class SourceFile { } }); } catch (e) { - let message: string; - if (e instanceof Error) { - message = e.stack || e.message; - } else { - message = JSON.stringify(e); - } - + const message: string = (e.stack ? e.stack.toString() : undefined) || + (typeof e.message === 'string' ? e.message : undefined) || + JSON.stringify(e); this._console.log( `An internal error occurred while while performing type analysis for ${ this.getFilePath() }: ` + message); const diagSink = new DiagnosticSink(); diff --git a/server/src/analyzer/typeAnalyzer.ts b/server/src/analyzer/typeAnalyzer.ts index 49775043f..3db500239 100644 --- a/server/src/analyzer/typeAnalyzer.ts +++ b/server/src/analyzer/typeAnalyzer.ts @@ -17,16 +17,16 @@ import { convertOffsetsToRange } from '../common/positionUtils'; import { PythonVersion } from '../common/pythonVersion'; import { TextRange } from '../common/textRange'; import { AssertNode, AssignmentNode, AugmentedAssignmentExpressionNode, - BinaryExpressionNode, BreakNode, CallExpressionNode, ClassNode, ConstantNode, + BinaryExpressionNode, BreakNode, CallExpressionNode, ClassNode, ContinueNode, DecoratorNode, DelNode, ErrorExpressionNode, ExceptNode, ExpressionNode, - FormatStringNode, ForNode, FunctionNode, IfNode, ImportAsNode, - ImportFromNode, IndexExpressionNode, LambdaNode, ListComprehensionForNode, - ListComprehensionNode, ListNode, MemberAccessExpressionNode, ModuleNode, NameNode, - ParameterCategory, ParseNode, RaiseNode, ReturnNode, SliceExpressionNode, - StringListNode, StringNode, SuiteNode, TernaryExpressionNode, TryNode, - TupleExpressionNode, TypeAnnotationExpressionNode, UnaryExpressionNode, - UnpackExpressionNode, WhileNode, WithNode, YieldExpressionNode, - YieldFromExpressionNode } from '../parser/parseNodes'; + FormatStringNode, ForNode, FunctionNode, IfNode, + ImportAsNode, ImportFromNode, IndexExpressionNode, LambdaNode, + ListComprehensionNode, MemberAccessExpressionNode, ModuleNode, + NameNode, ParameterCategory, ParameterNode, ParseNode, ParseNodeType, + RaiseNode, ReturnNode, SliceExpressionNode, StringListNode, + SuiteNode, TernaryExpressionNode, TryNode, + TupleExpressionNode, TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode, + WhileNode, WithNode, YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes'; import { KeywordType } from '../parser/tokenizerTypes'; import { ScopeUtils } from '../scopeUtils'; import { AnalyzerFileInfo } from './analyzerFileInfo'; @@ -288,7 +288,8 @@ export class TypeAnalyzer extends ParseTreeWalker { node: node.name, declaredType: decoratedType, path: this._fileInfo.filePath, - range: convertOffsetsToRange(node.name.start, node.name.end, this._fileInfo.lines) + range: convertOffsetsToRange(node.name.start, + TextRange.getEnd(node.name), this._fileInfo.lines) }; this._assignTypeToNameNode(node.name, decoratedType, declaration); @@ -366,7 +367,7 @@ export class TypeAnalyzer extends ParseTreeWalker { } } - node.parameters.forEach((param, index) => { + node.parameters.forEach((param: ParameterNode, index) => { let annotatedType: Type | undefined; let defaultValueType: Type | undefined; @@ -386,7 +387,7 @@ export class TypeAnalyzer extends ParseTreeWalker { // PEP 484 indicates that if a parameter has a default value of 'None' // the type checker should assume that the type is optional (i.e. a union // of the specified type and 'None'). - if (param.defaultValue instanceof ConstantNode) { + if (param.defaultValue && param.defaultValue.nodeType === ParseNodeType.Constant) { if (param.defaultValue.token.keywordType === KeywordType.None) { isNoneWithoutOptional = true; @@ -514,7 +515,8 @@ export class TypeAnalyzer extends ParseTreeWalker { category: DeclarationCategory.Parameter, node: paramNode, path: this._fileInfo.filePath, - range: convertOffsetsToRange(paramNode.start, paramNode.end, this._fileInfo.lines), + range: convertOffsetsToRange(paramNode.start, TextRange.getEnd(paramNode), + this._fileInfo.lines), declaredType: specializedParamType }; assert(paramNode !== undefined && paramNode.name !== undefined); @@ -542,7 +544,8 @@ export class TypeAnalyzer extends ParseTreeWalker { category: containingClassNode ? DeclarationCategory.Method : DeclarationCategory.Function, node: node.name, path: this._fileInfo.filePath, - range: convertOffsetsToRange(node.name.start, node.name.end, this._fileInfo.lines), + range: convertOffsetsToRange(node.name.start, TextRange.getEnd(node.name), + this._fileInfo.lines), declaredType: decoratedType }; this._assignTypeToNameNode(node.name, decoratedType, declaration); @@ -579,7 +582,8 @@ export class TypeAnalyzer extends ParseTreeWalker { category: DeclarationCategory.Parameter, node: param, path: this._fileInfo.filePath, - range: convertOffsetsToRange(param.start, param.end, this._fileInfo.lines) + range: convertOffsetsToRange(param.start, TextRange.getEnd(param), + this._fileInfo.lines) }; const paramType = UnknownType.create(); this._addTypeSourceToNameNode(param.name, paramType, declaration); @@ -602,7 +606,8 @@ export class TypeAnalyzer extends ParseTreeWalker { functionType.getInferredReturnType().addSource( returnType, AnalyzerNodeInfo.getTypeSourceId(node.expression)); - this.walkChildren(node.expression); + // Walk the children. + this.walkMultiple([...node.parameters, node.expression]); }); // Cache the function type. @@ -684,7 +689,7 @@ export class TypeAnalyzer extends ParseTreeWalker { // when complete. this._enterTemporaryScope(() => { node.comprehensions.forEach(compr => { - if (compr instanceof ListComprehensionForNode) { + if (compr.nodeType === ParseNodeType.ListComprehensionFor) { this.walk(compr.iterableExpression); const iteratorType = this._getTypeOfExpression(compr.iterableExpression); @@ -987,7 +992,8 @@ export class TypeAnalyzer extends ParseTreeWalker { category: DeclarationCategory.Variable, node: node.name, path: this._fileInfo.filePath, - range: convertOffsetsToRange(node.name.start, node.name.end, this._fileInfo.lines) + range: convertOffsetsToRange(node.name.start, TextRange.getEnd(node.name), + this._fileInfo.lines) }; this._addNamedTargetToCurrentScope(node.name); this._assignTypeToNameNode(node.name, exceptionType, declaration); @@ -1119,7 +1125,7 @@ export class TypeAnalyzer extends ParseTreeWalker { // If this is an enum, transform the type as required. let effectiveType = srcType; - if (node.leftExpression instanceof NameNode && !node.typeAnnotationComment) { + if (node.leftExpression.nodeType === ParseNodeType.Name && !node.typeAnnotationComment) { effectiveType = this._transformTypeForPossibleEnumClass( node.leftExpression, effectiveType); } @@ -1139,7 +1145,7 @@ export class TypeAnalyzer extends ParseTreeWalker { // Did the caller pass an optional assert message as a second parameter? // If so, strip it off and include only the test. - if (node.testExpression instanceof TupleExpressionNode) { + if (node.testExpression.nodeType === ParseNodeType.Tuple) { assertTestExpression = node.testExpression.expressions[0]; } @@ -1217,7 +1223,7 @@ export class TypeAnalyzer extends ParseTreeWalker { visitFormatString(node: FormatStringNode): boolean { node.expressions.forEach(formatExpr => { - this._getTypeOfExpression(formatExpr.expression, + this._getTypeOfExpression(formatExpr, EvaluatorFlags.AllowForwardReferences); }); @@ -1261,7 +1267,7 @@ export class TypeAnalyzer extends ParseTreeWalker { this._markExpressionAccessed(expr); this._evaluateExpressionForDeletion(expr); - if (expr instanceof NameNode) { + if (expr.nodeType === ParseNodeType.Name) { const symbolWithScope = this._currentScope.lookUpSymbolRecursive(expr.nameToken.value); if (symbolWithScope) { if (symbolWithScope.symbol.hasDeclarations()) { @@ -1507,7 +1513,7 @@ export class TypeAnalyzer extends ParseTreeWalker { let typeHintType = this._getTypeOfAnnotation(node.typeAnnotation); // If this is within an enum, transform the type. - if (node.valueExpression instanceof NameNode) { + if (node.valueExpression && node.valueExpression.nodeType === ParseNodeType.Name) { typeHintType = this._transformTypeForPossibleEnumClass( node.valueExpression, typeHintType); } @@ -1552,8 +1558,9 @@ export class TypeAnalyzer extends ParseTreeWalker { // Create a text range that covers the next statement through // the end of the suite. const start = node.statements[index + 1].start; - const end = node.statements[node.statements.length - 1].end; - this._addUnusedCode(new TextRange(start, end - start)); + const lastStatement = node.statements[node.statements.length - 1]; + const end = TextRange.getEnd(lastStatement); + this._addUnusedCode({ start, length: end - start }); } // Note that we already reported this so we don't do it again. @@ -1572,7 +1579,7 @@ export class TypeAnalyzer extends ParseTreeWalker { return; } - if (!(node.leftExpression instanceof NameNode) || + if (node.leftExpression.nodeType !== ParseNodeType.Name || node.leftExpression.nameToken.value !== 'isinstance' || node.arguments.length !== 2) { return; @@ -1687,7 +1694,7 @@ export class TypeAnalyzer extends ParseTreeWalker { return false; } - if (node.leftExpression instanceof NameNode) { + if (node.leftExpression.nodeType === ParseNodeType.Name) { const assignedName = node.leftExpression.nameToken.value; let specialType: Type | undefined; @@ -1754,14 +1761,14 @@ export class TypeAnalyzer extends ParseTreeWalker { node: node.leftExpression, path: this._fileInfo.filePath, range: convertOffsetsToRange(node.leftExpression.start, - node.leftExpression.end, this._fileInfo.lines) + TextRange.getEnd(node.leftExpression), this._fileInfo.lines) }; this._assignTypeToNameNode(node.leftExpression, specialType, declaration); this._updateExpressionTypeForNode(node.leftExpression, specialType); return true; } - } else if (node.leftExpression instanceof TypeAnnotationExpressionNode && - node.leftExpression.valueExpression instanceof NameNode) { + } else if (node.leftExpression.nodeType === ParseNodeType.TypeAnnotation && + node.leftExpression.valueExpression.nodeType === ParseNodeType.Name) { const nameNode = node.leftExpression.valueExpression; const assignedName = nameNode.nameToken.value; @@ -1804,7 +1811,7 @@ export class TypeAnalyzer extends ParseTreeWalker { node: nameNode, path: this._fileInfo.filePath, range: convertOffsetsToRange(nameNode.start, - nameNode.end, this._fileInfo.lines) + TextRange.getEnd(nameNode), this._fileInfo.lines) }; this._assignTypeToNameNode(nameNode, specialType, declaration); this._updateExpressionTypeForNode(nameNode, specialType); @@ -1869,7 +1876,7 @@ export class TypeAnalyzer extends ParseTreeWalker { let declarationHandled = false; - if (target instanceof NameNode) { + if (target.nodeType === ParseNodeType.Name) { const name = target.nameToken; const declaration: Declaration = { category: DeclarationCategory.Variable, @@ -1877,7 +1884,7 @@ export class TypeAnalyzer extends ParseTreeWalker { isConstant: SymbolUtils.isConstantName(name.value), path: this._fileInfo.filePath, declaredType, - range: convertOffsetsToRange(name.start, name.end, this._fileInfo.lines) + range: convertOffsetsToRange(name.start, TextRange.getEnd(name), this._fileInfo.lines) }; const symbolWithScope = this._currentScope.lookUpSymbolRecursive(name.value); @@ -1886,11 +1893,11 @@ export class TypeAnalyzer extends ParseTreeWalker { } AnalyzerNodeInfo.setDeclarations(target, [declaration]); declarationHandled = true; - } else if (target instanceof MemberAccessExpressionNode) { + } else if (target.nodeType === ParseNodeType.MemberAccess) { const targetNode = target.leftExpression; // Handle member accesses (e.g. self.x or cls.y). - if (targetNode instanceof NameNode) { + if (targetNode && targetNode.nodeType === ParseNodeType.Name) { // Determine whether we're writing to a class or instance member. const enclosingClassNode = ParseTreeUtils.getEnclosingClass(target); @@ -2028,14 +2035,14 @@ export class TypeAnalyzer extends ParseTreeWalker { if (primaryDeclaration.node && primaryDeclaration.node.parent && primaryDeclaration.node.parent === classOrModuleNode && - classOrModuleNode instanceof ClassNode) { + classOrModuleNode.nodeType === ParseNodeType.Class) { classOrModuleNode = ParseTreeUtils.getEnclosingClassOrModule(classOrModuleNode); } // If it's a class member, check whether it's a legal protected access. let isProtectedAccess = false; - if (classOrModuleNode instanceof ClassNode) { + if (classOrModuleNode && classOrModuleNode.nodeType === ParseNodeType.Class) { if (isProtectedName) { const declarationClassType = AnalyzerNodeInfo.getExpressionType(classOrModuleNode); if (declarationClassType && declarationClassType instanceof ClassType) { @@ -2065,7 +2072,7 @@ export class TypeAnalyzer extends ParseTreeWalker { `'${ nameValue }' is protected and used outside of a derived class`, node); } else { - const scopeName = classOrModuleNode instanceof ClassNode ? + const scopeName = classOrModuleNode.nodeType === ParseNodeType.Class ? 'class' : 'module'; this._addDiagnostic(this._fileInfo.diagnosticSettings.reportPrivateUsage, @@ -2325,7 +2332,7 @@ export class TypeAnalyzer extends ParseTreeWalker { } // Handle property setters and deleters. - if (decoratorNode.leftExpression instanceof MemberAccessExpressionNode) { + if (decoratorNode.leftExpression.nodeType === ParseNodeType.MemberAccess) { const baseType = this._getTypeOfExpression(decoratorNode.leftExpression.leftExpression); if (baseType instanceof PropertyType) { const memberName = decoratorNode.leftExpression.memberName.nameToken.value; @@ -2736,7 +2743,9 @@ export class TypeAnalyzer extends ParseTreeWalker { node: node.memberName, isConstant, path: this._fileInfo.filePath, - range: convertOffsetsToRange(node.memberName.start, node.memberName.end, this._fileInfo.lines) + range: convertOffsetsToRange(node.memberName.start, + node.memberName.start + node.memberName.length, + this._fileInfo.lines) }; if (typeAnnotationNode) { @@ -3021,7 +3030,7 @@ export class TypeAnalyzer extends ParseTreeWalker { } private _markExpressionAccessed(target: ExpressionNode) { - if (target instanceof NameNode) { + if (target.nodeType === ParseNodeType.Name) { const nameValue = target.nameToken.value; const symbolWithScope = this._currentScope.lookUpSymbolRecursive(nameValue); @@ -3032,14 +3041,15 @@ export class TypeAnalyzer extends ParseTreeWalker { } private _assignTypeToExpression(target: ExpressionNode, srcType: Type, srcExpr: ExpressionNode): void { - if (target instanceof NameNode) { + if (target.nodeType === ParseNodeType.Name) { const name = target.nameToken; const declaration: Declaration = { category: DeclarationCategory.Variable, node: target, isConstant: SymbolUtils.isConstantName(name.value), path: this._fileInfo.filePath, - range: convertOffsetsToRange(name.start, name.end, this._fileInfo.lines) + range: convertOffsetsToRange(name.start, TextRange.getEnd(name), + this._fileInfo.lines) }; // Handle '__all__' as a special case in the module scope. @@ -3047,10 +3057,12 @@ export class TypeAnalyzer extends ParseTreeWalker { // It's common for modules to include the expression // __all__ = ['a', 'b', 'c'] // We will mark the symbols referenced by these strings as accessed. - if (srcExpr instanceof ListNode) { + if (srcExpr.nodeType === ParseNodeType.List) { srcExpr.entries.forEach(entryExpr => { - if (entryExpr instanceof StringListNode || entryExpr instanceof StringNode) { - const symbolName = entryExpr.getValue(); + if (entryExpr.nodeType === ParseNodeType.StringList || entryExpr.nodeType === ParseNodeType.String) { + const symbolName = entryExpr.nodeType === ParseNodeType.String ? + entryExpr.value : + entryExpr.strings.map(s => s.value).join(''); const symbolInScope = this._currentScope.lookUpSymbolRecursive(symbolName); if (symbolInScope) { this._setSymbolAccessed(symbolInScope.symbol); @@ -3065,11 +3077,11 @@ export class TypeAnalyzer extends ParseTreeWalker { target, srcType, srcExpr); this._assignTypeToNameNode(target, srcType, declaration, srcExpr); - } else if (target instanceof MemberAccessExpressionNode) { + } else if (target.nodeType === ParseNodeType.MemberAccess) { const targetNode = target.leftExpression; // Handle member accesses (e.g. self.x or cls.y). - if (targetNode instanceof NameNode) { + if (targetNode.nodeType === ParseNodeType.Name) { // Determine whether we're writing to a class or instance member. const enclosingClassNode = ParseTreeUtils.getEnclosingClass(target); @@ -3092,7 +3104,7 @@ export class TypeAnalyzer extends ParseTreeWalker { } } } - } else if (target instanceof TupleExpressionNode) { + } else if (target.nodeType === ParseNodeType.Tuple) { // Initialize the array of target types, one for each target. const targetTypes: Type[][] = new Array(target.expressions.length); for (let i = 0; i < target.expressions.length; i++) { @@ -3113,7 +3125,7 @@ export class TypeAnalyzer extends ParseTreeWalker { } const targetEndsWithUnpackOperator = target.expressions.length > 0 && - target.expressions[target.expressions.length - 1] instanceof UnpackExpressionNode; + target.expressions[target.expressions.length - 1].nodeType === ParseNodeType.Unpack; if (targetEndsWithUnpackOperator) { if (entryCount >= target.expressions.length) { @@ -3169,7 +3181,7 @@ export class TypeAnalyzer extends ParseTreeWalker { const targetType = typeList.length === 0 ? UnknownType.create() : TypeUtils.combineTypes(typeList); this._assignTypeToExpression(expr, targetType, srcExpr); }); - } else if (target instanceof TypeAnnotationExpressionNode) { + } else if (target.nodeType === ParseNodeType.TypeAnnotation) { const typeHintType = this._getTypeOfAnnotation(target.typeAnnotation); const diagAddendum = new DiagnosticAddendum(); if (TypeUtils.canAssignType(typeHintType, srcType, diagAddendum)) { @@ -3177,15 +3189,16 @@ export class TypeAnalyzer extends ParseTreeWalker { } this._assignTypeToExpression(target.valueExpression, srcType, srcExpr); - } else if (target instanceof UnpackExpressionNode) { - if (target.expression instanceof NameNode) { + } else if (target.nodeType === ParseNodeType.Unpack) { + if (target.expression.nodeType === ParseNodeType.Name) { const name = target.expression.nameToken; const declaration: Declaration = { category: DeclarationCategory.Variable, node: target.expression, isConstant: SymbolUtils.isConstantName(name.value), path: this._fileInfo.filePath, - range: convertOffsetsToRange(name.start, name.end, this._fileInfo.lines) + range: convertOffsetsToRange(name.start, TextRange.getEnd(name), + this._fileInfo.lines) }; if (!srcType.isAny()) { @@ -3199,7 +3212,7 @@ export class TypeAnalyzer extends ParseTreeWalker { } this._assignTypeToNameNode(target.expression, srcType, declaration, srcExpr); } - } else if (target instanceof ListNode) { + } else if (target.nodeType === ParseNodeType.List) { target.entries.forEach(entry => { this._assignTypeToExpression(entry, UnknownType.create(), srcExpr); }); @@ -3210,20 +3223,20 @@ export class TypeAnalyzer extends ParseTreeWalker { } private _addNamedTargetToCurrentScope(node: ExpressionNode) { - if (node instanceof NameNode) { + if (node.nodeType === ParseNodeType.Name) { const symbol = this._currentScope.addSymbol(node.nameToken.value, true); // Mark the symbol as accessed. These symbols are not persisted // between analysis passes, so we never have an opportunity to // mark them as accessed. symbol.setIsAcccessed(); - } else if (node instanceof TypeAnnotationExpressionNode) { + } else if (node.nodeType === ParseNodeType.TypeAnnotation) { this._addNamedTargetToCurrentScope(node.valueExpression); - } else if (node instanceof TupleExpressionNode) { + } else if (node.nodeType === ParseNodeType.Tuple) { node.expressions.forEach(expr => { this._addNamedTargetToCurrentScope(expr); }); - } else if (node instanceof ListNode) { + } else if (node.nodeType === ParseNodeType.List) { node.entries.forEach(expr => { this._addNamedTargetToCurrentScope(expr); }); @@ -3292,9 +3305,9 @@ export class TypeAnalyzer extends ParseTreeWalker { // Is this module ever accessed? if (targetSymbol && !targetSymbol.isAccessed()) { const multipartName = nameParts.map(np => np.nameToken.value).join('.'); - const textRange = new TextRange(nameParts[0].start, nameParts[0].length); + const textRange = { start: nameParts[0].start, length: nameParts[0].length }; if (nameParts.length > 1) { - textRange.extend(nameParts[nameParts.length - 1]); + TextRange.extend(textRange, nameParts[nameParts.length - 1]); } this._fileInfo.diagnosticSink.addUnusedCodeWithTextRange( `'${ multipartName }' is not accessed`, textRange); @@ -3581,6 +3594,12 @@ export class TypeAnalyzer extends ParseTreeWalker { const newScope = AnalyzerNodeInfo.getScope(node); assert(newScope !== undefined); + // Clear the defaultValueInitializerExpression because we want + // to allow calls within lambdas that are used to initialize + // parameters. + const wasDefaultValueInitizer = this._defaultValueInitializerExpression; + this._defaultValueInitializerExpression = false; + let prevParent: Scope | undefined; if (!newScope!.isIndependentlyExecutable()) { // Temporary reparent the scope in case it is contained @@ -3608,6 +3627,8 @@ export class TypeAnalyzer extends ParseTreeWalker { newScope!.setParent(prevParent); } + this._defaultValueInitializerExpression = wasDefaultValueInitizer; + return newScope!; } diff --git a/server/src/analyzer/typeConstraint.ts b/server/src/analyzer/typeConstraint.ts index 9be8102f4..31f126495 100644 --- a/server/src/analyzer/typeConstraint.ts +++ b/server/src/analyzer/typeConstraint.ts @@ -12,10 +12,7 @@ * None within that scope. */ -import { ArgumentCategory, BinaryExpressionNode, CallExpressionNode, ConstantNode, - ExpressionNode, MemberAccessExpressionNode, NameNode, - TypeAnnotationExpressionNode, - UnaryExpressionNode } from '../parser/parseNodes'; +import { ArgumentCategory, ExpressionNode, ParseNodeType } from '../parser/parseNodes'; import { KeywordType, OperatorType } from '../parser/tokenizerTypes'; import { ClassType, NeverType, NoneType, ObjectType, Type, TypeCategory, UnionType } from './types'; import { TypeUtils } from './typeUtils'; @@ -102,9 +99,9 @@ export class TypeConstraint { // For now, we support only simple names and member access chains // that include only simple names (e.g. "A.B.C.D"). static isSupportedExpression(expression: ExpressionNode) { - if (expression instanceof NameNode) { + if (expression.nodeType === ParseNodeType.Name) { return true; - } else if (expression instanceof MemberAccessExpressionNode) { + } else if (expression.nodeType === ParseNodeType.MemberAccess) { if (!this.isSupportedExpression(expression.leftExpression)) { return false; } @@ -122,12 +119,12 @@ export class TypeConstraint { private _doesExpressionMatchRecursive(expression1: ExpressionNode, expression2: ExpressionNode): boolean { - if (expression1 instanceof NameNode) { - if (expression2 instanceof NameNode) { + if (expression1.nodeType === ParseNodeType.Name) { + if (expression2.nodeType === ParseNodeType.Name) { return expression1.nameToken.value === expression2.nameToken.value; } - } else if (expression1 instanceof MemberAccessExpressionNode) { - if (expression2 instanceof MemberAccessExpressionNode) { + } else if (expression1.nodeType === ParseNodeType.MemberAccess) { + if (expression2.nodeType === ParseNodeType.MemberAccess) { return this._doesExpressionMatchRecursive(expression1.leftExpression, expression2.leftExpression) && this._doesExpressionMatchRecursive(expression1.memberName, expression2.memberName); } @@ -145,7 +142,7 @@ export class TypeConstraintBuilder { typeEvaluator: (node: ExpressionNode) => Type): ConditionalTypeConstraintResults | undefined { - if (testExpression instanceof BinaryExpressionNode) { + if (testExpression.nodeType === ParseNodeType.BinaryOperation) { const results: ConditionalTypeConstraintResults = { ifConstraints: [], elseConstraints: [] @@ -157,7 +154,7 @@ export class TypeConstraintBuilder { // Look for "X is None" or "X is not None". These are commonly-used // patterns used in control flow. if (TypeConstraint.isSupportedExpression(testExpression.leftExpression)) { - if (testExpression.rightExpression instanceof ConstantNode && + if (testExpression.rightExpression.nodeType === ParseNodeType.Constant && testExpression.rightExpression.token.keywordType === KeywordType.None) { const originalType = typeEvaluator(testExpression.leftExpression); @@ -175,7 +172,7 @@ export class TypeConstraintBuilder { } // Look for "type(X) is Y" or "type(X) is not Y". - if (testExpression.leftExpression instanceof CallExpressionNode) { + if (testExpression.leftExpression.nodeType === ParseNodeType.Call) { const callType = typeEvaluator(testExpression.leftExpression.leftExpression); if (callType instanceof ClassType && callType.isBuiltIn() && callType.getClassName() === 'type' && @@ -242,7 +239,7 @@ export class TypeConstraintBuilder { } return results; } - } else if (testExpression instanceof UnaryExpressionNode) { + } else if (testExpression.nodeType === ParseNodeType.UnaryOperation) { if (testExpression.operator === OperatorType.Not) { const constraints = this.buildTypeConstraintsForConditional( testExpression.expression, typeEvaluator); @@ -255,8 +252,8 @@ export class TypeConstraintBuilder { }; } } - } else if (testExpression instanceof NameNode || - testExpression instanceof MemberAccessExpressionNode) { + } else if (testExpression.nodeType === ParseNodeType.Name || + testExpression.nodeType === ParseNodeType.MemberAccess) { if (TypeConstraint.isSupportedExpression(testExpression)) { const originalType = typeEvaluator(testExpression); @@ -269,8 +266,8 @@ export class TypeConstraintBuilder { elseConstraints: [falseConstraint] }; } - } else if (testExpression instanceof CallExpressionNode) { - if (testExpression.leftExpression instanceof NameNode && + } else if (testExpression.nodeType === ParseNodeType.Call) { + if (testExpression.leftExpression.nodeType === ParseNodeType.Name && testExpression.leftExpression.nameToken.value === 'isinstance' && testExpression.arguments.length === 2) { @@ -329,7 +326,7 @@ export class TypeConstraintBuilder { static buildTypeConstraintForAssignment(targetNode: ExpressionNode, assignmentType: Type): TypeConstraint | undefined { - if (targetNode instanceof TypeAnnotationExpressionNode) { + if (targetNode.nodeType === ParseNodeType.TypeAnnotation) { if (TypeConstraint.isSupportedExpression(targetNode.valueExpression)) { return new TypeConstraint(targetNode.valueExpression, assignmentType); } diff --git a/server/src/analyzer/typeStubWriter.ts b/server/src/analyzer/typeStubWriter.ts index e44e6fdc7..8f5dc581e 100644 --- a/server/src/analyzer/typeStubWriter.ts +++ b/server/src/analyzer/typeStubWriter.ts @@ -13,8 +13,9 @@ import * as fs from 'fs'; import { ArgumentCategory, ArgumentNode, AssignmentNode, AugmentedAssignmentExpressionNode, ClassNode, DecoratorNode, ExpressionNode, ForNode, FunctionNode, IfNode, ImportFromNode, ImportNode, MemberAccessExpressionNode, ModuleNameNode, NameNode, - ParameterCategory, ParameterNode, ParseNode, StatementListNode, - StringListNode, StringNode, TryNode, TypeAnnotationExpressionNode, WhileNode, + ParameterCategory, ParameterNode, ParseNode, ParseNodeType, + StatementListNode, StringListNode, StringNode, TryNode, TypeAnnotationExpressionNode, + WhileNode, WithNode } from '../parser/parseNodes'; import { AnalyzerNodeInfo } from './analyzerNodeInfo'; import { ParseTreeUtils, PrintExpressionFlags } from './parseTreeUtils'; @@ -86,7 +87,7 @@ class ImportSymbolWalker extends ParseTreeWalker { visitString(node: StringNode) { if (this._treatStringsAsSymbols) { - const value = node.getValue(); + const value = node.value; this._markNameAccessed(node, value); } @@ -173,7 +174,7 @@ export class TypeStubWriter extends ParseTreeWalker { this._emitSuite(() => { this._classNestCount++; - this.walkChildren(node); + this.walk(node.suite); this._classNestCount--; }); @@ -228,7 +229,7 @@ export class TypeStubWriter extends ParseTreeWalker { this._emitSuite(() => { // Don't emit any nested functions. this._functionNestCount++; - this.walkChildren(node); + this.walk(node.suite); this._functionNestCount--; }); @@ -273,13 +274,18 @@ export class TypeStubWriter extends ParseTreeWalker { this._emittedSuite = true; this._emitLine('if ' + this._printExpression(node.testExpression) + ':'); this._emitSuite(() => { - this.walkChildren(node.ifSuite); + this.walkMultiple(node.ifSuite.statements); }); - if (node.elseSuite) { + const elseSuite = node.elseSuite; + if (elseSuite) { this._emitLine('else:'); this._emitSuite(() => { - this.walkChildren(node.elseSuite!); + if (elseSuite.nodeType === ParseNodeType.If) { + this.walkMultiple([elseSuite.testExpression, elseSuite.ifSuite, elseSuite.elseSuite]); + } else { + this.walkMultiple(elseSuite.statements); + } }); } this._ifNestCount--; @@ -291,7 +297,7 @@ export class TypeStubWriter extends ParseTreeWalker { visitAssignment(node: AssignmentNode) { let line = ''; - if (node.leftExpression instanceof NameNode) { + if (node.leftExpression.nodeType === ParseNodeType.Name) { if (this._functionNestCount === 0) { line = this._printExpression(node.leftExpression); } @@ -299,9 +305,9 @@ export class TypeStubWriter extends ParseTreeWalker { if (node.leftExpression.nameToken.value === '__all__') { this._emitLine(this._printExpression(node, false, true)); } - } else if (node.leftExpression instanceof MemberAccessExpressionNode) { + } else if (node.leftExpression.nodeType === ParseNodeType.MemberAccess) { const baseExpression = node.leftExpression.leftExpression; - if (baseExpression instanceof NameNode) { + if (baseExpression.nodeType === ParseNodeType.Name) { if (baseExpression.nameToken.value === 'self') { const memberName = node.leftExpression.memberName.nameToken.value; if (!SymbolUtils.isProtectedName(memberName) && @@ -342,7 +348,7 @@ export class TypeStubWriter extends ParseTreeWalker { visitAugmentedAssignment(node: AugmentedAssignmentExpressionNode) { if (this._classNestCount === 0 && this._functionNestCount === 0) { - if (node.leftExpression instanceof NameNode) { + if (node.leftExpression.nodeType === ParseNodeType.Name) { if (node.leftExpression.nameToken.value === '__all__') { this._emitLine(this._printExpression(node, false, true)); } @@ -355,11 +361,11 @@ export class TypeStubWriter extends ParseTreeWalker { visitTypeAnnotation(node: TypeAnnotationExpressionNode) { if (this._functionNestCount === 0) { let line = ''; - if (node.valueExpression instanceof NameNode) { + if (node.valueExpression.nodeType === ParseNodeType.Name) { line = this._printExpression(node.valueExpression); - } else if (node.valueExpression instanceof MemberAccessExpressionNode) { + } else if (node.valueExpression.nodeType === ParseNodeType.MemberAccess) { const baseExpression = node.valueExpression.leftExpression; - if (baseExpression instanceof NameNode) { + if (baseExpression.nodeType === ParseNodeType.Name) { if (baseExpression.nameToken.value === 'self') { const memberName = node.valueExpression.memberName.nameToken.value; if (!SymbolUtils.isProtectedName(memberName) && @@ -438,7 +444,7 @@ export class TypeStubWriter extends ParseTreeWalker { } visitStatementList(node: StatementListNode) { - if (node.statements.length > 0 && node.statements[0] instanceof StringListNode) { + if (node.statements.length > 0 && node.statements[0].nodeType === ParseNodeType.StringList) { // Is this the first statement in a suite? If it's a string // literal, assume it's a doc string and emit it. if (!this._emittedSuite && this._emitDocString) { @@ -449,7 +455,7 @@ export class TypeStubWriter extends ParseTreeWalker { // Don't emit a doc string after the first statement. this._emitDocString = false; - this.walkChildren(node); + this.walkMultiple(node.statements); return false; } diff --git a/server/src/common/diagnosticSink.ts b/server/src/common/diagnosticSink.ts index cc5c3a0a0..67a858a9a 100644 --- a/server/src/common/diagnosticSink.ts +++ b/server/src/common/diagnosticSink.ts @@ -71,14 +71,14 @@ export class TextRangeDiagnosticSink extends DiagnosticSink { } addErrorWithTextRange(message: string, range: TextRange) { - return this.addError(message, convertOffsetsToRange(range.start, range.end, this._lines)); + return this.addError(message, convertOffsetsToRange(range.start, range.start + range.length, this._lines)); } addWarningWithTextRange(message: string, range: TextRange) { - return this.addWarning(message, convertOffsetsToRange(range.start, range.end, this._lines)); + return this.addWarning(message, convertOffsetsToRange(range.start, range.start + range.length, this._lines)); } addUnusedCodeWithTextRange(message: string, range: TextRange) { - return this.addUnusedCode(message, convertOffsetsToRange(range.start, range.end, this._lines)); + return this.addUnusedCode(message, convertOffsetsToRange(range.start, range.start + range.length, this._lines)); } } diff --git a/server/src/common/textRange.ts b/server/src/common/textRange.ts index 7f64ae38c..0f2ae4764 100644 --- a/server/src/common/textRange.ts +++ b/server/src/common/textRange.ts @@ -7,43 +7,44 @@ * Specifies the range of text within a larger string. */ -export class TextRange { +export interface TextRange { start: number; length: number; +} - constructor(start: number, length: number) { +export namespace TextRange { + export function create(start: number, length: number): TextRange { if (start < 0) { throw new Error('start must be non-negative'); } if (length < 0) { throw new Error('length must be non-negative'); } - this.start = start; - this.length = length; + return { start, length }; } - get end(): number { - return this.start + this.length; + export function getEnd(range: TextRange): number { + return range.start + range.length; } - contains(position: number): boolean { - return position >= this.start && position < this.end; + export function contains(range: TextRange, position: number): boolean { + return position >= range.start && position < getEnd(range); } - extend(range: TextRange | TextRange[] | undefined) { - if (range) { - if (Array.isArray(range)) { - range.forEach(r => { - this.extend(r); + export function extend(range: TextRange, extension: TextRange | TextRange[] | undefined) { + if (extension) { + if (Array.isArray(extension)) { + extension.forEach(r => { + extend(range, r); }); } else { - if (range.start < this.start) { - this.length += this.start - range.start; - this.start = range.start; + if (extension.start < range.start) { + range.length += range.start - extension.start; + range.start = extension.start; } - if (range.end > this.end) { - this.length += range.end - this.end; + if (getEnd(extension) > getEnd(range)) { + range.length += getEnd(extension) - getEnd(range); } } } diff --git a/server/src/common/textRangeCollection.ts b/server/src/common/textRangeCollection.ts index f2a2b5122..cbcf65037 100644 --- a/server/src/common/textRangeCollection.ts +++ b/server/src/common/textRangeCollection.ts @@ -25,7 +25,8 @@ export class TextRangeCollection { } get end(): number { - return this._items.length > 0 ? this._items[this._items.length - 1].end : 0; + const lastItem = this._items[this._items.length - 1]; + return this._items.length > 0 ? lastItem.start + lastItem.length : 0; } get length(): number { @@ -102,10 +103,13 @@ export class TextRangeCollection { const mid = Math.floor(min + (max - min) / 2); const item = this._items[mid]; - if (item.contains(position)) { + if (TextRange.contains(item, position)) { return mid; } - if (mid < this.count - 1 && item.end <= position && position < this._items[mid + 1].start) { + + if (mid < this.count - 1 && TextRange.getEnd(item) <= position && + position < this._items[mid + 1].start) { + return -1; } diff --git a/server/src/languageService/completionProvider.ts b/server/src/languageService/completionProvider.ts index d5915fb42..cec58f6ff 100644 --- a/server/src/languageService/completionProvider.ts +++ b/server/src/languageService/completionProvider.ts @@ -9,8 +9,7 @@ */ import { CompletionItem, CompletionItemKind, CompletionList, - MarkupKind, - TextEdit } from 'vscode-languageserver'; + MarkupKind, TextEdit } from 'vscode-languageserver'; import { ImportMap } from '../analyzer/analyzerFileInfo'; import { AnalyzerNodeInfo } from '../analyzer/analyzerNodeInfo'; @@ -32,10 +31,10 @@ import { TextEditAction } from '../common/editAction'; import { getFileName, stripFileExtension } from '../common/pathUtils'; import { convertPositionToOffset } from '../common/positionUtils'; import { StringUtils } from '../common/stringUtils'; +import { TextRange } from '../common/textRange'; import { ErrorExpressionCategory, ErrorExpressionNode, - ExpressionNode, ImportFromAsNode, ImportFromNode, - MemberAccessExpressionNode, ModuleNameNode, ModuleNode, NameNode, - ParseNode, StringListNode, SuiteNode } from '../parser/parseNodes'; + ExpressionNode, ImportFromNode, isExpressionNode, ModuleNameNode, + ParseNode, ParseNodeType } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; import { TokenType } from '../parser/tokenizerTypes'; @@ -230,7 +229,7 @@ export class CompletionProvider { // precendence. let errorNode: ParseNode | undefined = node; while (errorNode) { - if (errorNode instanceof ErrorExpressionNode) { + if (errorNode.nodeType === ParseNodeType.Error) { break; } @@ -242,50 +241,50 @@ export class CompletionProvider { let curNode = errorNode || node; while (true) { // Don't offer completions inside of a string node. - if (curNode instanceof StringListNode) { + if (curNode.nodeType === ParseNodeType.StringList) { return undefined; } - if (curNode instanceof ModuleNameNode) { + if (curNode.nodeType === ParseNodeType.ModuleName) { return this._getImportModuleCompletions(curNode); } - if (curNode instanceof ErrorExpressionNode) { + if (curNode.nodeType === ParseNodeType.Error) { return this._getExpressionErrorCompletions(curNode, priorWord); } - if (curNode instanceof MemberAccessExpressionNode) { + if (curNode.nodeType === ParseNodeType.MemberAccess) { return this._getMemberAccessCompletions(curNode.leftExpression, priorWord); } - if (curNode instanceof NameNode) { + if (curNode.nodeType === ParseNodeType.Name) { // Are we within a "from X import Y as Z" statement and // more specifically within the "Y"? - if (curNode.parent instanceof ImportFromAsNode) { + if (curNode.parent && curNode.parent.nodeType === ParseNodeType.ImportFromAs) { const parentNode = curNode.parent.parent; - if (parentNode instanceof ImportFromNode) { + if (parentNode && parentNode.nodeType === ParseNodeType.ImportFrom) { if (curNode.parent.name === curNode) { return this._getImportFromCompletions(parentNode, priorWord); } else { return this._getImportFromCompletions(parentNode, ''); } } - } else if (curNode.parent instanceof MemberAccessExpressionNode) { + } else if (curNode.parent && curNode.parent.nodeType === ParseNodeType.MemberAccess) { return this._getMemberAccessCompletions( curNode.parent.leftExpression, priorWord); } } - if (curNode instanceof ImportFromNode) { + if (curNode.nodeType === ParseNodeType.ImportFrom) { return this._getImportFromCompletions(curNode, priorWord); } - if (curNode instanceof ExpressionNode) { + if (isExpressionNode(curNode)) { return this._getExpressionCompletions(curNode, priorWord); } - if (curNode instanceof SuiteNode || curNode instanceof ModuleNode) { + if (curNode.nodeType === ParseNodeType.Suite || curNode.nodeType === ParseNodeType.Module) { return this._getStatementCompletions(curNode, priorWord); } @@ -312,7 +311,7 @@ export class CompletionProvider { } // If we're in the middle of a token, we're not in a comment. - if (offset > token.start && offset < token.end) { + if (offset > token.start && offset < TextRange.getEnd(token)) { return false; } @@ -342,7 +341,7 @@ export class CompletionProvider { } case ErrorExpressionCategory.MissingMemberAccessName: { - if (node.child instanceof ExpressionNode) { + if (node.child && isExpressionNode(node.child)) { return this._getMemberAccessCompletions(node.child, priorWord); } break; @@ -482,7 +481,7 @@ export class CompletionProvider { // Does an 'import from' statement already exist? If so, we'll reuse it. const importStatement = importStatements.mapByFilePath[filePath]; - if (importStatement && importStatement.node instanceof ImportFromNode) { + if (importStatement && importStatement.node.nodeType === ParseNodeType.ImportFrom) { return ImportStatementUtils.getTextEditsForAutoImportSymbolAddition( symbolName, importStatement, this._parseResults); } @@ -804,7 +803,9 @@ export class CompletionProvider { // If we're in the middle of a "from X import Y" statement, offer // the "import" keyword as a completion. - if (!node.hasTrailingDot && node.parent instanceof ImportFromNode && node.parent.missingImportKeyword) { + if (!node.hasTrailingDot && node.parent && node.parent.nodeType === ParseNodeType.ImportFrom && + node.parent.missingImportKeyword) { + const keyword = 'import'; const completionItem = CompletionItem.create(keyword); completionItem.kind = CompletionItemKind.Keyword; diff --git a/server/src/languageService/definitionProvider.ts b/server/src/languageService/definitionProvider.ts index bd802bbfc..c3bf0009f 100644 --- a/server/src/languageService/definitionProvider.ts +++ b/server/src/languageService/definitionProvider.ts @@ -15,7 +15,8 @@ import { ParseTreeUtils } from '../analyzer/parseTreeUtils'; import { DiagnosticTextPosition, DiagnosticTextRange, DocumentTextRange } from '../common/diagnostic'; import { isFile } from '../common/pathUtils'; import { convertPositionToOffset } from '../common/positionUtils'; -import { ModuleNameNode } from '../parser/parseNodes'; +import { TextRange } from '../common/textRange'; +import { ParseNodeType } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; const _startOfFilePosition: DiagnosticTextPosition = { line: 0, column: 0 }; @@ -35,7 +36,7 @@ export class DefinitionProvider { return undefined; } - if (node instanceof ModuleNameNode) { + if (node.nodeType === ParseNodeType.ModuleName) { // If this is an imported module name, try to map the position // to the resolved import path. const importInfo = AnalyzerNodeInfo.getImportInfo(node); @@ -44,7 +45,7 @@ export class DefinitionProvider { } const pathOffset = node.nameParts.findIndex(range => { - return offset >= range.start && offset < range.end; + return offset >= range.start && offset < TextRange.getEnd(range); }); if (pathOffset < 0) { diff --git a/server/src/languageService/hoverProvider.ts b/server/src/languageService/hoverProvider.ts index 255e5cbf0..77387d359 100644 --- a/server/src/languageService/hoverProvider.ts +++ b/server/src/languageService/hoverProvider.ts @@ -18,7 +18,8 @@ import { ClassType, FunctionType, ModuleType, OverloadedFunctionType, Type, UnknownType } from '../analyzer/types'; import { DiagnosticTextPosition, DiagnosticTextRange } from '../common/diagnostic'; import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils'; -import { ModuleNameNode, NameNode, ParseNode } from '../parser/parseNodes'; +import { TextRange } from '../common/textRange'; +import { ParseNode, ParseNodeType } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; export interface HoverTextPart { @@ -49,11 +50,11 @@ export class HoverProvider { parts: [], range: { start: convertOffsetToPosition(node.start, parseResults.lines), - end: convertOffsetToPosition(node.end, parseResults.lines) + end: convertOffsetToPosition(TextRange.getEnd(node), parseResults.lines) } }; - if (node instanceof ModuleNameNode) { + if (node.nodeType === ParseNodeType.ModuleName) { // If this is an imported module name, try to map the position // to the resolved import path. const importInfo = AnalyzerNodeInfo.getImportInfo(node); @@ -62,7 +63,7 @@ export class HoverProvider { } let pathOffset = node.nameParts.findIndex(range => { - return offset >= range.start && offset < range.end; + return offset >= range.start && offset < TextRange.getEnd(range); }); if (pathOffset < 0) { @@ -104,7 +105,7 @@ export class HoverProvider { switch (declaration.category) { case DeclarationCategory.Variable: { - if (node instanceof NameNode) { + if (node.nodeType === ParseNodeType.Name) { this._addResultsPart(results, '(variable) ' + node.nameToken.value + this._getTypeText(node), true); this._addDocumentationPart(results, node); @@ -114,7 +115,7 @@ export class HoverProvider { } case DeclarationCategory.Parameter: { - if (node instanceof NameNode) { + if (node.nodeType === ParseNodeType.Name) { this._addResultsPart(results, '(parameter) ' + node.nameToken.value + this._getTypeText(node), true); this._addDocumentationPart(results, node); @@ -124,7 +125,7 @@ export class HoverProvider { } case DeclarationCategory.Class: { - if (node instanceof NameNode) { + if (node.nodeType === ParseNodeType.Name) { this._addResultsPart(results, '(class) ' + this._getTypeText(node), true); this._addDocumentationPart(results, node); return results; @@ -133,7 +134,7 @@ export class HoverProvider { } case DeclarationCategory.Function: { - if (node instanceof NameNode) { + if (node.nodeType === ParseNodeType.Name) { this._addResultsPart(results, '(function) ' + node.nameToken.value + this._getTypeText(node), true); this._addDocumentationPart(results, node); @@ -143,7 +144,7 @@ export class HoverProvider { } case DeclarationCategory.Method: { - if (node instanceof NameNode) { + if (node.nodeType === ParseNodeType.Name) { this._addResultsPart(results, '(method) ' + node.nameToken.value + this._getTypeText(node), true); this._addDocumentationPart(results, node); @@ -153,7 +154,7 @@ export class HoverProvider { } case DeclarationCategory.Module: { - if (node instanceof NameNode) { + if (node.nodeType === ParseNodeType.Name) { this._addResultsPart(results, '(module) ' + node.nameToken.value, true); this._addDocumentationPart(results, node); return results; @@ -164,12 +165,10 @@ export class HoverProvider { } // If we had no declaration, see if we can provide a minimal tooltip. - if (node instanceof NameNode) { - if (node instanceof NameNode) { - this._addResultsPart(results, node.nameToken.value + this._getTypeText(node), true); - this._addDocumentationPart(results, node); - return results; - } + if (node.nodeType === ParseNodeType.Name) { + this._addResultsPart(results, node.nameToken.value + this._getTypeText(node), true); + this._addDocumentationPart(results, node); + return results; } return undefined; diff --git a/server/src/languageService/importSorter.ts b/server/src/languageService/importSorter.ts index a51bf6120..1c6a8e333 100644 --- a/server/src/languageService/importSorter.ts +++ b/server/src/languageService/importSorter.ts @@ -13,7 +13,8 @@ import { ImportStatement, ImportStatementUtils } from '../analyzer/importStateme import { DiagnosticTextRange } from '../common/diagnostic'; import { TextEditAction } from '../common/editAction'; import { convertOffsetToPosition } from '../common/positionUtils'; -import { ImportAsNode, ImportFromAsNode, ImportFromNode, ImportNode } from '../parser/parseNodes'; +import { TextRange } from '../common/textRange'; +import { ImportAsNode, ImportFromAsNode, ImportFromNode, ParseNodeType } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; const _maxLineLength = 80; @@ -99,11 +100,12 @@ export class ImportSorter { statementLimit = statements.length; } + const lastStatement = statements[statementLimit - 1].node; return { start: convertOffsetToPosition( statements[0].node.start, this._parseResults.lines), end: convertOffsetToPosition( - statements[statementLimit - 1].node.end, this._parseResults.lines) + TextRange.getEnd(lastStatement), this._parseResults.lines) }; } @@ -128,7 +130,7 @@ export class ImportSorter { statements[secondaryBlockStart].node.start, this._parseResults.lines), end: convertOffsetToPosition( - statements[secondaryBlockLimit - 1].node.end, + TextRange.getEnd(statements[secondaryBlockLimit - 1].node), this._parseResults.lines) }, replacementText: '' @@ -154,7 +156,7 @@ export class ImportSorter { } let importLine: string; - if (statement.node instanceof ImportNode) { + if (statement.node.nodeType === ParseNodeType.Import) { importLine = this._formatImportNode(statement.subnode!, statement.moduleName); } else { diff --git a/server/src/languageService/quickActions.ts b/server/src/languageService/quickActions.ts index d474756c2..81d7ac0f9 100644 --- a/server/src/languageService/quickActions.ts +++ b/server/src/languageService/quickActions.ts @@ -12,7 +12,8 @@ import { ImportStatementUtils } from '../analyzer/importStatementUtils'; import { ParseTreeUtils } from '../analyzer/parseTreeUtils'; import { TextEditAction } from '../common/editAction'; import { convertOffsetToPosition } from '../common/positionUtils'; -import { ImportFromNode, ParameterNode, ParseNode } from '../parser/parseNodes'; +import { TextRange } from '../common/textRange'; +import { ParseNode, ParseNodeType } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; import { commandAddMissingOptionalToParam, commandOrderImports } from './commands'; import { ImportSorter } from './importSorter'; @@ -40,7 +41,7 @@ function _addMissingOptionalToParam(parseResults: ParseResults, let node: ParseNode | undefined = ParseTreeUtils.findNodeByOffset(parseResults.parseTree, offset); while (node) { - if (node instanceof ParameterNode) { + if (node.nodeType === ParseNodeType.Parameter) { break; } @@ -56,7 +57,7 @@ function _addMissingOptionalToParam(parseResults: ParseResults, const startPos = convertOffsetToPosition( node.typeAnnotation.start, parseResults.lines); const endPos = convertOffsetToPosition( - node.typeAnnotation.end, parseResults.lines); + TextRange.getEnd(node.typeAnnotation), parseResults.lines); editActions.push({ range: { start: startPos, end: startPos }, @@ -74,7 +75,7 @@ function _addMissingOptionalToParam(parseResults: ParseResults, imp => imp.moduleName === 'typing'); // If there's an existing import statement, insert into it. - if (importStatement && importStatement.node instanceof ImportFromNode) { + if (importStatement && importStatement.node.nodeType === ParseNodeType.ImportFrom) { const additionalEditActions = ImportStatementUtils.getTextEditsForAutoImportSymbolAddition( 'Optional', importStatement, parseResults); editActions.push(...additionalEditActions); diff --git a/server/src/languageService/referencesProvider.ts b/server/src/languageService/referencesProvider.ts index fbdf2b751..9ea7f8cd1 100644 --- a/server/src/languageService/referencesProvider.ts +++ b/server/src/languageService/referencesProvider.ts @@ -15,6 +15,7 @@ import { ParseTreeWalker } from '../analyzer/parseTreeWalker'; import { Symbol } from '../analyzer/symbol'; import { DiagnosticTextPosition, DocumentTextRange } from '../common/diagnostic'; import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils'; +import { TextRange } from '../common/textRange'; import { NameNode, ParseNode } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; @@ -75,7 +76,7 @@ class FindReferencesTreeWalker extends ParseTreeWalker { path: this._filePath, range: { start: convertOffsetToPosition(node.start, this._parseResults.lines), - end: convertOffsetToPosition(node.end, this._parseResults.lines) + end: convertOffsetToPosition(TextRange.getEnd(node), this._parseResults.lines) } }); } diff --git a/server/src/languageService/signatureHelpProvider.ts b/server/src/languageService/signatureHelpProvider.ts index 312ecc777..680c3cd82 100644 --- a/server/src/languageService/signatureHelpProvider.ts +++ b/server/src/languageService/signatureHelpProvider.ts @@ -16,7 +16,8 @@ import { ClassType, FunctionType, ObjectType, import { ClassMemberLookupFlags, TypeUtils } from '../analyzer/typeUtils'; import { DiagnosticTextPosition } from '../common/diagnostic'; import { convertPositionToOffset } from '../common/positionUtils'; -import { CallExpressionNode, ParseNode } from '../parser/parseNodes'; +import { TextRange } from '../common/textRange'; +import { CallExpressionNode, ParseNode, ParseNodeType } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; export interface ParamInfo { @@ -74,7 +75,7 @@ export class SignatureHelpProvider { return undefined; } - if (offset > callNode.end) { + if (offset > TextRange.getEnd(callNode)) { return undefined; } @@ -87,7 +88,7 @@ export class SignatureHelpProvider { let activeParameter = 0; const args = callNode.arguments; for (let i = args.length - 1; i >= 0; i--) { - if (offset > args[i].valueExpression.end) { + if (offset > TextRange.getEnd(args[i].valueExpression)) { activeParameter = i + 1; break; } @@ -193,7 +194,7 @@ export class SignatureHelpProvider { let curNode: ParseNode | undefined = node; while (curNode !== undefined) { - if (curNode instanceof CallExpressionNode) { + if (curNode.nodeType === ParseNodeType.Call) { return curNode; } diff --git a/server/src/parser/parseNodes.ts b/server/src/parser/parseNodes.ts index c55fb0035..9c1345817 100644 --- a/server/src/parser/parseNodes.ts +++ b/server/src/parser/parseNodes.ts @@ -12,11 +12,8 @@ import { TextRange } from '../common/textRange'; import { IdentifierToken, KeywordToken, NumberToken, OperatorType, StringToken, Token } from './tokenizerTypes'; -export type ParseNodeArray = (undefined | ParseNode)[]; - export enum ParseNodeType { - None, - Error, + Error, // 0 Argument, Assert, @@ -27,7 +24,8 @@ export enum ParseNodeType { Break, Call, Class, - Constant, + + Constant, // 10 Continue, Decorator, Del, @@ -37,7 +35,8 @@ export enum ParseNodeType { Ellipsis, If, Import, - ImportAs, + + ImportAs, // 20 ImportFrom, ImportFromAs, Index, @@ -47,7 +46,8 @@ export enum ParseNodeType { FormatString, Function, Global, - Lambda, + + Lambda, // 30 List, ListComprehension, ListComprehensionFor, @@ -57,7 +57,8 @@ export enum ParseNodeType { ModuleName, Name, Nonlocal, - Number, + + Number, // 40 Parameter, Pass, Raise, @@ -67,7 +68,8 @@ export enum ParseNodeType { StatementList, StringList, String, - Suite, + + Suite, // 50 Ternary, Tuple, Try, @@ -77,7 +79,8 @@ export enum ParseNodeType { While, With, WithItem, - Yield, + + Yield, // 60 YieldFrom } @@ -93,181 +96,259 @@ export enum ErrorExpressionCategory { MissingListCloseBracket } -export abstract class ParseNode extends TextRange { - readonly nodeType: ParseNodeType = ParseNodeType.None; +export interface ParseNodeBase extends TextRange { + readonly nodeType: ParseNodeType; // The parent field is filled in by the PostParseWalker, // which isn't technically part of the parser. parent?: ParseNode; - - constructor(initialRange: TextRange) { - super(initialRange.start, initialRange.length); - } - - abstract getChildren(): ParseNodeArray; } -export class ModuleNode extends ParseNode { - readonly nodeType = ParseNodeType.Module; - statements: StatementNode[] = []; +export function extendRange(node: ParseNodeBase, newRange: TextRange) { + if (newRange.start < node.start) { + node.length += node.start - newRange.start; + node.start = newRange.start; + } - getChildren(): ParseNodeArray { - return this.statements; + if (TextRange.getEnd(newRange) > TextRange.getEnd(node)) { + node.length = TextRange.getEnd(newRange) - node.start; } } -export class SuiteNode extends ParseNode { - readonly nodeType = ParseNodeType.Suite; - statements: StatementNode[] = []; +export type ParseNodeArray = (ParseNode | undefined)[]; - getChildren(): ParseNodeArray { - return this.statements; +export interface ModuleNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Module; + statements: StatementNode[]; +} + +export namespace ModuleNode { + export function create(range: TextRange) { + const node: ModuleNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Module, + statements: [] + }; + + return node; } } -export class IfNode extends ParseNode { - readonly nodeType = ParseNodeType.If; +export interface SuiteNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Suite; + statements: StatementNode[]; +} + +export namespace SuiteNode { + export function create(range: TextRange) { + const node: SuiteNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Suite, + statements: [] + }; + + return node; + } +} + +export interface IfNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.If; testExpression: ExpressionNode; ifSuite: SuiteNode; - elseSuite?: SuiteNode | IfNode; + elseSuite ?: SuiteNode | IfNode; +} - constructor(ifOrElifToken: Token, testExpression: ExpressionNode, - ifSuite: SuiteNode) { - super(ifOrElifToken); - this.testExpression = testExpression; - this.ifSuite = ifSuite; - this.extend(this.testExpression); - this.extend(this.ifSuite); - } +export namespace IfNode { + export function create(ifOrElifToken: Token, testExpression: ExpressionNode, + ifSuite: SuiteNode, elseSuite?: SuiteNode) { - getChildren(): ParseNodeArray { - return [this.testExpression, this.ifSuite, this.elseSuite]; + const node: IfNode = { + start: ifOrElifToken.start, + length: ifOrElifToken.length, + nodeType: ParseNodeType.If, + testExpression, + ifSuite, + elseSuite + }; + + extendRange(node, testExpression); + extendRange(node, ifSuite); + if (elseSuite) { + extendRange(node, elseSuite); + } + + return node; } } -export class WhileNode extends ParseNode { - readonly nodeType = ParseNodeType.While; +export interface WhileNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.While; testExpression: ExpressionNode; whileSuite: SuiteNode; elseSuite?: SuiteNode; +} - getChildren(): ParseNodeArray { - return [this.testExpression, this.whileSuite, this.elseSuite]; +export namespace WhileNode { + export function create(whileToken: Token, testExpression: ExpressionNode, whileSuite: SuiteNode) { + const node: WhileNode = { + start: whileToken.start, + length: whileToken.length, + nodeType: ParseNodeType.While, + testExpression, + whileSuite + }; + + extendRange(node, whileSuite); + + return node; } } -export class ForNode extends ParseNode { - readonly nodeType = ParseNodeType.For; +export interface ForNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.For; isAsync?: boolean; targetExpression: ExpressionNode; iterableExpression: ExpressionNode; forSuite: SuiteNode; elseSuite?: SuiteNode; +} - constructor(forToken: Token, targetExpression: ExpressionNode, +export namespace ForNode { + export function create(forToken: Token, targetExpression: ExpressionNode, iterableExpression: ExpressionNode, forSuite: SuiteNode) { - super(forToken); - this.targetExpression = targetExpression; - this.iterableExpression = iterableExpression; - this.forSuite = forSuite; - this.extend(forSuite); - } - getChildren(): ParseNodeArray { - return [this.targetExpression, this.iterableExpression, this.forSuite, this.elseSuite]; + const node: ForNode = { + start: forToken.start, + length: forToken.length, + nodeType: ParseNodeType.For, + targetExpression, + iterableExpression, + forSuite + }; + + extendRange(node, forSuite); + + return node; } } export type ListComprehensionIterNode = ListComprehensionForNode | ListComprehensionIfNode; -export class ListComprehensionForNode extends ParseNode { - readonly nodeType = ParseNodeType.ListComprehensionFor; +export interface ListComprehensionForNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.ListComprehensionFor; isAsync?: boolean; targetExpression: ExpressionNode; iterableExpression: ExpressionNode; +} - constructor(startToken: Token, targetExpression: ExpressionNode, iterableExpression: ExpressionNode) { - super(startToken); - this.targetExpression = targetExpression; - this.iterableExpression = iterableExpression; - this.extend(targetExpression); - this.extend(iterableExpression); - } +export namespace ListComprehensionForNode { + export function create(startToken: Token, targetExpression: ExpressionNode, iterableExpression: ExpressionNode) { + const node: ListComprehensionForNode = { + start: startToken.start, + length: startToken.length, + nodeType: ParseNodeType.ListComprehensionFor, + targetExpression, + iterableExpression + }; - getChildren(): ParseNodeArray { - return [this.targetExpression, this.iterableExpression]; + extendRange(node, targetExpression); + extendRange(node, iterableExpression); + + return node; } } -export class ListComprehensionIfNode extends ParseNode { - readonly nodeType = ParseNodeType.ListComprehensionIf; +export interface ListComprehensionIfNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.ListComprehensionIf; testExpression: ExpressionNode; +} - constructor(ifToken: Token, testExpression: ExpressionNode) { - super(ifToken); - this.testExpression = testExpression; - this.extend(testExpression); - } +export namespace ListComprehensionIfNode { + export function create(ifToken: Token, testExpression: ExpressionNode) { + const node: ListComprehensionIfNode = { + start: ifToken.start, + length: ifToken.length, + nodeType: ParseNodeType.ListComprehensionIf, + testExpression + }; - getChildren(): ParseNodeArray { - return [this.testExpression]; + extendRange(node, testExpression); + + return node; } } -export class TryNode extends ParseNode { - readonly nodeType = ParseNodeType.Try; +export interface TryNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Try; trySuite: SuiteNode; - exceptClauses: ExceptNode[] = []; + exceptClauses: ExceptNode[]; elseSuite?: SuiteNode; finallySuite?: SuiteNode; +} - constructor(tryToken: Token, trySuite: SuiteNode) { - super(tryToken); - this.trySuite = trySuite; - } +export namespace TryNode { + export function create(tryToken: Token, trySuite: SuiteNode) { + const node: TryNode = { + start: tryToken.start, + length: tryToken.length, + nodeType: ParseNodeType.Try, + trySuite, + exceptClauses: [] + }; - getChildren(): ParseNodeArray { - return [this.trySuite, ...this.exceptClauses, this.elseSuite, this.finallySuite]; + return node; } } -export class ExceptNode extends ParseNode { - readonly nodeType = ParseNodeType.Except; +export interface ExceptNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Except; typeExpression?: ExpressionNode; name?: NameNode; exceptSuite: SuiteNode; +} - constructor(exceptToken: Token, exceptSuite: SuiteNode) { - super(exceptToken); - this.exceptSuite = exceptSuite; - this.extend(exceptSuite); - } +export namespace ExceptNode { + export function create(exceptToken: Token, exceptSuite: SuiteNode) { + const node: ExceptNode = { + start: exceptToken.start, + length: exceptToken.length, + nodeType: ParseNodeType.Except, + exceptSuite + }; - getChildren(): ParseNodeArray { - return [this.typeExpression, this.name, this.exceptSuite]; + extendRange(node, exceptSuite); + + return node; } } -export class FunctionNode extends ParseNode { - readonly nodeType = ParseNodeType.Function; - decorators: DecoratorNode[] = []; +export interface FunctionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Function; + decorators: DecoratorNode[]; isAsync?: boolean; name: NameNode; - parameters: ParameterNode[] = []; + parameters: ParameterNode[]; returnTypeAnnotation?: ExpressionNode; suite: SuiteNode; +} - constructor(defToken: Token, name: NameNode, suite: SuiteNode) { - super(defToken); - this.name = name; - this.suite = suite; - this.extend(suite); - } +export namespace FunctionNode { + export function create(defToken: Token, name: NameNode, suite: SuiteNode) { + const node: FunctionNode = { + start: defToken.start, + length: defToken.length, + nodeType: ParseNodeType.Function, + decorators: [], + name, + parameters: [], + suite + }; - getChildren(): ParseNodeArray { - return [...this.decorators, this.name, ...this.parameters, - this.returnTypeAnnotation ? this.returnTypeAnnotation : undefined, - this.suite]; + extendRange(node, suite); + + return node; } } @@ -277,98 +358,132 @@ export enum ParameterCategory { VarArgDictionary } -export class ParameterNode extends ParseNode { - readonly nodeType = ParseNodeType.Parameter; +export interface ParameterNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Parameter; category: ParameterCategory; name?: NameNode; typeAnnotation?: ExpressionNode; defaultValue?: ExpressionNode; +} - constructor(startToken: Token, paramCategory: ParameterCategory) { - super(startToken); - this.category = paramCategory; - } +export namespace ParameterNode { + export function create(startToken: Token, paramCategory: ParameterCategory) { + const node: ParameterNode = { + start: startToken.start, + length: startToken.length, + nodeType: ParseNodeType.Parameter, + category: paramCategory + }; - getChildren(): ParseNodeArray { - return [this.name, - this.typeAnnotation ? this.typeAnnotation : undefined, - this.defaultValue]; + return node; } } -export class ClassNode extends ParseNode { - readonly nodeType = ParseNodeType.Class; - decorators: DecoratorNode[] = []; +export interface ClassNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Class; + decorators: DecoratorNode[]; name: NameNode; - arguments: ArgumentNode[] = []; + arguments: ArgumentNode[]; suite: SuiteNode; +} - constructor(classToken: Token, name: NameNode, suite: SuiteNode) { - super(classToken); - this.name = name; - this.suite = suite; - this.extend(suite); - } +export namespace ClassNode { + export function create(classToken: Token, name: NameNode, suite: SuiteNode) { + const node: ClassNode = { + start: classToken.start, + length: classToken.length, + nodeType: ParseNodeType.Class, + decorators: [], + name, + arguments: [], + suite + }; - getChildren(): ParseNodeArray { - return [...this.decorators, this.name, ...this.arguments, this.suite]; + extendRange(node, suite); + + return node; } } -export class WithNode extends ParseNode { - readonly nodeType = ParseNodeType.With; +export interface WithNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.With; isAsync?: boolean; - withItems: WithItemNode[] = []; + withItems: WithItemNode[]; suite: SuiteNode; +} - constructor(withToken: Token, suite: SuiteNode) { - super(withToken); - this.suite = suite; - this.extend(suite); - } +export namespace WithNode { + export function create(withToken: Token, suite: SuiteNode) { + const node: WithNode = { + start: withToken.start, + length: withToken.length, + nodeType: ParseNodeType.With, + withItems: [], + suite + }; - getChildren(): ParseNodeArray { - return [...this.withItems, this.suite]; + extendRange(node, suite); + + return node; } } -export class WithItemNode extends ParseNode { - readonly nodeType = ParseNodeType.WithItem; +export interface WithItemNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.WithItem; expression: ExpressionNode; target?: ExpressionNode; +} - constructor(expression: ExpressionNode) { - super(expression); - this.expression = expression; - } +export namespace WithItemNode { + export function create(expression: ExpressionNode) { + const node: WithItemNode = { + start: expression.start, + length: expression.length, + nodeType: ParseNodeType.WithItem, + expression + }; - getChildren(): ParseNodeArray { - return [this.expression, this.target]; + return node; } } -export class DecoratorNode extends ParseNode { - readonly nodeType = ParseNodeType.Decorator; +export interface DecoratorNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Decorator; leftExpression: ExpressionNode; arguments: ArgumentNode[] | undefined; +} - constructor(atToken: Token, leftExpression: ExpressionNode) { - super(atToken); - this.leftExpression = leftExpression; - this.extend(leftExpression); - } +export namespace DecoratorNode { + export function create(atToken: Token, leftExpression: ExpressionNode) { + const node: DecoratorNode = { + start: atToken.start, + length: atToken.length, + nodeType: ParseNodeType.Decorator, + leftExpression, + arguments: undefined + }; - getChildren(): ParseNodeArray { - return [this.leftExpression, ...(this.arguments || [])]; + extendRange(node, leftExpression); + + return node; } } -export class StatementListNode extends ParseNode { - readonly nodeType = ParseNodeType.StatementList; - statements: ParseNode[] = []; +export interface StatementListNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.StatementList; + statements: ParseNode[]; +} - getChildren(): ParseNodeArray { - return this.statements; +export namespace StatementListNode { + export function create(atToken: Token) { + const node: StatementListNode = { + start: atToken.start, + length: atToken.length, + nodeType: ParseNodeType.StatementList, + statements: [] + }; + + return node; } } @@ -378,504 +493,707 @@ export type StatementNode = IfNode | WhileNode | ForNode | TryNode | export type SmallStatementNode = ExpressionNode | DelNode | PassNode | ImportNode | GlobalNode | NonlocalNode | AssertNode; -export abstract class ExpressionNode extends ParseNode { +export type ExpressionNode = ErrorExpressionNode | UnaryExpressionNode | + BinaryExpressionNode | AssignmentNode | TypeAnnotationExpressionNode | + AugmentedAssignmentExpressionNode | AwaitExpressionNode | + TernaryExpressionNode | UnpackExpressionNode | TupleExpressionNode | + CallExpressionNode | ListComprehensionNode | IndexExpressionNode | + SliceExpressionNode | YieldExpressionNode | YieldFromExpressionNode | + MemberAccessExpressionNode | LambdaNode | NameNode | ConstantNode | + EllipsisNode | NumberNode | StringNode | FormatStringNode | + StringListNode | DictionaryNode | DictionaryExpandEntryNode | + ListNode | SetNode; + +export function isExpressionNode(node: ParseNode) { + switch (node.nodeType) { + case ParseNodeType.Error: + case ParseNodeType.UnaryOperation: + case ParseNodeType.BinaryOperation: + case ParseNodeType.Assignment: + case ParseNodeType.TypeAnnotation: + case ParseNodeType.AugmentedAssignment: + case ParseNodeType.Await: + case ParseNodeType.Ternary: + case ParseNodeType.Unpack: + case ParseNodeType.Tuple: + case ParseNodeType.Call: + case ParseNodeType.ListComprehension: + case ParseNodeType.Index: + case ParseNodeType.Slice: + case ParseNodeType.Yield: + case ParseNodeType.YieldFrom: + case ParseNodeType.MemberAccess: + case ParseNodeType.Lambda: + case ParseNodeType.Name: + case ParseNodeType.Constant: + case ParseNodeType.Ellipsis: + case ParseNodeType.Number: + case ParseNodeType.String: + case ParseNodeType.FormatString: + case ParseNodeType.StringList: + case ParseNodeType.Dictionary: + case ParseNodeType.DictionaryExpandEntry: + case ParseNodeType.List: + case ParseNodeType.Set: + return true; + + default: + return false; + } } -export class ErrorExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Error; +export interface ErrorExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Error; readonly category: ErrorExpressionCategory; - readonly child?: ParseNode; + readonly child?: ExpressionNode; +} - constructor(initialRange: TextRange, category: ErrorExpressionCategory, - child?: ParseNode) { +export namespace ErrorExpressionNode { + export function create(initialRange: TextRange, category: ErrorExpressionCategory, + child?: ExpressionNode) { - super(initialRange); + const node: ErrorExpressionNode = { + start: initialRange.start, + length: initialRange.length, + nodeType: ParseNodeType.Error, + category, + child + }; - this.category = category; if (child) { - this.child = child; - this.extend(child); + extendRange(node, child); } - } - getChildren(): ParseNodeArray { - return [this.child]; + return node; } } -export class UnaryExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.UnaryOperation; +export interface UnaryExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.UnaryOperation; expression: ExpressionNode; operator: OperatorType; +} - constructor(expression: ExpressionNode, operator: OperatorType) { - super(expression); - this.expression = expression; - this.operator = operator; - this.extend(expression); - } +export namespace UnaryExpressionNode { + export function create(operatorToken: Token, expression: ExpressionNode, operator: OperatorType) { + const node: UnaryExpressionNode = { + start: operatorToken.start, + length: operatorToken.length, + nodeType: ParseNodeType.UnaryOperation, + operator, + expression + }; - getChildren(): ParseNodeArray { - return [this.expression]; + extendRange(node, expression); + + return node; } } -export class BinaryExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.BinaryOperation; +export interface BinaryExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.BinaryOperation; leftExpression: ExpressionNode; operator: OperatorType; rightExpression: ExpressionNode; +} - constructor(leftExpression: ExpressionNode, rightExpression: ExpressionNode, +export namespace BinaryExpressionNode { + export function create(leftExpression: ExpressionNode, rightExpression: ExpressionNode, operator: OperatorType) { - super(leftExpression); - this.leftExpression = leftExpression; - this.rightExpression = rightExpression; - this.operator = operator; - this.extend(rightExpression); - } - getChildren(): ParseNodeArray { - return [this.leftExpression, this.rightExpression]; + const node: BinaryExpressionNode = { + start: leftExpression.start, + length: leftExpression.length, + nodeType: ParseNodeType.BinaryOperation, + leftExpression, + operator, + rightExpression + }; + + extendRange(node, rightExpression); + + return node; } } -export class AssignmentNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Assignment; +export interface AssignmentNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Assignment; leftExpression: ExpressionNode; rightExpression: ExpressionNode; - typeAnnotationComment?: ExpressionNode; +} - constructor(leftExpression: ExpressionNode, rightExpression: ExpressionNode) { - super(leftExpression); - this.leftExpression = leftExpression; - this.rightExpression = rightExpression; - this.extend(rightExpression); - } +export namespace AssignmentNode { + export function create(leftExpression: ExpressionNode, rightExpression: ExpressionNode) { + const node: AssignmentNode = { + start: leftExpression.start, + length: leftExpression.length, + nodeType: ParseNodeType.Assignment, + leftExpression, + rightExpression + }; - getChildren(): ParseNodeArray { - return [this.leftExpression, this.rightExpression, this.typeAnnotationComment]; + extendRange(node, rightExpression); + + return node; } } -export class TypeAnnotationExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.TypeAnnotation; +export interface TypeAnnotationExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.TypeAnnotation; valueExpression: ExpressionNode; typeAnnotation: ExpressionNode; +} - constructor(valueExpression: ExpressionNode, typeAnnotation: ExpressionNode) { - super(valueExpression); - this.valueExpression = valueExpression; - this.typeAnnotation = typeAnnotation; - this.extend(typeAnnotation); - } +export namespace TypeAnnotationExpressionNode { + export function create(valueExpression: ExpressionNode, typeAnnotation: ExpressionNode) { + const node: TypeAnnotationExpressionNode = { + start: valueExpression.start, + length: valueExpression.length, + nodeType: ParseNodeType.TypeAnnotation, + valueExpression, + typeAnnotation + }; - getChildren(): ParseNodeArray { - return [this.valueExpression, this.typeAnnotation]; + extendRange(node, typeAnnotation); + + return node; } } -export class AugmentedAssignmentExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.AugmentedAssignment; +export interface AugmentedAssignmentExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.AugmentedAssignment; leftExpression: ExpressionNode; operator: OperatorType; rightExpression: ExpressionNode; +} - constructor(leftExpression: ExpressionNode, rightExpression: ExpressionNode, operator: OperatorType) { - super(leftExpression); - this.leftExpression = leftExpression; - this.rightExpression = rightExpression; - this.operator = operator; - this.extend(rightExpression); - } +export namespace AugmentedAssignmentExpressionNode { + export function create(leftExpression: ExpressionNode, rightExpression: ExpressionNode, + operator: OperatorType) { - getChildren(): ParseNodeArray { - return [this.leftExpression, this.rightExpression]; + const node: AugmentedAssignmentExpressionNode = { + start: leftExpression.start, + length: leftExpression.length, + nodeType: ParseNodeType.AugmentedAssignment, + leftExpression, + operator, + rightExpression + }; + + extendRange(node, rightExpression); + + return node; } } -export class AwaitExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Await; +export interface AwaitExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Await; expression: ExpressionNode; +} - constructor(awaitToken: Token, expression: ExpressionNode) { - super(awaitToken); - this.expression = expression; - this.extend(expression); - } +export namespace AwaitExpressionNode { + export function create(awaitToken: Token, expression: ExpressionNode) { + const node: AwaitExpressionNode = { + start: awaitToken.start, + length: awaitToken.length, + nodeType: ParseNodeType.Await, + expression + }; - getChildren(): ParseNodeArray { - return [this.expression]; + extendRange(node, expression); + + return node; } } -export class TernaryExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Ternary; +export interface TernaryExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Ternary; ifExpression: ExpressionNode; testExpression: ExpressionNode; elseExpression: ExpressionNode; +} - constructor(ifExpression: ExpressionNode, testExpression: ExpressionNode, elseExpression: ExpressionNode) { - super(ifExpression); - this.ifExpression = ifExpression; - this.testExpression = testExpression; - this.elseExpression = elseExpression; - this.extend(elseExpression); - } +export namespace TernaryExpressionNode { + export function create(ifExpression: ExpressionNode, testExpression: ExpressionNode, + elseExpression: ExpressionNode) { - getChildren(): ParseNodeArray { - return [this.ifExpression, this.testExpression, this.elseExpression]; + const node: TernaryExpressionNode = { + start: ifExpression.start, + length: ifExpression.length, + nodeType: ParseNodeType.Ternary, + ifExpression, + testExpression, + elseExpression + }; + + extendRange(node, elseExpression); + + return node; } } -export class UnpackExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Unpack; +export interface UnpackExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Unpack; expression: ExpressionNode; +} - constructor(starToken: Token, expression: ExpressionNode) { - super(starToken); - this.expression = expression; - this.extend(expression); - } +export namespace UnpackExpressionNode { + export function create(starToken: Token, expression: ExpressionNode) { + const node: UnpackExpressionNode = { + start: starToken.start, + length: starToken.length, + nodeType: ParseNodeType.Unpack, + expression + }; - getChildren(): ParseNodeArray { - return [this.expression]; + extendRange(node, expression); + + return node; } } -export class TupleExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Tuple; - expressions: ExpressionNode[] = []; +export interface TupleExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Tuple; + expressions: ExpressionNode[]; +} - getChildren(): ParseNodeArray { - return this.expressions; +export namespace TupleExpressionNode { + export function create(range: TextRange) { + const node: TupleExpressionNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Tuple, + expressions: [] + }; + + return node; } } -export class CallExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Call; +export interface CallExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Call; leftExpression: ExpressionNode; - arguments: ArgumentNode[] = []; + arguments: ArgumentNode[]; +} - constructor(leftExpression: ExpressionNode) { - super(leftExpression); - this.leftExpression = leftExpression; - } +export namespace CallExpressionNode { + export function create(leftExpression: ExpressionNode) { + const node: CallExpressionNode = { + start: leftExpression.start, + length: leftExpression.length, + nodeType: ParseNodeType.Call, + leftExpression, + arguments: [] + }; - getChildren(): ParseNodeArray { - return [this.leftExpression, ...this.arguments]; + return node; } } -export class ListComprehensionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.ListComprehension; - expression: T; - comprehensions: ListComprehensionIterNode[] = []; +export interface ListComprehensionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.ListComprehension; + expression: ParseNode; + comprehensions: ListComprehensionIterNode[]; +} - constructor(expression: T) { - super(expression); - this.expression = expression; - } +export namespace ListComprehensionNode { + export function create(expression: ParseNode) { + const node: ListComprehensionNode = { + start: expression.start, + length: expression.length, + nodeType: ParseNodeType.ListComprehension, + expression, + comprehensions: [] + }; - getChildren(): ParseNodeArray { - return [this.expression, ...this.comprehensions]; + return node; } } -export class IndexItemsNode extends ParseNode { - readonly nodeType = ParseNodeType.IndexItems; +export interface IndexItemsNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.IndexItems; items: ExpressionNode[]; +} - constructor(openBracketToken: Token, closeBracketToken: Token, items: ExpressionNode[]) { - super(openBracketToken); - this.items = items; - this.extend(closeBracketToken); - } +export namespace IndexItemsNode { + export function create(openBracketToken: Token, closeBracketToken: Token, items: ExpressionNode[]) { + const node: IndexItemsNode = { + start: openBracketToken.start, + length: openBracketToken.length, + nodeType: ParseNodeType.IndexItems, + items + }; - getChildren(): ParseNodeArray { - return this.items; + extendRange(node, closeBracketToken); + + return node; } } -export class IndexExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Index; +export interface IndexExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Index; baseExpression: ExpressionNode; items: IndexItemsNode; +} - constructor(baseExpression: ExpressionNode, items: IndexItemsNode) { - super(baseExpression); - this.baseExpression = baseExpression; - this.items = items; - this.extend(items); - } +export namespace IndexExpressionNode { + export function create(baseExpression: ExpressionNode, items: IndexItemsNode) { + const node: IndexExpressionNode = { + start: baseExpression.start, + length: baseExpression.length, + nodeType: ParseNodeType.Index, + baseExpression, + items + }; - getChildren(): ParseNodeArray { - return [this.baseExpression, this.items]; + extendRange(node, items); + + return node; } } -export class SliceExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Slice; +export interface SliceExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Slice; startValue?: ExpressionNode; endValue?: ExpressionNode; stepValue?: ExpressionNode; +} - getChildren(): ParseNodeArray { - return [this.startValue, this.endValue, this.stepValue]; +export namespace SliceExpressionNode { + export function create(range: TextRange) { + const node: SliceExpressionNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Slice + }; + + return node; } } -export class YieldExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Yield; +export interface YieldExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Yield; expression: ExpressionNode; +} - constructor(yieldToken: Token, expression: ExpressionNode) { - super(yieldToken); - this.expression = expression; - this.extend(expression); - } +export namespace YieldExpressionNode { + export function create(yieldToken: Token, expression: ExpressionNode) { + const node: YieldExpressionNode = { + start: yieldToken.start, + length: yieldToken.length, + nodeType: ParseNodeType.Yield, + expression + }; - getChildren(): ParseNodeArray { - return [this.expression]; + extendRange(node, expression); + + return node; } } -export class YieldFromExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.YieldFrom; +export interface YieldFromExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.YieldFrom; expression: ExpressionNode; +} - constructor(yieldToken: Token, expression: ExpressionNode) { - super(yieldToken); - this.expression = expression; - this.extend(expression); - } +export namespace YieldFromExpressionNode { + export function create(yieldToken: Token, expression: ExpressionNode) { + const node: YieldFromExpressionNode = { + start: yieldToken.start, + length: yieldToken.length, + nodeType: ParseNodeType.YieldFrom, + expression + }; - getChildren(): ParseNodeArray { - return [this.expression]; + extendRange(node, expression); + + return node; } } -export class MemberAccessExpressionNode extends ExpressionNode { - readonly nodeType = ParseNodeType.MemberAccess; +export interface MemberAccessExpressionNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.MemberAccess; leftExpression: ExpressionNode; memberName: NameNode; +} - constructor(leftExpression: ExpressionNode, memberName: NameNode) { - super(leftExpression); - this.leftExpression = leftExpression; - this.memberName = memberName; - this.extend(memberName); - } +export namespace MemberAccessExpressionNode { + export function create(leftExpression: ExpressionNode, memberName: NameNode) { + const node: MemberAccessExpressionNode = { + start: leftExpression.start, + length: leftExpression.length, + nodeType: ParseNodeType.MemberAccess, + leftExpression, + memberName + }; - getChildren(): ParseNodeArray { - return [this.leftExpression, this.memberName]; + extendRange(node, memberName); + + return node; } } -export class LambdaNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Lambda; - parameters: ParameterNode[] = []; +export interface LambdaNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Lambda; + parameters: ParameterNode[]; expression: ExpressionNode; +} - constructor(lambdaToken: Token, expression: ExpressionNode) { - super(lambdaToken); - this.expression = expression; - this.extend(expression); - } +export namespace LambdaNode { + export function create(lambdaToken: Token, expression: ExpressionNode) { + const node: LambdaNode = { + start: lambdaToken.start, + length: lambdaToken.length, + nodeType: ParseNodeType.Lambda, + parameters: [], + expression + }; - getChildren(): ParseNodeArray { - return [...this.parameters, this.expression]; + extendRange(node, expression); + + return node; } } -export class NameNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Name; +export interface NameNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Name; nameToken: IdentifierToken; +} - constructor(nameToken: IdentifierToken) { - super(nameToken); - this.nameToken = nameToken; - } +export namespace NameNode { + export function create(nameToken: IdentifierToken) { + const node: NameNode = { + start: nameToken.start, + length: nameToken.length, + nodeType: ParseNodeType.Name, + nameToken + }; - getChildren(): ParseNodeArray { - return []; + return node; } } -export class ConstantNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Constant; +export interface ConstantNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Constant; token: KeywordToken; +} - constructor(token: KeywordToken) { - super(token); - this.token = token; - } +export namespace ConstantNode { + export function create(token: KeywordToken) { + const node: ConstantNode = { + start: token.start, + length: token.length, + nodeType: ParseNodeType.Constant, + token + }; - getChildren(): ParseNodeArray { - return []; + return node; } } -export class EllipsisNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Ellipsis; +export interface EllipsisNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Ellipsis; +} - getChildren(): ParseNodeArray { - return []; +export namespace EllipsisNode { + export function create(range: TextRange) { + const node: EllipsisNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Ellipsis + }; + + return node; } } -export class NumberNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Number; +export interface NumberNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Number; token: NumberToken; +} - constructor(token: NumberToken) { - super(token); - this.token = token; - } +export namespace NumberNode { + export function create(token: NumberToken) { + const node: NumberNode = { + start: token.start, + length: token.length, + nodeType: ParseNodeType.Number, + token + }; - getChildren(): ParseNodeArray { - return []; + return node; } } -export class StringNode extends ExpressionNode { - readonly nodeType = ParseNodeType.String; +export interface StringNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.String; token: StringToken; value: string; hasUnescapeErrors: boolean; +} - constructor(token: StringToken, unescapedValue: string, hasUnescapeErrors: boolean) { - super(token); - this.token = token; - this.value = unescapedValue; - this.hasUnescapeErrors = hasUnescapeErrors; - } +export namespace StringNode { + export function create(token: StringToken, unescapedValue: string, hasUnescapeErrors: boolean) { + const node: StringNode = { + start: token.start, + length: token.length, + nodeType: ParseNodeType.String, + token, + value: unescapedValue, + hasUnescapeErrors + }; - getChildren(): ParseNodeArray { - return []; - } - - getValue(): string { - return this.value; + return node; } } -export class FormatStringExpression { - expression: ExpressionNode; -} - -export class FormatStringNode extends ExpressionNode { - readonly nodeType = ParseNodeType.FormatString; +export interface FormatStringNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.FormatString; token: StringToken; value: string; hasUnescapeErrors: boolean; - expressions: FormatStringExpression[]; + expressions: ExpressionNode[]; +} - constructor(token: StringToken, unescapedValue: string, hasUnescapeErrors: boolean, - expressions: FormatStringExpression[]) { - super(token); - this.token = token; - this.value = unescapedValue; - this.hasUnescapeErrors = hasUnescapeErrors; - this.expressions = expressions; - } +export namespace FormatStringNode { + export function create(token: StringToken, unescapedValue: string, hasUnescapeErrors: boolean, + expressions: ExpressionNode[]) { - getChildren(): ParseNodeArray { - return this.expressions.map(e => e.expression); - } + const node: FormatStringNode = { + start: token.start, + length: token.length, + nodeType: ParseNodeType.FormatString, + token, + value: unescapedValue, + hasUnescapeErrors, + expressions + }; - getValue(): string { - return this.value; + return node; } } -export class StringListNode extends ExpressionNode { - readonly nodeType = ParseNodeType.StringList; +export interface StringListNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.StringList; strings: (StringNode | FormatStringNode)[]; // If strings are found within the context of // a type annotation, they are further parsed // into an expression. typeAnnotation?: ExpressionNode; +} - constructor(strings: (StringNode | FormatStringNode)[]) { - super(strings[0]); - this.strings = strings; - if (strings.length > 1) { - this.extend(strings[strings.length - 1]); +export namespace StringListNode { + export function create(strings: (StringNode | FormatStringNode)[]) { + const node: StringListNode = { + start: strings[0].start, + length: strings[0].length, + nodeType: ParseNodeType.StringList, + strings + }; + + if (strings.length > 0) { + extendRange(node, strings[strings.length - 1]); } - } - getChildren(): ParseNodeArray { - // Return type annotations first (if they're not undefined) - // so position lookups favor annotations over the raw string. - return [this.typeAnnotation, ...this.strings]; - } - - getValue(): string { - return this.strings.map(t => t.getValue()).join(''); + return node; } } -export class DictionaryNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Dictionary; - entries: DictionaryEntryNode[] = []; +export interface DictionaryNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Dictionary; + entries: DictionaryEntryNode[]; +} - getChildren(): ParseNodeArray { - return this.entries; +export namespace DictionaryNode { + export function create(range: TextRange) { + const node: DictionaryNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Dictionary, + entries: [] + }; + + return node; } } -export class DictionaryKeyEntryNode extends ParseNode { - readonly nodeType = ParseNodeType.DictionaryKeyEntry; +export interface DictionaryKeyEntryNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.DictionaryKeyEntry; keyExpression: ExpressionNode; valueExpression: ExpressionNode; +} - constructor(keyExpression: ExpressionNode, valueExpression: ExpressionNode) { - super(keyExpression); - this.keyExpression = keyExpression; - this.valueExpression = valueExpression; - this.extend(valueExpression); - } +export namespace DictionaryKeyEntryNode { + export function create(keyExpression: ExpressionNode, valueExpression: ExpressionNode) { + const node: DictionaryKeyEntryNode = { + start: keyExpression.start, + length: keyExpression.length, + nodeType: ParseNodeType.DictionaryKeyEntry, + keyExpression, + valueExpression + }; - getChildren(): ParseNodeArray { - return [this.keyExpression, this.valueExpression]; + extendRange(node, valueExpression); + + return node; } } -export class DictionaryExpandEntryNode extends ExpressionNode { - readonly nodeType = ParseNodeType.DictionaryExpandEntry; +export interface DictionaryExpandEntryNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.DictionaryExpandEntry; expandExpression: ExpressionNode; +} - constructor(expandExpression: ExpressionNode) { - super(expandExpression); - this.expandExpression = expandExpression; - } +export namespace DictionaryExpandEntryNode { + export function create(expandExpression: ExpressionNode) { + const node: DictionaryExpandEntryNode = { + start: expandExpression.start, + length: expandExpression.length, + nodeType: ParseNodeType.DictionaryExpandEntry, + expandExpression + }; - getChildren(): ParseNodeArray { - return [this.expandExpression]; + return node; } } -export type DictionaryEntryNode = DictionaryKeyEntryNode | DictionaryExpandEntryNode | - ListComprehensionNode | ListComprehensionNode; +export type DictionaryEntryNode = DictionaryKeyEntryNode | DictionaryExpandEntryNode | ListComprehensionNode; -export class SetNode extends ExpressionNode { - readonly nodeType = ParseNodeType.Set; - entries: ExpressionNode[] = []; +export interface SetNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Set; + entries: ExpressionNode[]; +} - getChildren(): ParseNodeArray { - return this.entries; +export namespace SetNode { + export function create(range: TextRange) { + const node: SetNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Set, + entries: [] + }; + + return node; } } -export class ListNode extends ExpressionNode { - readonly nodeType = ParseNodeType.List; - entries: ExpressionNode[] = []; +export interface ListNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.List; + entries: ExpressionNode[]; +} - getChildren(): ParseNodeArray { - return this.entries; +export namespace ListNode { + export function create(range: TextRange) { + const node: ListNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.List, + entries: [] + }; + + return node; } } @@ -885,184 +1203,304 @@ export enum ArgumentCategory { UnpackedDictionary } -export class ArgumentNode extends ParseNode { - readonly nodeType = ParseNodeType.Argument; +export interface ArgumentNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Argument; argumentCategory: ArgumentCategory; name?: NameNode; valueExpression: ExpressionNode; +} - constructor(startToken: Token, valueExpression: ExpressionNode, argCategory: ArgumentCategory) { - super(startToken); - this.valueExpression = valueExpression; - this.argumentCategory = argCategory; - this.extend(valueExpression); - } +export namespace ArgumentNode { + export function create(startToken: Token, valueExpression: ExpressionNode, argCategory: ArgumentCategory) { + const node: ArgumentNode = { + start: startToken.start, + length: startToken.length, + nodeType: ParseNodeType.Argument, + valueExpression, + argumentCategory: argCategory + }; - getChildren(): ParseNodeArray { - return [this.valueExpression]; + extendRange(node, valueExpression); + + return node; } } -export class DelNode extends ParseNode { - readonly nodeType = ParseNodeType.Del; - expressions: ExpressionNode[] = []; +export interface DelNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Del; + expressions: ExpressionNode[]; +} - constructor(delToken: Token) { - super(delToken); - } +export namespace DelNode { + export function create(delToken: Token) { + const node: DelNode = { + start: delToken.start, + length: delToken.length, + nodeType: ParseNodeType.Del, + expressions: [] + }; - getChildren(): ParseNodeArray { - return this.expressions; + return node; } } -export class PassNode extends ParseNode { - readonly nodeType = ParseNodeType.Pass; +export interface PassNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Pass; +} - getChildren(): ParseNodeArray { - return []; +export namespace PassNode { + export function create(passToken: TextRange) { + const node: PassNode = { + start: passToken.start, + length: passToken.length, + nodeType: ParseNodeType.Pass + }; + + return node; } } -export class ImportNode extends ParseNode { - readonly nodeType = ParseNodeType.Import; - list: ImportAsNode[] = []; +export interface ImportNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Import; + list: ImportAsNode[]; +} - getChildren(): ParseNodeArray { - return this.list; +export namespace ImportNode { + export function create(passToken: TextRange) { + const node: ImportNode = { + start: passToken.start, + length: passToken.length, + nodeType: ParseNodeType.Import, + list: [] + }; + + return node; } } -export class ModuleNameNode extends ParseNode { - readonly nodeType = ParseNodeType.ModuleName; - leadingDots = 0; - nameParts: NameNode[] = []; +export interface ModuleNameNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.ModuleName; + leadingDots: number; + nameParts: NameNode[]; // This is an error condition used only for type completion. hasTrailingDot?: boolean; +} - getChildren(): ParseNodeArray { - return []; +export namespace ModuleNameNode { + export function create(range: TextRange) { + const node: ModuleNameNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.ModuleName, + leadingDots: 0, + nameParts: [] + }; + + return node; } } -export class ImportAsNode extends ParseNode { - readonly nodeType = ParseNodeType.ImportAs; +export interface ImportAsNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.ImportAs; module: ModuleNameNode; alias?: NameNode; +} - constructor(module: ModuleNameNode) { - super(module); - this.module = module; - } +export namespace ImportAsNode { + export function create(module: ModuleNameNode) { + const node: ImportAsNode = { + start: module.start, + length: module.length, + nodeType: ParseNodeType.ImportAs, + module + }; - getChildren(): ParseNodeArray { - return [this.module, this.alias]; + return node; } } -export class ImportFromNode extends ParseNode { - readonly nodeType = ParseNodeType.ImportFrom; +export interface ImportFromNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.ImportFrom; module: ModuleNameNode; - imports: ImportFromAsNode[] = []; + imports: ImportFromAsNode[]; isWildcardImport: boolean; usesParens: boolean; missingImportKeyword?: boolean; +} - constructor(fromToken: Token, module: ModuleNameNode) { - super(fromToken); - this.module = module; - this.extend(module); - this.isWildcardImport = false; - this.usesParens = false; - } +export namespace ImportFromNode { + export function create(fromToken: Token, module: ModuleNameNode) { + const node: ImportFromNode = { + start: fromToken.start, + length: fromToken.length, + nodeType: ParseNodeType.ImportFrom, + module, + imports: [], + isWildcardImport: false, + usesParens: false + }; - getChildren(): ParseNodeArray { - return [this.module, ...this.imports]; + extendRange(node, module); + + return node; } } -export class ImportFromAsNode extends ParseNode { - readonly nodeType = ParseNodeType.ImportFromAs; +export interface ImportFromAsNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.ImportFromAs; name: NameNode; alias?: NameNode; +} - constructor(name: NameNode) { - super(name); - this.name = name; - } +export namespace ImportFromAsNode { + export function create(name: NameNode) { + const node: ImportFromAsNode = { + start: name.start, + length: name.length, + nodeType: ParseNodeType.ImportFromAs, + name + }; - getChildren(): ParseNodeArray { - return [this.name, this.alias]; + return node; } } -export class GlobalNode extends ParseNode { - readonly nodeType = ParseNodeType.Global; - nameList: NameNode[] = []; +export interface GlobalNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Global; + nameList: NameNode[]; +} - getChildren(): ParseNodeArray { - return this.nameList; +export namespace GlobalNode { + export function create(range: TextRange) { + const node: GlobalNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Global, + nameList: [] + }; + + return node; } } -export class NonlocalNode extends ParseNode { - readonly nodeType = ParseNodeType.Nonlocal; - nameList: NameNode[] = []; +export interface NonlocalNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Nonlocal; + nameList: NameNode[]; +} - getChildren(): ParseNodeArray { - return this.nameList; +export namespace NonlocalNode { + export function create(range: TextRange) { + const node: NonlocalNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Nonlocal, + nameList: [] + }; + + return node; } } -export class AssertNode extends ParseNode { - readonly nodeType = ParseNodeType.Assert; +export interface AssertNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Assert; testExpression: ExpressionNode; exceptionExpression?: ExpressionNode; +} - constructor(assertToken: Token, testExpression: ExpressionNode) { - super(assertToken); - this.testExpression = testExpression; - this.extend(testExpression); - } +export namespace AssertNode { + export function create(assertToken: Token, testExpression: ExpressionNode) { + const node: AssertNode = { + start: assertToken.start, + length: assertToken.length, + nodeType: ParseNodeType.Assert, + testExpression + }; - getChildren(): ParseNodeArray { - return [this.testExpression, this.exceptionExpression]; + extendRange(node, testExpression); + + return node; } } -export class BreakNode extends ParseNode { - readonly nodeType = ParseNodeType.Break; +export interface BreakNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Break; +} - getChildren(): ParseNodeArray { - return []; +export namespace BreakNode { + export function create(range: TextRange) { + const node: BreakNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Break + }; + + return node; } } -export class ContinueNode extends ParseNode { - readonly nodeType = ParseNodeType.Continue; +export interface ContinueNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Continue; +} - getChildren(): ParseNodeArray { - return []; +export namespace ContinueNode { + export function create(range: TextRange) { + const node: ContinueNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Continue + }; + + return node; } } -export class ReturnNode extends ParseNode { - readonly nodeType = ParseNodeType.Return; +export interface ReturnNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Return; returnExpression?: ExpressionNode; +} - getChildren(): ParseNodeArray { - return [this.returnExpression]; +export namespace ReturnNode { + export function create(range: TextRange) { + const node: ReturnNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Return + }; + + return node; } } -export class RaiseNode extends ParseNode { - readonly nodeType = ParseNodeType.Raise; +export interface RaiseNode extends ParseNodeBase { + readonly nodeType: ParseNodeType.Raise; typeExpression?: ExpressionNode; valueExpression?: ExpressionNode; tracebackExpression?: ExpressionNode; +} - getChildren(): ParseNodeArray { - return [this.typeExpression, this.valueExpression, this.tracebackExpression]; +export namespace RaiseNode { + export function create(range: TextRange) { + const node: RaiseNode = { + start: range.start, + length: range.length, + nodeType: ParseNodeType.Raise + }; + + return node; } } + +export type ParseNode = ErrorExpressionNode | ArgumentNode | AssertNode | AssignmentNode | + AugmentedAssignmentExpressionNode | AwaitExpressionNode | BinaryExpressionNode | + BreakNode | CallExpressionNode | ClassNode | ConstantNode | ContinueNode | + DecoratorNode | DelNode | DictionaryNode | DictionaryEntryNode | DictionaryExpandEntryNode | + DictionaryKeyEntryNode | EllipsisNode | IfNode | ImportNode | ImportAsNode | ImportFromNode | + ImportFromAsNode | IndexExpressionNode | IndexItemsNode | ExceptNode | ForNode | FormatStringNode | + FunctionNode | GlobalNode | LambdaNode | ListNode | ListComprehensionNode | ListComprehensionForNode | + ListComprehensionIfNode | MemberAccessExpressionNode | ModuleNameNode | ModuleNode | NameNode | + NonlocalNode | NumberNode | ParameterNode | PassNode | RaiseNode | ReturnNode | SetNode | + SliceExpressionNode | StatementListNode | StringListNode | StringNode | SuiteNode | + TernaryExpressionNode | TupleExpressionNode | TryNode | TypeAnnotationExpressionNode | + UnaryExpressionNode | UnpackExpressionNode | WhileNode | WithNode | WithItemNode | + YieldExpressionNode | YieldFromExpressionNode; diff --git a/server/src/parser/parser.ts b/server/src/parser/parser.ts index 6be497a5a..35578ad29 100644 --- a/server/src/parser/parser.ts +++ b/server/src/parser/parser.ts @@ -22,24 +22,19 @@ import StringMap from '../common/stringMap'; import { TextRange } from '../common/textRange'; import { TextRangeCollection } from '../common/textRangeCollection'; import { timingStats } from '../common/timing'; -import { ArgumentCategory, ArgumentNode, AssertNode, - AssignmentNode, AugmentedAssignmentExpressionNode, AwaitExpressionNode, - BinaryExpressionNode, BreakNode, CallExpressionNode, ClassNode, - ConstantNode, ContinueNode, DecoratorNode, DelNode, - DictionaryEntryNode, DictionaryExpandEntryNode, DictionaryKeyEntryNode, - DictionaryNode, EllipsisNode, ErrorExpressionCategory, ErrorExpressionNode, - ExceptNode, ExpressionNode, FormatStringExpression, FormatStringNode, ForNode, FunctionNode, - GlobalNode, IfNode, ImportAsNode, ImportFromAsNode, ImportFromNode, - ImportNode, IndexExpressionNode, IndexItemsNode, LambdaNode, - ListComprehensionForNode, ListComprehensionIfNode, ListComprehensionIterNode, ListComprehensionNode, - ListNode, MemberAccessExpressionNode, ModuleNameNode, ModuleNode, NameNode, NonlocalNode, - NumberNode, ParameterCategory, ParameterNode, ParseNode, PassNode, RaiseNode, - ReturnNode, SetNode, SliceExpressionNode, StatementListNode, - StatementNode, StringListNode, StringNode, SuiteNode, TernaryExpressionNode, - TryNode, TupleExpressionNode, TypeAnnotationExpressionNode, - UnaryExpressionNode, UnpackExpressionNode, WhileNode, WithItemNode, WithNode, - YieldExpressionNode, - YieldFromExpressionNode } from './parseNodes'; +import { ArgumentCategory, ArgumentNode, AssertNode, AssignmentNode, AugmentedAssignmentExpressionNode, + AwaitExpressionNode, BinaryExpressionNode, BreakNode, CallExpressionNode, ClassNode, + ConstantNode, ContinueNode, DecoratorNode, DelNode, DictionaryEntryNode, DictionaryExpandEntryNode, + DictionaryKeyEntryNode, DictionaryNode, EllipsisNode, ErrorExpressionCategory, ErrorExpressionNode, + ExceptNode, ExpressionNode, extendRange, FormatStringNode, ForNode, FunctionNode, GlobalNode, IfNode, + ImportAsNode, ImportFromAsNode, ImportFromNode, ImportNode, IndexExpressionNode, IndexItemsNode, + LambdaNode, ListComprehensionForNode, ListComprehensionIfNode, ListComprehensionIterNode, + ListComprehensionNode, ListNode, MemberAccessExpressionNode, ModuleNameNode, ModuleNode, NameNode, + NonlocalNode, NumberNode, ParameterCategory, ParameterNode, ParseNode, ParseNodeType, + PassNode, RaiseNode, ReturnNode, SetNode, SliceExpressionNode, StatementListNode, StatementNode, + StringListNode, StringNode, SuiteNode, TernaryExpressionNode, TryNode, TupleExpressionNode, + TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode, WhileNode, WithItemNode, + WithNode, YieldExpressionNode, YieldFromExpressionNode } from './parseNodes'; import { StringTokenUtils, UnescapedString } from './stringTokenUtils'; import { Tokenizer, TokenizerOutput } from './tokenizer'; import { DedentToken, IdentifierToken, KeywordToken, KeywordType, @@ -97,7 +92,7 @@ export class Parser { parseOptions, diagSink, cancelToken); }); - const moduleNode = new ModuleNode(new TextRange(0, fileContents.length)); + const moduleNode = ModuleNode.create({ start: 0, length: fileContents.length }); timingStats.parseFileTime.timeOperation(() => { while (!this._atEof()) { @@ -243,15 +238,15 @@ export class Parser { const test = this._parseTestExpression(); const suite = this._parseSuite(); - const ifNode = new IfNode(ifOrElifToken, test, suite); + const ifNode = IfNode.create(ifOrElifToken, test, suite); if (this._consumeTokenIfKeyword(KeywordType.Else)) { ifNode.elseSuite = this._parseSuite(); - ifNode.extend(ifNode.elseSuite); + extendRange(ifNode, ifNode.elseSuite); } else if (this._peekKeywordType() === KeywordType.Elif) { // Recursively handle an "elif" statement. ifNode.elseSuite = this._parseIfStatement(KeywordType.Elif); - ifNode.extend(ifNode.elseSuite); + extendRange(ifNode, ifNode.elseSuite); } return ifNode; @@ -274,7 +269,7 @@ export class Parser { // suite: ':' (simple_stmt | NEWLINE INDENT stmt+ DEDENT) private _parseSuite(): SuiteNode { const nextToken = this._peekToken(); - const suite = new SuiteNode(nextToken); + const suite = SuiteNode.create(nextToken); if (!this._consumeTokenIfType(TokenType.Colon)) { this._addError('Expected ":"', nextToken); @@ -320,7 +315,7 @@ export class Parser { } if (suite.statements.length > 0) { - suite.extend(suite.statements); + extendRange(suite, suite.statements[suite.statements.length - 1]); } return suite; @@ -339,7 +334,7 @@ export class Parser { if (!this._consumeTokenIfKeyword(KeywordType.In)) { seqExpr = this._handleExpressionParseError( ErrorExpressionCategory.MissingIn, 'Expected "in"'); - forSuite = new SuiteNode(this._peekToken()); + forSuite = SuiteNode.create(this._peekToken()); } else { seqExpr = this._parseTestListAsExpression( ErrorExpressionCategory.MissingExpression, 'Expected expression after "in"'); @@ -350,20 +345,22 @@ export class Parser { } } - const forNode = new ForNode(forToken, targetExpr, seqExpr, forSuite); + const forNode = ForNode.create(forToken, targetExpr, seqExpr, forSuite); forNode.elseSuite = elseSuite; - forNode.extend(elseSuite); + if (elseSuite) { + extendRange(forNode, elseSuite); + } if (asyncToken) { forNode.isAsync = true; - forNode.extend(asyncToken); + extendRange(forNode, asyncToken); } return forNode; } // comp_iter: comp_for | comp_if - private _tryParseListComprehension(target: T): ListComprehensionNode | undefined { + private _tryParseListComprehension(target: ParseNode): ListComprehensionNode | undefined { const compFor = this._tryParseCompForStatement(); if (!compFor) { @@ -379,9 +376,11 @@ export class Parser { compList.push(compIter); } - const listCompNode = new ListComprehensionNode(target); + const listCompNode = ListComprehensionNode.create(target); listCompNode.comprehensions = compList; - listCompNode.extend(compList); + if (compList.length > 0) { + extendRange(listCompNode, compList[compList.length - 1]); + } return listCompNode; } @@ -416,7 +415,7 @@ export class Parser { seqExpr = this._parseOrTest(); } - const compForNode = new ListComprehensionForNode(asyncToken || forToken, + const compForNode = ListComprehensionForNode.create(asyncToken || forToken, targetExpr, seqExpr); if (asyncToken) { @@ -436,7 +435,7 @@ export class Parser { const ifToken = this._getKeywordToken(KeywordType.If); const ifExpr = this._tryParseLambdaExpression() || this._parseOrTest(); - const compIfNode = new ListComprehensionIfNode(ifToken, ifExpr); + const compIfNode = ListComprehensionIfNode.create(ifToken, ifExpr); return compIfNode; } @@ -445,15 +444,14 @@ export class Parser { private _parseWhileStatement(): WhileNode { const whileToken = this._getKeywordToken(KeywordType.While); - const whileNode = new WhileNode(whileToken); - - whileNode.testExpression = this._parseTestExpression(); - whileNode.whileSuite = this._parseLoopSuite(); + const whileNode = WhileNode.create(whileToken, + this._parseTestExpression(), + this._parseLoopSuite()); if (this._consumeTokenIfKeyword(KeywordType.Else)) { whileNode.elseSuite = this._parseSuite(); + extendRange(whileNode, whileNode.elseSuite); } - whileNode.extend(whileNode.elseSuite || whileNode.whileSuite); return whileNode; } @@ -467,7 +465,7 @@ export class Parser { private _parseTryStatement(): TryNode { const tryToken = this._getKeywordToken(KeywordType.Try); const trySuite = this._parseSuite(); - const tryNode = new TryNode(tryToken, trySuite); + const tryNode = TryNode.create(tryToken, trySuite); let sawCatchAllExcept = false; while (true) { @@ -502,26 +500,28 @@ export class Parser { } const exceptSuite = this._parseSuite(); - const exceptNode = new ExceptNode(exceptToken, exceptSuite); + const exceptNode = ExceptNode.create(exceptToken, exceptSuite); exceptNode.typeExpression = typeExpr; if (symbolName) { - exceptNode.name = new NameNode(symbolName); + exceptNode.name = NameNode.create(symbolName); } tryNode.exceptClauses.push(exceptNode); } - tryNode.extend(tryNode.exceptClauses); + if (tryNode.exceptClauses.length > 0) { + extendRange(tryNode, tryNode.exceptClauses[tryNode.exceptClauses.length - 1]); + } if (tryNode.exceptClauses.length > 0) { if (this._consumeTokenIfKeyword(KeywordType.Else)) { tryNode.elseSuite = this._parseSuite(); - tryNode.extend(tryNode.elseSuite); + extendRange(tryNode, tryNode.elseSuite); } } if (this._consumeTokenIfKeyword(KeywordType.Finally)) { tryNode.finallySuite = this._parseSuite(); - tryNode.extend(tryNode.finallySuite); + extendRange(tryNode, tryNode.finallySuite); } return tryNode; @@ -557,21 +557,21 @@ export class Parser { const suite = this._parseSuite(); - const functionNode = new FunctionNode(defToken, new NameNode(nameToken), suite); + const functionNode = FunctionNode.create(defToken, NameNode.create(nameToken), suite); if (asyncToken) { functionNode.isAsync = true; - functionNode.extend(asyncToken); + extendRange(functionNode, asyncToken); } functionNode.parameters = paramList; if (decorators) { functionNode.decorators = decorators; if (decorators.length > 0) { - functionNode.extend(decorators[0]); + extendRange(functionNode, decorators[0]); } } if (returnType) { functionNode.returnTypeAnnotation = returnType; - functionNode.extend(returnType); + extendRange(functionNode, returnType); } return functionNode; @@ -681,7 +681,7 @@ export class Parser { const paramName = this._getTokenIfIdentifier(); if (!paramName) { if (starCount === 1) { - const paramNode = new ParameterNode(firstToken, ParameterCategory.VarArgList); + const paramNode = ParameterNode.create(firstToken, ParameterCategory.VarArgList); return paramNode; } this._addError('Expected parameter name', this._peekToken()); @@ -693,22 +693,22 @@ export class Parser { } else if (starCount === 2) { paramType = ParameterCategory.VarArgDictionary; } - const paramNode = new ParameterNode(firstToken, paramType); + const paramNode = ParameterNode.create(firstToken, paramType); if (paramName) { - paramNode.name = new NameNode(paramName); + paramNode.name = NameNode.create(paramName); + extendRange(paramNode, paramName); } - paramNode.extend(paramName); if (allowAnnotations && this._consumeTokenIfType(TokenType.Colon)) { this._parseTypeAnnotation(() => { paramNode.typeAnnotation = this._parseTestExpression(); - paramNode.extend(paramNode.typeAnnotation); + extendRange(paramNode, paramNode.typeAnnotation); }); } if (this._consumeTokenIfOperator(OperatorType.Assign)) { paramNode.defaultValue = this._parseTestExpression(); - paramNode.extend(paramNode.defaultValue); + extendRange(paramNode, paramNode.defaultValue); if (starCount > 0) { this._addError(`Parameter with '*' or '**' cannot have default value`, @@ -733,10 +733,10 @@ export class Parser { } const withSuite = this._parseSuite(); - const withNode = new WithNode(withToken, withSuite); + const withNode = WithNode.create(withToken, withSuite); if (asyncToken) { withNode.isAsync = true; - withNode.extend(asyncToken); + extendRange(withNode, asyncToken); } withNode.withItems = withItemList; return withNode; @@ -745,11 +745,11 @@ export class Parser { // with_item: test ['as' expr] private _parseWithItem(): WithItemNode { const expr = this._parseTestExpression(); - const itemNode = new WithItemNode(expr); + const itemNode = WithItemNode.create(expr); if (this._consumeTokenIfKeyword(KeywordType.As)) { itemNode.target = this._parseExpression(false); - itemNode.extend(itemNode.target); + extendRange(itemNode, itemNode.target); } return itemNode; @@ -799,18 +799,18 @@ export class Parser { const namePart = this._getTokenIfIdentifier(); if (!namePart) { this._addError('Expected decorator name', this._peekToken()); - callNameExpr = new ErrorExpressionNode( + callNameExpr = ErrorExpressionNode.create( this._peekToken(), ErrorExpressionCategory.MissingDecoratorCallName); break; } - const namePartNode = new NameNode(namePart); + const namePartNode = NameNode.create(namePart); if (!callNameExpr) { callNameExpr = namePartNode; } else { - callNameExpr = new MemberAccessExpressionNode(callNameExpr, namePartNode); + callNameExpr = MemberAccessExpressionNode.create(callNameExpr, namePartNode); } if (!this._consumeTokenIfType(TokenType.Dot)) { @@ -818,7 +818,7 @@ export class Parser { } } - const decoratorNode = new DecoratorNode(atOperator, callNameExpr); + const decoratorNode = DecoratorNode.create(atOperator, callNameExpr); if (this._consumeTokenIfType(TokenType.OpenParenthesis)) { decoratorNode.arguments = this._parseArgList(); @@ -827,7 +827,7 @@ export class Parser { if (!this._consumeTokenIfType(TokenType.CloseParenthesis)) { this._addError('Expected ")"', this._peekToken()); } else { - decoratorNode.extend(nextToken); + extendRange(decoratorNode, nextToken); } } @@ -860,12 +860,12 @@ export class Parser { const suite = this._parseSuite(); - const classNode = new ClassNode(classToken, new NameNode(nameToken), suite); + const classNode = ClassNode.create(classToken, NameNode.create(nameToken), suite); classNode.arguments = argList; if (decorators) { classNode.decorators = decorators; if (decorators.length > 0) { - classNode.extend(decorators[0]); + extendRange(classNode, decorators[0]); } } @@ -873,7 +873,7 @@ export class Parser { } private _parsePassStatement(): PassNode { - return new PassNode(this._getKeywordToken(KeywordType.Pass)); + return PassNode.create(this._getKeywordToken(KeywordType.Pass)); } private _parseBreakStatement(): BreakNode { @@ -884,7 +884,7 @@ export class Parser { breakToken); } - return new BreakNode(breakToken); + return BreakNode.create(breakToken); } private _parseContinueStatement(): ContinueNode { @@ -898,21 +898,21 @@ export class Parser { continueToken); } - return new ContinueNode(continueToken); + return ContinueNode.create(continueToken); } // return_stmt: 'return' [testlist] private _parseReturnStatement(): ReturnNode { const returnToken = this._getKeywordToken(KeywordType.Return); - const returnNode = new ReturnNode(returnToken); + const returnNode = ReturnNode.create(returnToken); if (!this._isNextTokenNeverExpression()) { const returnExpr = this._parseTestListAsExpression( ErrorExpressionCategory.MissingExpression, 'Expected expression after "return"'); returnNode.returnExpression = returnExpr; - returnNode.extend(returnExpr); + extendRange(returnNode, returnExpr); } return returnNode; @@ -926,7 +926,7 @@ export class Parser { const fromToken = this._getKeywordToken(KeywordType.From); const modName = this._parseDottedModuleName(true); - const importFromNode = new ImportFromNode(fromToken, modName); + const importFromNode = ImportFromNode.create(fromToken, modName); // Handle imports from __future__ specially because they can // change the way we interpret the rest of the file. @@ -941,12 +941,12 @@ export class Parser { importFromNode.missingImportKeyword = true; } } else { - importFromNode.extend(possibleInputToken); + extendRange(importFromNode, possibleInputToken); // Look for "*" token. const possibleStarToken = this._peekToken(); if (this._consumeTokenIfOperator(OperatorType.Multiply)) { - importFromNode.extend(possibleStarToken); + extendRange(importFromNode, possibleStarToken); importFromNode.isWildcardImport = true; } else { const inParen = this._consumeTokenIfType(TokenType.OpenParenthesis); @@ -957,20 +957,20 @@ export class Parser { break; } - const importFromAsNode = new ImportFromAsNode(new NameNode(importName)); + const importFromAsNode = ImportFromAsNode.create(NameNode.create(importName)); if (this._consumeTokenIfKeyword(KeywordType.As)) { const aliasName = this._getTokenIfIdentifier(); if (!aliasName) { this._addError('Expected alias symbol name', this._peekToken()); } else { - importFromAsNode.alias = new NameNode(aliasName); - importFromAsNode.extend(aliasName); + importFromAsNode.alias = NameNode.create(aliasName); + extendRange(importFromAsNode, aliasName); } } importFromNode.imports.push(importFromAsNode); - importFromNode.extend(importFromAsNode); + extendRange(importFromNode, importFromAsNode); if (isFutureImport) { // Add the future import to the map. @@ -993,7 +993,7 @@ export class Parser { if (!this._consumeTokenIfType(TokenType.CloseParenthesis)) { this._addError('Expected ")"', this._peekToken()); } else { - importFromNode.extend(nextToken); + extendRange(importFromNode, nextToken); } } } @@ -1008,17 +1008,17 @@ export class Parser { private _parseImportStatement(): ImportNode { const importToken = this._getKeywordToken(KeywordType.Import); - const importNode = new ImportNode(importToken); + const importNode = ImportNode.create(importToken); while (true) { const modName = this._parseDottedModuleName(); - const importAsNode = new ImportAsNode(modName); + const importAsNode = ImportAsNode.create(modName); if (this._consumeTokenIfKeyword(KeywordType.As)) { const aliasToken = this._getTokenIfIdentifier(); if (aliasToken) { - importAsNode.alias = new NameNode(aliasToken); - importAsNode.extend(importAsNode.alias); + importAsNode.alias = NameNode.create(aliasToken); + extendRange(importAsNode, importAsNode.alias); } else { this._addError('Expected identifier after "as"', this._peekToken()); } @@ -1031,7 +1031,9 @@ export class Parser { } } - importNode.extend(importNode.list); + if (importNode.list.length > 0) { + extendRange(importNode, importNode.list[importNode.list.length - 1]); + } return importNode; } @@ -1039,7 +1041,7 @@ export class Parser { // ('.' | '...')* dotted_name | ('.' | '...')+ // dotted_name: NAME ('.' NAME)* private _parseDottedModuleName(allowJustDots = false): ModuleNameNode { - const moduleNameNode = new ModuleNameNode(this._peekToken()); + const moduleNameNode = ModuleNameNode.create(this._peekToken()); while (true) { if (this._consumeTokenIfType(TokenType.Ellipsis)) { @@ -1061,8 +1063,8 @@ export class Parser { break; } - moduleNameNode.nameParts.push(new NameNode(identifier)); - moduleNameNode.extend(identifier); + moduleNameNode.nameParts.push(NameNode.create(identifier)); + extendRange(moduleNameNode, identifier); const nextToken = this._peekToken(); if (!this._consumeTokenIfType(TokenType.Dot)) { @@ -1070,7 +1072,7 @@ export class Parser { } // Extend the module name to include the dot. - moduleNameNode.extend(nextToken); + extendRange(moduleNameNode, nextToken); } return moduleNameNode; @@ -1079,18 +1081,22 @@ export class Parser { private _parseGlobalStatement(): GlobalNode { const globalToken = this._getKeywordToken(KeywordType.Global); - const globalNode = new GlobalNode(globalToken); + const globalNode = GlobalNode.create(globalToken); globalNode.nameList = this._parseNameList(); - globalNode.extend(globalNode.nameList); + if (globalNode.nameList.length > 0) { + extendRange(globalNode, globalNode.nameList[globalNode.nameList.length - 1]); + } return globalNode; } private _parseNonlocalStatement(): NonlocalNode { const nonlocalToken = this._getKeywordToken(KeywordType.Nonlocal); - const nonlocalNode = new NonlocalNode(nonlocalToken); + const nonlocalNode = NonlocalNode.create(nonlocalToken); nonlocalNode.nameList = this._parseNameList(); - nonlocalNode.extend(nonlocalNode.nameList); + if (nonlocalNode.nameList.length > 0) { + extendRange(nonlocalNode, nonlocalNode.nameList[nonlocalNode.nameList.length - 1]); + } return nonlocalNode; } @@ -1104,7 +1110,7 @@ export class Parser { break; } - nameList.push(new NameNode(name)); + nameList.push(NameNode.create(name)); if (!this._consumeTokenIfType(TokenType.Comma)) { break; @@ -1119,23 +1125,23 @@ export class Parser { private _parseRaiseStatement(): RaiseNode { const raiseToken = this._getKeywordToken(KeywordType.Raise); - const raiseNode = new RaiseNode(raiseToken); + const raiseNode = RaiseNode.create(raiseToken); if (!this._isNextTokenNeverExpression()) { raiseNode.typeExpression = this._parseTestExpression(); - raiseNode.extend(raiseNode.typeExpression); + extendRange(raiseNode, raiseNode.typeExpression); if (this._consumeTokenIfKeyword(KeywordType.From)) { raiseNode.valueExpression = this._parseTestExpression(); - raiseNode.extend(raiseNode.valueExpression); + extendRange(raiseNode, raiseNode.valueExpression); } else { if (this._consumeTokenIfType(TokenType.Comma)) { // Handle the Python 2.x variant raiseNode.valueExpression = this._parseTestExpression(); - raiseNode.extend(raiseNode.valueExpression); + extendRange(raiseNode, raiseNode.valueExpression); if (this._consumeTokenIfType(TokenType.Comma)) { raiseNode.tracebackExpression = this._parseTestExpression(); - raiseNode.extend(raiseNode.tracebackExpression); + extendRange(raiseNode, raiseNode.tracebackExpression); } } } @@ -1149,12 +1155,12 @@ export class Parser { const assertToken = this._getKeywordToken(KeywordType.Assert); const expr = this._parseTestExpression(); - const assertNode = new AssertNode(assertToken, expr); + const assertNode = AssertNode.create(assertToken, expr); if (this._consumeTokenIfType(TokenType.Comma)) { const exceptionExpr = this._parseTestExpression(); assertNode.exceptionExpression = exceptionExpr; - assertNode.extend(exceptionExpr); + extendRange(assertNode, exceptionExpr); } return assertNode; @@ -1168,9 +1174,11 @@ export class Parser { if (!exprListResult.parseError && exprListResult.list.length === 0) { this._addError('Expected expression after "del"', this._peekToken()); } - const delNode = new DelNode(delToken); + const delNode = DelNode.create(delToken); delNode.expressions = exprListResult.list; - delNode.extend(delNode.expressions); + if (delNode.expressions.length > 0) { + extendRange(delNode, delNode.expressions[delNode.expressions.length - 1]); + } return delNode; } @@ -1186,13 +1194,13 @@ export class Parser { `Use of 'yield from' requires Python 3.3 or newer`, nextToken); } - return new YieldFromExpressionNode(yieldToken, this._parseTestExpression()); + return YieldFromExpressionNode.create(yieldToken, this._parseTestExpression()); } const exprListResult = this._parseTestExpressionList(); const exprList = this._makeExpressionOrTuple(exprListResult); - return new YieldExpressionNode(yieldToken, exprList); + return YieldExpressionNode.create(yieldToken, exprList); } private _tryParseYieldExpression(): YieldExpressionNode | YieldFromExpressionNode | undefined { @@ -1205,7 +1213,7 @@ export class Parser { // simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE private _parseSimpleStatement(): StatementListNode { - const statement = new StatementListNode(this._peekToken()); + const statement = StatementListNode.create(this._peekToken()); while (true) { // Swallow invalid tokens to make sure we make forward progress. @@ -1219,9 +1227,9 @@ export class Parser { const smallStatement = this._parseSmallStatement(); statement.statements.push(smallStatement); - statement.extend(smallStatement); + extendRange(statement, smallStatement); - if (smallStatement instanceof ErrorExpressionNode) { + if (smallStatement.nodeType === ParseNodeType.Error) { // No need to log an error here. We assume that // it was already logged by _parseSmallStatement. break; @@ -1303,9 +1311,11 @@ export class Parser { const tupleStartRange: TextRange = exprListResult.list.length > 0 ? exprListResult.list[0] : this._peekToken(-1); - const tupleNode = new TupleExpressionNode(tupleStartRange); + const tupleNode = TupleExpressionNode.create(tupleStartRange); tupleNode.expressions = exprListResult.list; - tupleNode.extend(exprListResult.list); + if (exprListResult.list.length > 0) { + extendRange(tupleNode, exprListResult.list[exprListResult.list.length - 1]); + } return tupleNode; } @@ -1353,7 +1363,7 @@ export class Parser { // Make sure that we don't have more than one star expression in the list. let sawStar = false; for (const expr of exprListResult.list) { - if (expr instanceof UnpackExpressionNode) { + if (expr.nodeType === ParseNodeType.Unpack) { if (sawStar) { this._addError('Only one unpack operation allowed in list', expr); break; @@ -1373,7 +1383,7 @@ export class Parser { const startToken = this._peekToken(); if (allowUnpack && this._consumeTokenIfOperator(OperatorType.Multiply)) { - return new UnpackExpressionNode(startToken, this._parseExpression(false)); + return UnpackExpressionNode.create(startToken, this._parseExpression(false)); } return this._parseBitwiseOrExpression(); @@ -1395,7 +1405,7 @@ export class Parser { } const ifExpr = this._parseOrTest(); - if (ifExpr instanceof ErrorExpressionNode) { + if (ifExpr.nodeType === ParseNodeType.Error) { return ifExpr; } @@ -1404,7 +1414,7 @@ export class Parser { } const testExpr = this._parseOrTest(); - if (testExpr instanceof ErrorExpressionNode) { + if (testExpr.nodeType === ParseNodeType.Error) { return testExpr; } @@ -1414,23 +1424,23 @@ export class Parser { } const elseExpr = this._parseTestExpression(); - if (elseExpr instanceof ErrorExpressionNode) { + if (elseExpr.nodeType === ParseNodeType.Error) { return elseExpr; } - return new TernaryExpressionNode(ifExpr, testExpr, elseExpr); + return TernaryExpressionNode.create(ifExpr, testExpr, elseExpr); } // or_test: and_test ('or' and_test)* private _parseOrTest(): ExpressionNode { let leftExpr = this._parseAndTest(); - if (leftExpr instanceof ErrorExpressionNode) { + if (leftExpr.nodeType === ParseNodeType.Error) { return leftExpr; } while (this._consumeTokenIfKeyword(KeywordType.Or)) { const rightExpr = this._parseAndTest(); - leftExpr = new BinaryExpressionNode(leftExpr, rightExpr, OperatorType.Or); + leftExpr = BinaryExpressionNode.create(leftExpr, rightExpr, OperatorType.Or); } return leftExpr; @@ -1439,13 +1449,13 @@ export class Parser { // and_test: not_test ('and' not_test)* private _parseAndTest(): ExpressionNode { let leftExpr = this._parseNotTest(); - if (leftExpr instanceof ErrorExpressionNode) { + if (leftExpr.nodeType === ParseNodeType.Error) { return leftExpr; } while (this._consumeTokenIfKeyword(KeywordType.And)) { const rightExpr = this._parseNotTest(); - leftExpr = new BinaryExpressionNode(leftExpr, rightExpr, OperatorType.And); + leftExpr = BinaryExpressionNode.create(leftExpr, rightExpr, OperatorType.And); } return leftExpr; @@ -1453,9 +1463,10 @@ export class Parser { // not_test: 'not' not_test | comparison private _parseNotTest(): ExpressionNode { + const notToken = this._peekToken(); if (this._consumeTokenIfKeyword(KeywordType.Not)) { const notExpr = this._parseNotTest(); - return new UnaryExpressionNode(notExpr, OperatorType.Not); + return UnaryExpressionNode.create(notToken, notExpr, OperatorType.Not); } return this._parseComparison(); @@ -1465,7 +1476,7 @@ export class Parser { // comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' private _parseComparison(): ExpressionNode { let leftExpr = this._parseBitwiseOrExpression(); - if (leftExpr instanceof ErrorExpressionNode) { + if (leftExpr.nodeType === ParseNodeType.Error) { return leftExpr; } @@ -1498,7 +1509,7 @@ export class Parser { } const rightExpr = this._parseBitwiseOrExpression(); - leftExpr = new BinaryExpressionNode(leftExpr, rightExpr, comparisonOperator); + leftExpr = BinaryExpressionNode.create(leftExpr, rightExpr, comparisonOperator); } return leftExpr; @@ -1507,13 +1518,13 @@ export class Parser { // expr: xor_expr ('|' xor_expr)* private _parseBitwiseOrExpression(): ExpressionNode { let leftExpr = this._parseBitwiseXorExpression(); - if (leftExpr instanceof ErrorExpressionNode) { + if (leftExpr.nodeType === ParseNodeType.Error) { return leftExpr; } while (this._consumeTokenIfOperator(OperatorType.BitwiseOr)) { const rightExpr = this._parseBitwiseXorExpression(); - leftExpr = new BinaryExpressionNode(leftExpr, rightExpr, OperatorType.BitwiseOr); + leftExpr = BinaryExpressionNode.create(leftExpr, rightExpr, OperatorType.BitwiseOr); } return leftExpr; @@ -1522,13 +1533,13 @@ export class Parser { // xor_expr: and_expr ('^' and_expr)* private _parseBitwiseXorExpression(): ExpressionNode { let leftExpr = this._parseBitwiseAndExpression(); - if (leftExpr instanceof ErrorExpressionNode) { + if (leftExpr.nodeType === ParseNodeType.Error) { return leftExpr; } while (this._consumeTokenIfOperator(OperatorType.BitwiseXor)) { const rightExpr = this._parseBitwiseAndExpression(); - leftExpr = new BinaryExpressionNode(leftExpr, rightExpr, OperatorType.BitwiseXor); + leftExpr = BinaryExpressionNode.create(leftExpr, rightExpr, OperatorType.BitwiseXor); } return leftExpr; @@ -1537,13 +1548,13 @@ export class Parser { // and_expr: shift_expr ('&' shift_expr)* private _parseBitwiseAndExpression(): ExpressionNode { let leftExpr = this._parseShiftExpression(); - if (leftExpr instanceof ErrorExpressionNode) { + if (leftExpr.nodeType === ParseNodeType.Error) { return leftExpr; } while (this._consumeTokenIfOperator(OperatorType.BitwiseAnd)) { const rightExpr = this._parseShiftExpression(); - leftExpr = new BinaryExpressionNode(leftExpr, rightExpr, OperatorType.BitwiseAnd); + leftExpr = BinaryExpressionNode.create(leftExpr, rightExpr, OperatorType.BitwiseAnd); } return leftExpr; @@ -1552,7 +1563,7 @@ export class Parser { // shift_expr: arith_expr (('<<'|'>>') arith_expr)* private _parseShiftExpression(): ExpressionNode { let leftExpr = this._parseAirthmeticExpression(); - if (leftExpr instanceof ErrorExpressionNode) { + if (leftExpr.nodeType === ParseNodeType.Error) { return leftExpr; } @@ -1560,7 +1571,7 @@ export class Parser { while (nextOperator === OperatorType.LeftShift || nextOperator === OperatorType.RightShift) { this._getNextToken(); const rightExpr = this._parseAirthmeticExpression(); - leftExpr = new BinaryExpressionNode(leftExpr, rightExpr, nextOperator); + leftExpr = BinaryExpressionNode.create(leftExpr, rightExpr, nextOperator); nextOperator = this._peekOperatorType(); } @@ -1570,7 +1581,7 @@ export class Parser { // arith_expr: term (('+'|'-') term)* private _parseAirthmeticExpression(): ExpressionNode { let leftExpr = this._parseAirthmeticTerm(); - if (leftExpr instanceof ErrorExpressionNode) { + if (leftExpr.nodeType === ParseNodeType.Error) { return leftExpr; } @@ -1578,11 +1589,11 @@ export class Parser { while (nextOperator === OperatorType.Add || nextOperator === OperatorType.Subtract) { this._getNextToken(); const rightExpr = this._parseAirthmeticTerm(); - if (rightExpr instanceof ErrorExpressionNode) { + if (rightExpr.nodeType === ParseNodeType.Error) { return rightExpr; } - leftExpr = new BinaryExpressionNode(leftExpr, rightExpr, nextOperator); + leftExpr = BinaryExpressionNode.create(leftExpr, rightExpr, nextOperator); nextOperator = this._peekOperatorType(); } @@ -1592,7 +1603,7 @@ export class Parser { // term: factor (('*'|'@'|'/'|'%'|'//') factor)* private _parseAirthmeticTerm(): ExpressionNode { let leftExpr = this._parseAirthmeticFactor(); - if (leftExpr instanceof ErrorExpressionNode) { + if (leftExpr.nodeType === ParseNodeType.Error) { return leftExpr; } @@ -1604,7 +1615,7 @@ export class Parser { nextOperator === OperatorType.FloorDivide) { this._getNextToken(); const rightExpr = this._parseAirthmeticFactor(); - leftExpr = new BinaryExpressionNode(leftExpr, rightExpr, nextOperator); + leftExpr = BinaryExpressionNode.create(leftExpr, rightExpr, nextOperator); nextOperator = this._peekOperatorType(); } @@ -1614,23 +1625,24 @@ export class Parser { // factor: ('+'|'-'|'~') factor | power // power: atom_expr ['**' factor] private _parseAirthmeticFactor(): ExpressionNode { + const nextToken = this._peekToken(); const nextOperator = this._peekOperatorType(); if (nextOperator === OperatorType.Add || nextOperator === OperatorType.Subtract || nextOperator === OperatorType.BitwiseInvert) { this._getNextToken(); const expression = this._parseAirthmeticFactor(); - return new UnaryExpressionNode(expression, nextOperator); + return UnaryExpressionNode.create(nextToken, expression, nextOperator); } const leftExpr = this._parseAtomExpression(); - if (leftExpr instanceof ErrorExpressionNode) { + if (leftExpr.nodeType === ParseNodeType.Error) { return leftExpr; } if (this._consumeTokenIfOperator(OperatorType.Power)) { const rightExpr = this._parseAirthmeticFactor(); - return new BinaryExpressionNode(leftExpr, rightExpr, OperatorType.Power); + return BinaryExpressionNode.create(leftExpr, rightExpr, OperatorType.Power); } return leftExpr; @@ -1650,7 +1662,7 @@ export class Parser { } let atomExpression = this._parseAtom(); - if (atomExpression instanceof ErrorExpressionNode) { + if (atomExpression.nodeType === ParseNodeType.Error) { return atomExpression; } @@ -1661,9 +1673,11 @@ export class Parser { // Is it a function call? if (this._consumeTokenIfType(TokenType.OpenParenthesis)) { const argList = this._parseArgList(); - const callNode = new CallExpressionNode(atomExpression); + const callNode = CallExpressionNode.create(atomExpression); callNode.arguments = argList; - callNode.extend(argList); + if (argList.length > 0) { + extendRange(callNode, argList[argList.length - 1]); + } const nextToken = this._peekToken(); if (!this._consumeTokenIfType(TokenType.CloseParenthesis)) { @@ -1675,10 +1689,10 @@ export class Parser { // Extend the node's range to include the rest of the line. // This helps the signatureHelpProvider. - callNode.extend(this._peekToken()); + extendRange(callNode, this._peekToken()); return callNode; } else { - callNode.extend(nextToken); + extendRange(callNode, nextToken); } atomExpression = callNode; @@ -1686,14 +1700,14 @@ export class Parser { // Is it an index operator? const indexExpr = this._parseSubscriptList(); let expressions = [indexExpr]; - if (indexExpr instanceof TupleExpressionNode) { + if (indexExpr.nodeType === ParseNodeType.Tuple) { expressions = indexExpr.expressions; } const closingToken = this._peekToken(); - const indexItemsNode = new IndexItemsNode(nextToken, closingToken, expressions); - const indexNode = new IndexExpressionNode(atomExpression, indexItemsNode); - indexNode.extend(indexNode); + const indexItemsNode = IndexItemsNode.create(nextToken, closingToken, expressions); + const indexNode = IndexExpressionNode.create(atomExpression, indexItemsNode); + extendRange(indexNode, indexNode); if (!this._consumeTokenIfType(TokenType.CloseBracket)) { return this._handleExpressionParseError( @@ -1710,15 +1724,15 @@ export class Parser { ErrorExpressionCategory.MissingMemberAccessName, 'Expected member name after "."', atomExpression); } - atomExpression = new MemberAccessExpressionNode( - atomExpression, new NameNode(memberName)); + atomExpression = MemberAccessExpressionNode.create( + atomExpression, NameNode.create(memberName)); } else { break; } } if (awaitToken) { - return new AwaitExpressionNode(awaitToken, atomExpression); + return AwaitExpressionNode.create(awaitToken, atomExpression); } return atomExpression; @@ -1779,11 +1793,14 @@ export class Parser { return sliceExpressions[0]!; } - const sliceNode = new SliceExpressionNode(firstToken); + const sliceNode = SliceExpressionNode.create(firstToken); sliceNode.startValue = sliceExpressions[0]; sliceNode.endValue = sliceExpressions[1]; sliceNode.stepValue = sliceExpressions[2]; - sliceNode.extend(sliceExpressions[2] || sliceExpressions[1] || sliceExpressions[0]); + const extension = sliceExpressions[2] || sliceExpressions[1] || sliceExpressions[0]; + if (extension) { + extendRange(sliceNode, extension); + } return sliceNode; } @@ -1841,7 +1858,7 @@ export class Parser { const nameExpr = valueExpr; valueExpr = this._parseTestExpression(); - if (nameExpr instanceof NameNode) { + if (nameExpr.nodeType === ParseNodeType.Name) { nameIdentifier = nameExpr.nameToken; } else { this._addError('Expected parameter name', nameExpr); @@ -1854,9 +1871,9 @@ export class Parser { } } - const argNode = new ArgumentNode(firstToken, valueExpr, argType); + const argNode = ArgumentNode.create(firstToken, valueExpr, argType); if (nameIdentifier) { - argNode.name = new NameNode(nameIdentifier); + argNode.name = NameNode.create(nameIdentifier); } return argNode; @@ -1870,15 +1887,15 @@ export class Parser { const nextToken = this._peekToken(); if (nextToken.type === TokenType.Ellipsis) { - return new EllipsisNode(this._getNextToken()); + return EllipsisNode.create(this._getNextToken()); } if (nextToken.type === TokenType.Number) { - return new NumberNode(this._getNextToken() as NumberToken); + return NumberNode.create(this._getNextToken() as NumberToken); } if (nextToken.type === TokenType.Identifier) { - return new NameNode(this._getNextToken() as IdentifierToken); + return NameNode.create(this._getNextToken() as IdentifierToken); } if (nextToken.type === TokenType.String) { @@ -1899,13 +1916,13 @@ export class Parser { keywordToken.keywordType === KeywordType.True || keywordToken.keywordType === KeywordType.Debug || keywordToken.keywordType === KeywordType.None) { - return new ConstantNode(this._getNextToken() as KeywordToken); + return ConstantNode.create(this._getNextToken() as KeywordToken); } // Make an identifier out of the keyword. const keywordAsIdentifier = this._getTokenIfIdentifier(); if (keywordAsIdentifier) { - return new NameNode(keywordAsIdentifier); + return NameNode.create(keywordAsIdentifier); } } @@ -1919,10 +1936,10 @@ export class Parser { // child node can be passed to help the completion provider determine // what to do. private _handleExpressionParseError(category: ErrorExpressionCategory, - errorMsg: string, childNode?: ParseNode): ErrorExpressionNode { + errorMsg: string, childNode?: ExpressionNode): ErrorExpressionNode { this._addError(errorMsg, this._peekToken()); - const expr = new ErrorExpressionNode(this._peekToken(), category, childNode); + const expr = ErrorExpressionNode.create(this._peekToken(), category, childNode); this._consumeTokensUntilType(TokenType.NewLine); return expr; } @@ -1944,7 +1961,7 @@ export class Parser { testExpr = this._tryParseLambdaExpression(false) || this._parseOrTest(); } - const lambdaNode = new LambdaNode(labmdaToken, testExpr); + const lambdaNode = LambdaNode.create(labmdaToken, testExpr); lambdaNode.parameters = argList; return lambdaNode; } @@ -1970,7 +1987,7 @@ export class Parser { ErrorExpressionCategory.MissingTupleCloseParen, 'Expected ")"'); } else { - yieldExpr.extend(this._getNextToken()); + extendRange(yieldExpr, this._getNextToken()); } return yieldExpr; @@ -1984,7 +2001,7 @@ export class Parser { ErrorExpressionCategory.MissingTupleCloseParen, 'Expected ")"'); } else { - tupleOrExpression.extend(this._getNextToken()); + extendRange(tupleOrExpression, this._getNextToken()); } return tupleOrExpression; @@ -2004,9 +2021,11 @@ export class Parser { 'Expected "]"'); } - const listAtom = new ListNode(startBracket); - listAtom.extend(closeBracket); - listAtom.extend(exprListResult.list); + const listAtom = ListNode.create(startBracket); + extendRange(listAtom, closeBracket); + if (exprListResult.list.length > 0) { + extendRange(listAtom, exprListResult.list[exprListResult.list.length - 1]); + } listAtom.entries = exprListResult.list; return listAtom; } @@ -2064,14 +2083,14 @@ export class Parser { } if (keyExpression && valueExpression) { - if (keyExpression instanceof UnpackExpressionNode) { + if (keyExpression.nodeType === ParseNodeType.Unpack) { this._addError('Unpack operation not allowed in dictionaries', keyExpression); } if (isSet) { this._addError('Key/value pairs are not allowed within a set', valueExpression); } else { - const keyEntryNode = new DictionaryKeyEntryNode(keyExpression, valueExpression); + const keyEntryNode = DictionaryKeyEntryNode.create(keyExpression, valueExpression); let dictEntry: DictionaryEntryNode = keyEntryNode; const listComp = this._tryParseListComprehension(keyEntryNode); if (listComp) { @@ -2085,7 +2104,7 @@ export class Parser { if (isSet) { this._addError('Unpack operator not allowed within a set', doubleStarExpression); } else { - const listEntryNode = new DictionaryExpandEntryNode(doubleStarExpression); + const listEntryNode = DictionaryExpandEntryNode.create(doubleStarExpression); let expandEntryNode: DictionaryEntryNode = listEntryNode; const listComp = this._tryParseListComprehension(listEntryNode); if (listComp) { @@ -2129,16 +2148,24 @@ export class Parser { } if (isSet) { - const setAtom = new SetNode(startBrace); - setAtom.extend(closeCurlyBrace); - setAtom.extend(setEntries); + const setAtom = SetNode.create(startBrace); + if (closeCurlyBrace) { + extendRange(setAtom, closeCurlyBrace); + } + if (setEntries.length > 0) { + extendRange(setAtom, setEntries[setEntries.length - 1]); + } setAtom.entries = setEntries; return setAtom; } - const dictionaryAtom = new DictionaryNode(startBrace); - dictionaryAtom.extend(closeCurlyBrace); - dictionaryAtom.extend(dictionaryEntries); + const dictionaryAtom = DictionaryNode.create(startBrace); + if (closeCurlyBrace) { + extendRange(dictionaryAtom, closeCurlyBrace); + } + if (dictionaryEntries.length > 0) { + extendRange(dictionaryAtom, dictionaryEntries[dictionaryEntries.length - 1]); + } dictionaryAtom.entries = dictionaryEntries; return dictionaryAtom; } @@ -2157,7 +2184,7 @@ export class Parser { } const expr = parser(); - if (expr instanceof ErrorExpressionNode) { + if (expr.nodeType === ParseNodeType.Error) { parseError = expr; break; } @@ -2189,7 +2216,7 @@ export class Parser { let leftExpr = this._parseTestOrStarListAsExpression(); let annotationExpr: ExpressionNode | undefined; - if (leftExpr instanceof ErrorExpressionNode) { + if (leftExpr.nodeType === ParseNodeType.Error) { return leftExpr; } @@ -2197,7 +2224,7 @@ export class Parser { if (this._consumeTokenIfType(TokenType.Colon)) { this._parseTypeAnnotation(() => { annotationExpr = this._parseTestExpression(); - leftExpr = new TypeAnnotationExpressionNode(leftExpr, annotationExpr); + leftExpr = TypeAnnotationExpressionNode.create(leftExpr, annotationExpr); if (!this._parseOptions.isStubFile && this._getLanguageVersion() < PythonVersion.V36) { this._addError('Type annotations for variables requires Python 3.6 or newer', @@ -2210,7 +2237,7 @@ export class Parser { } const rightExpr = this._parseTestExpression(); - return new AssignmentNode(leftExpr, rightExpr); + return AssignmentNode.create(leftExpr, rightExpr); } // Is this a simple assignment? @@ -2225,7 +2252,7 @@ export class Parser { this._parseTestListAsExpression( ErrorExpressionCategory.MissingExpression, 'Expected expression to the right of operator'); - return new AugmentedAssignmentExpressionNode(leftExpr, rightExpr, operatorToken.operatorType); + return AugmentedAssignmentExpressionNode.create(leftExpr, rightExpr, operatorToken.operatorType); } return leftExpr; @@ -2240,25 +2267,25 @@ export class Parser { 'Expected expression to the right of "="'); } - if (rightExpr instanceof ErrorExpressionNode) { + if (rightExpr.nodeType === ParseNodeType.Error) { return rightExpr; } // Recurse until we've consumed the entire chain. if (this._consumeTokenIfOperator(OperatorType.Assign)) { rightExpr = this._parseChainAssignments(rightExpr); - if (rightExpr instanceof ErrorExpressionNode) { + if (rightExpr.nodeType === ParseNodeType.Error) { return rightExpr; } } - const assignmentNode = new AssignmentNode(leftExpr, rightExpr); + const assignmentNode = AssignmentNode.create(leftExpr, rightExpr); // Look for a type annotation comment at the end of the line. const typeAnnotationComment = this._getTypeAnnotationComment(); if (typeAnnotationComment) { assignmentNode.typeAnnotationComment = typeAnnotationComment; - assignmentNode.extend(assignmentNode.typeAnnotationComment); + extendRange(assignmentNode, assignmentNode.typeAnnotationComment); } return assignmentNode; @@ -2301,7 +2328,7 @@ export class Parser { private _makeStringNode(stringToken: StringToken): StringNode { const unescapedResult = StringTokenUtils.getUnescapedString(stringToken); this._reportStringTokenErrors(stringToken, unescapedResult); - return new StringNode(stringToken, unescapedResult.value, unescapedResult.unescapeErrors.length > 0); + return StringNode.create(stringToken, unescapedResult.value, unescapedResult.unescapeErrors.length > 0); } private _getTypeAnnotationComment(): ExpressionNode | undefined { @@ -2312,11 +2339,12 @@ export class Parser { const curToken = this._tokenizerOutput!.tokens.getItemAt(this._tokenIndex - 1); const nextToken = this._tokenizerOutput!.tokens.getItemAt(this._tokenIndex); - if (curToken.end === nextToken.start) { + if (curToken.start + curToken.length === nextToken.start) { return undefined; } - const interTokenContents = this._fileContents!.substring(curToken.end, nextToken.start); + const interTokenContents = this._fileContents!.substring( + curToken.start + curToken.length, nextToken.start); const commentRegEx = /^(\s*#\s*type:\s*)([^\r\n]*)/; const match = interTokenContents.match(commentRegEx); if (!match) { @@ -2325,11 +2353,11 @@ export class Parser { // Synthesize a string token and StringNode. const typeString = match[2]; - const tokenOffset = curToken.end + match[1].length; + const tokenOffset = curToken.start + curToken.length + match[1].length; const stringToken = new StringToken(tokenOffset, typeString.length, StringTokenFlags.None, typeString, 0, undefined); const stringNode = this._makeStringNode(stringToken); - const stringListNode = new StringListNode([stringNode]); + const stringListNode = StringListNode.create([stringNode]); const parser = new Parser(); const parseResults = parser.parseTextExpression(this._fileContents!, @@ -2352,7 +2380,7 @@ export class Parser { const unescapedResult = StringTokenUtils.getUnescapedString(stringToken); this._reportStringTokenErrors(stringToken, unescapedResult); - const formatExpressions: FormatStringExpression[] = []; + const formatExpressions: ExpressionNode[] = []; for (const segment of unescapedResult.formatStringSegments) { if (segment.isExpression) { @@ -2372,20 +2400,18 @@ export class Parser { stringToken.start) || stringToken.start; const textRangeEnd = (diag.range ? convertPositionToOffset(diag.range.end, this._tokenizerOutput!.lines) : - stringToken.end) || stringToken.end; - const textRange = new TextRange(textRangeStart, textRangeEnd - textRangeStart); + stringToken.start + stringToken.length) || (stringToken.start + stringToken.length); + const textRange = { start: textRangeStart, length: textRangeEnd - textRangeStart }; this._addError(diag.message, textRange); }); if (parseResults.parseTree) { - formatExpressions.push({ - expression: parseResults.parseTree - }); + formatExpressions.push(parseResults.parseTree); } } } - return new FormatStringNode(stringToken, unescapedResult.value, + return FormatStringNode.create(stringToken, unescapedResult.value, unescapedResult.unescapeErrors.length > 0, formatExpressions); } @@ -2470,7 +2496,7 @@ export class Parser { } } - const stringNode = new StringListNode(stringList); + const stringNode = StringListNode.create(stringList); // If we're parsing a type annotation, parse the contents of the string. if (this._isParsingTypeAnnotation) { @@ -2630,10 +2656,10 @@ export class Parser { if (nextToken.type === TokenType.Keyword) { const keywordType = this._peekKeywordType(); if (!disallowedKeywords.find(type => type === keywordType)) { - const keywordText = this._fileContents!.substring(nextToken.start, nextToken.end); + const keywordText = this._fileContents!.substr(nextToken.start, nextToken.length); this._getNextToken(); return new IdentifierToken(nextToken.start, - nextToken.end - nextToken.start, keywordText, nextToken.comments); + nextToken.length, keywordText, nextToken.comments); } } @@ -2699,6 +2725,7 @@ export class Parser { private _addError(message: string, range: TextRange) { assert(range !== undefined); this._diagSink.addError(message, - convertOffsetsToRange(range.start, range.end, this._tokenizerOutput!.lines)); + convertOffsetsToRange(range.start, range.start + range.length, + this._tokenizerOutput!.lines)); } } diff --git a/server/src/parser/tokenizer.ts b/server/src/parser/tokenizer.ts index 1a97f5ba9..61a4b133f 100644 --- a/server/src/parser/tokenizer.ts +++ b/server/src/parser/tokenizer.ts @@ -405,7 +405,7 @@ export class Tokenizer { private _addLineRange() { const lineLength = this._cs.position - this._prevLineStart; if (lineLength > 0) { - this._lineRanges.push(new TextRange(this._prevLineStart, lineLength)); + this._lineRanges.push({ start: this._prevLineStart, length: lineLength }); } this._prevLineStart = this._cs.position; diff --git a/server/src/parser/tokenizerTypes.ts b/server/src/parser/tokenizerTypes.ts index 66ee94f2b..8c4a9c691 100644 --- a/server/src/parser/tokenizerTypes.ts +++ b/server/src/parser/tokenizerTypes.ts @@ -158,25 +158,29 @@ export enum StringTokenFlags { Unterminated = 0x1000 } -export class Comment extends TextRange { +export class Comment { readonly value: string; + readonly range: TextRange; constructor(start: number, length: number, value: string) { - super(start, length); + this.range = { start, length }; this.value = value; } } -export class Token extends TextRange implements Token { +export class Token implements TextRange { readonly type: TokenType; // Comments prior to the token. + readonly start: number; + readonly length: number; readonly comments?: Comment[]; constructor(type: TokenType, start: number, length: number, comments: Comment[] | undefined) { - super(start, length); + this.start = start; + this.length = length; this.type = type; this.comments = comments; } diff --git a/server/src/tests/testWalker.ts b/server/src/tests/testWalker.ts index e83e1f977..46630fbd5 100644 --- a/server/src/tests/testWalker.ts +++ b/server/src/tests/testWalker.ts @@ -7,19 +7,20 @@ import * as assert from 'assert'; import { ParseTreeWalker } from '../analyzer/parseTreeWalker'; -import { AssignmentNode, ParseNode, ParseNodeArray, StringListNode } from '../parser/parseNodes'; +import { TextRange } from '../common/textRange'; +import { ParseNode, ParseNodeArray, ParseNodeType, StringListNode } from '../parser/parseNodes'; export class TestWalker extends ParseTreeWalker { constructor() { super(); } - visitNode(node: ParseNode): boolean { - const children = node.getChildren(); + visitNode(node: ParseNode) { + const children = super.visitNode(node); this._verifyParentChildLinks(node, children); this._verifyChildRanges(node, children); - return super.visitNode(node); + return children; } // Make sure that all of the children point to their parent. @@ -44,13 +45,13 @@ export class TestWalker extends ParseTreeWalker { // There are a few exceptions we need to deal with here. Comment // annotations can occur outside of an assignment node's range. - if (node instanceof AssignmentNode) { + if (node.nodeType === ParseNodeType.Assignment) { if (child === node.typeAnnotationComment) { skipCheck = true; } } - if (node instanceof StringListNode) { + if (node.nodeType === ParseNodeType.StringList) { if (child === node.typeAnnotation) { skipCheck = true; } @@ -58,10 +59,10 @@ export class TestWalker extends ParseTreeWalker { if (!skipCheck) { // Make sure the child is contained within the parent. - assert(child.start >= node.start && child.end <= node.end); + assert(child.start >= node.start && TextRange.getEnd(child) <= TextRange.getEnd(node)); if (prevNode) { // Make sure the child is after the previous child. - assert(child.start >= prevNode.end); + assert(child.start >= TextRange.getEnd(prevNode)); } prevNode = child;