Switched parse nodes from classes to interfaces with an enumerated union type.

This commit is contained in:
Eric Traut 2019-09-07 17:37:36 -07:00
parent 499f8bff30
commit 80d120c838
33 changed files with 1976 additions and 1283 deletions

View File

@ -1010,9 +1010,9 @@
} }
}, },
"typescript": { "typescript": {
"version": "3.5.2", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz",
"integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==", "integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==",
"dev": true "dev": true
}, },
"uc.micro": { "uc.micro": {

View File

@ -104,7 +104,7 @@
"postinstall": "node ./node_modules/vscode/bin/install" "postinstall": "node ./node_modules/vscode/bin/install"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^3.5.2", "typescript": "^3.6.2",
"vsce": "^1.64.0", "vsce": "^1.64.0",
"vscode": "^1.1.35" "vscode": "^1.1.35"
}, },

6
package-lock.json generated
View File

@ -17,9 +17,9 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "3.5.2", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz",
"integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==", "integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==",
"dev": true "dev": true
} }
} }

View File

@ -25,7 +25,7 @@
"devDependencies": { "devDependencies": {
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "^11.13.15", "@types/node": "^11.13.15",
"typescript": "^3.5.2" "typescript": "^3.6.2"
}, },
"main": "index.js", "main": "index.js",
"bin": { "bin": {

View File

@ -6387,9 +6387,9 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "3.5.2", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz",
"integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==", "integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==",
"dev": true "dev": true
}, },
"typescript-char": { "typescript-char": {

View File

@ -38,7 +38,7 @@
"ts-loader": "^5.4.5", "ts-loader": "^5.4.5",
"tslint": "^5.18.0", "tslint": "^5.18.0",
"tslint-microsoft-contrib": "^6.2.0", "tslint-microsoft-contrib": "^6.2.0",
"typescript": "^3.5.2", "typescript": "^3.6.2",
"webpack": "^4.35.0", "webpack": "^4.35.0",
"webpack-cli": "^3.3.5" "webpack-cli": "^3.3.5"
}, },

View File

@ -16,16 +16,13 @@ import { TextRangeDiagnosticSink } from '../common/diagnosticSink';
import { convertOffsetsToRange } from '../common/positionUtils'; import { convertOffsetsToRange } from '../common/positionUtils';
import StringMap from '../common/stringMap'; import StringMap from '../common/stringMap';
import { TextRange } from '../common/textRange'; import { TextRange } from '../common/textRange';
import { ArgumentCategory, AssignmentNode, AugmentedAssignmentExpressionNode, import { ArgumentCategory, AugmentedAssignmentExpressionNode, BinaryExpressionNode,
AwaitExpressionNode, BinaryExpressionNode, CallExpressionNode, ClassNode, CallExpressionNode, ClassNode, ConstantNode, DecoratorNode, DictionaryNode,
ConstantNode, DecoratorNode, DictionaryExpandEntryNode, DictionaryKeyEntryNode, ExpressionNode, IndexExpressionNode, IndexItemsNode, isExpressionNode, LambdaNode,
DictionaryNode, EllipsisNode, ErrorExpressionNode, ExpressionNode, ListComprehensionNode, ListNode, MemberAccessExpressionNode, NameNode, ParameterCategory,
IndexExpressionNode, IndexItemsNode, LambdaNode, ListComprehensionForNode, ParseNode, ParseNodeType, SetNode, SliceExpressionNode, TernaryExpressionNode,
ListComprehensionIfNode, ListComprehensionNode, ListNode, MemberAccessExpressionNode, TupleExpressionNode, UnaryExpressionNode, YieldExpressionNode,
NameNode, NumberNode, ParameterCategory, ParseNode, SetNode, SliceExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes';
StatementListNode, StringListNode, TernaryExpressionNode, TupleExpressionNode,
TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode,
YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes';
import { KeywordToken, KeywordType, OperatorType, StringTokenFlags, import { KeywordToken, KeywordType, OperatorType, StringTokenFlags,
TokenType } from '../parser/tokenizerTypes'; TokenType } from '../parser/tokenizerTypes';
import { ScopeUtils } from '../scopeUtils'; 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 // Gets a member type from an object and if it's a function binds
// it to the object. // 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 { usage: EvaluatorUsage, memberAccessFlags = MemberAccessFlags.None): Type | undefined {
const memberInfo = this._getTypeFromClassMemberName(errorNode, 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 // Gets a member type from a class and if it's a function binds
// it to the object. // 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 { usage: EvaluatorUsage, memberAccessFlags = MemberAccessFlags.None): Type | undefined {
const memberInfo = this._getTypeFromClassMemberName(errorNode, const memberInfo = this._getTypeFromClassMemberName(errorNode,
@ -445,19 +442,19 @@ export class ExpressionEvaluator {
}); });
node.suite.statements.forEach(statementList => { node.suite.statements.forEach(statementList => {
if (statementList instanceof StatementListNode) { if (statementList.nodeType === ParseNodeType.StatementList) {
statementList.statements.forEach(statement => { statementList.statements.forEach(statement => {
let variableNameNode: NameNode | undefined; let variableNameNode: NameNode | undefined;
let variableType: Type | undefined; let variableType: Type | undefined;
let hasDefaultValue = false; let hasDefaultValue = false;
if (statement instanceof AssignmentNode) { if (statement.nodeType === ParseNodeType.Assignment) {
if (statement.leftExpression instanceof NameNode) { if (statement.leftExpression.nodeType === ParseNodeType.Name) {
variableNameNode = statement.leftExpression; variableNameNode = statement.leftExpression;
variableType = TypeUtils.stripLiteralValue( variableType = TypeUtils.stripLiteralValue(
this.getType(statement.rightExpression, { method: 'get' })); this.getType(statement.rightExpression, { method: 'get' }));
} else if (statement.leftExpression instanceof TypeAnnotationExpressionNode && } else if (statement.leftExpression.nodeType === ParseNodeType.TypeAnnotation &&
statement.leftExpression.valueExpression instanceof NameNode) { statement.leftExpression.valueExpression.nodeType === ParseNodeType.Name) {
variableNameNode = statement.leftExpression.valueExpression; variableNameNode = statement.leftExpression.valueExpression;
variableType = TypeUtils.convertClassToObject( variableType = TypeUtils.convertClassToObject(
@ -466,8 +463,8 @@ export class ExpressionEvaluator {
} }
hasDefaultValue = true; hasDefaultValue = true;
} else if (statement instanceof TypeAnnotationExpressionNode) { } else if (statement.nodeType === ParseNodeType.TypeAnnotation) {
if (statement.valueExpression instanceof NameNode) { if (statement.valueExpression.nodeType === ParseNodeType.Name) {
variableNameNode = statement.valueExpression; variableNameNode = statement.valueExpression;
variableType = TypeUtils.convertClassToObject( variableType = TypeUtils.convertClassToObject(
this.getType(statement.typeAnnotation, { method: 'get' }, this.getType(statement.typeAnnotation, { method: 'get' },
@ -609,21 +606,21 @@ export class ExpressionEvaluator {
let typeResult: TypeResult | undefined; let typeResult: TypeResult | undefined;
if (node instanceof NameNode) { if (node.nodeType === ParseNodeType.Name) {
typeResult = this._getTypeFromName(node, usage, flags); typeResult = this._getTypeFromName(node, usage, flags);
} else if (node instanceof MemberAccessExpressionNode) { } else if (node.nodeType === ParseNodeType.MemberAccess) {
typeResult = this._getTypeFromMemberAccessExpression(node, usage, flags); typeResult = this._getTypeFromMemberAccessExpression(node, usage, flags);
} else if (node instanceof IndexExpressionNode) { } else if (node.nodeType === ParseNodeType.Index) {
typeResult = this._getTypeFromIndexExpression(node, usage); typeResult = this._getTypeFromIndexExpression(node, usage);
} else if (node instanceof CallExpressionNode) { } else if (node.nodeType === ParseNodeType.Call) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromCallExpression(node, flags); typeResult = this._getTypeFromCallExpression(node, flags);
} else if (node instanceof TupleExpressionNode) { } else if (node.nodeType === ParseNodeType.Tuple) {
typeResult = this._getTypeFromTupleExpression(node, usage); typeResult = this._getTypeFromTupleExpression(node, usage);
} else if (node instanceof ConstantNode) { } else if (node.nodeType === ParseNodeType.Constant) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromConstantExpression(node); typeResult = this._getTypeFromConstantExpression(node);
} else if (node instanceof StringListNode) { } else if (node.nodeType === ParseNodeType.StringList) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
if (node.typeAnnotation && !AnalyzerNodeInfo.getIgnoreTypeAnnotation(node)) { if (node.typeAnnotation && !AnalyzerNodeInfo.getIgnoreTypeAnnotation(node)) {
let typeResult: TypeResult = { node, type: UnknownType.create() }; let typeResult: TypeResult = { node, type: UnknownType.create() };
@ -639,12 +636,12 @@ export class ExpressionEvaluator {
const isBytes = (node.strings[0].token.flags & StringTokenFlags.Bytes) !== 0; const isBytes = (node.strings[0].token.flags & StringTokenFlags.Bytes) !== 0;
typeResult = { node, type: this._cloneBuiltinTypeWithLiteral( typeResult = { node, type: this._cloneBuiltinTypeWithLiteral(
isBytes ? 'bytes' : 'str', node.getValue()) }; isBytes ? 'bytes' : 'str', node.strings.map(s => s.value).join('')) };
} else if (node instanceof NumberNode) { } else if (node.nodeType === ParseNodeType.Number) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = { node, type: this._cloneBuiltinTypeWithLiteral( typeResult = { node, type: this._cloneBuiltinTypeWithLiteral(
node.token.isInteger ? 'int' : 'float', node.token.value) }; node.token.isInteger ? 'int' : 'float', node.token.value) };
} else if (node instanceof EllipsisNode) { } else if (node.nodeType === ParseNodeType.Ellipsis) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
if ((flags & EvaluatorFlags.ConvertEllipsisToAny) !== 0) { if ((flags & EvaluatorFlags.ConvertEllipsisToAny) !== 0) {
typeResult = { type: AnyType.create(true), node }; typeResult = { type: AnyType.create(true), node };
@ -653,61 +650,61 @@ export class ExpressionEvaluator {
AnyType.create(); AnyType.create();
typeResult = { type: ellipsisType, node }; typeResult = { type: ellipsisType, node };
} }
} else if (node instanceof UnaryExpressionNode) { } else if (node.nodeType === ParseNodeType.UnaryOperation) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromUnaryExpression(node); typeResult = this._getTypeFromUnaryExpression(node);
} else if (node instanceof BinaryExpressionNode) { } else if (node.nodeType === ParseNodeType.BinaryOperation) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromBinaryExpression(node); typeResult = this._getTypeFromBinaryExpression(node);
} else if (node instanceof AugmentedAssignmentExpressionNode) { } else if (node.nodeType === ParseNodeType.AugmentedAssignment) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromAugmentedExpression(node); typeResult = this._getTypeFromAugmentedExpression(node);
} else if (node instanceof ListNode) { } else if (node.nodeType === ParseNodeType.List) {
typeResult = this._getTypeFromListExpression(node); typeResult = this._getTypeFromListExpression(node);
} else if (node instanceof SliceExpressionNode) { } else if (node.nodeType === ParseNodeType.Slice) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromSliceExpression(node); typeResult = this._getTypeFromSliceExpression(node);
} else if (node instanceof AwaitExpressionNode) { } else if (node.nodeType === ParseNodeType.Await) {
typeResult = this._getTypeFromExpression( typeResult = this._getTypeFromExpression(
node.expression, { method: 'get' }, flags); node.expression, { method: 'get' }, flags);
typeResult = { typeResult = {
type: this.getTypeFromAwaitable(typeResult.type, node.expression), type: this.getTypeFromAwaitable(typeResult.type, node.expression),
node node
}; };
} else if (node instanceof TernaryExpressionNode) { } else if (node.nodeType === ParseNodeType.Ternary) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromTernaryExpression(node, flags); typeResult = this._getTypeFromTernaryExpression(node, flags);
} else if (node instanceof ListComprehensionNode) { } else if (node.nodeType === ParseNodeType.ListComprehension) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromListComprehensionExpression(node); typeResult = this._getTypeFromListComprehensionExpression(node);
} else if (node instanceof DictionaryNode) { } else if (node.nodeType === ParseNodeType.Dictionary) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromDictionaryExpression(node); typeResult = this._getTypeFromDictionaryExpression(node);
} else if (node instanceof LambdaNode) { } else if (node.nodeType === ParseNodeType.Lambda) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromLambdaExpression(node); typeResult = this._getTypeFromLambdaExpression(node);
} else if (node instanceof SetNode) { } else if (node.nodeType === ParseNodeType.Set) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromSetExpression(node); typeResult = this._getTypeFromSetExpression(node);
} else if (node instanceof AssignmentNode) { } else if (node.nodeType === ParseNodeType.Assignment) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
// Don't validate the type match for the assignment here. Simply // Don't validate the type match for the assignment here. Simply
// return the type result of the RHS. // return the type result of the RHS.
typeResult = this._getTypeFromExpression(node.rightExpression); typeResult = this._getTypeFromExpression(node.rightExpression);
} else if (node instanceof YieldExpressionNode) { } else if (node.nodeType === ParseNodeType.Yield) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromYieldExpression(node); typeResult = this._getTypeFromYieldExpression(node);
} else if (node instanceof YieldFromExpressionNode) { } else if (node.nodeType === ParseNodeType.YieldFrom) {
this._reportUsageErrorForReadOnly(node, usage); this._reportUsageErrorForReadOnly(node, usage);
typeResult = this._getTypeFromYieldFromExpression(node); typeResult = this._getTypeFromYieldFromExpression(node);
} else if (node instanceof UnpackExpressionNode) { } else if (node.nodeType === ParseNodeType.Unpack) {
const iterType = this._getTypeFromExpression(node.expression, usage).type; const iterType = this._getTypeFromExpression(node.expression, usage).type;
const type = this.getTypeFromIterable(iterType, false, node, false); const type = this.getTypeFromIterable(iterType, false, node, false);
typeResult = { type, node }; typeResult = { type, node };
} else if (node instanceof TypeAnnotationExpressionNode) { } else if (node.nodeType === ParseNodeType.TypeAnnotation) {
typeResult = this._getTypeFromExpression(node.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 // Evaluate the child expression as best we can so the
// type information is cached for the completion handler. // type information is cached for the completion handler.
this._silenceDiagnostics(() => { this._silenceDiagnostics(() => {
@ -971,7 +968,7 @@ export class ExpressionEvaluator {
return undefined; return undefined;
} }
private _getTypeFromClassMemberName(errorNode: ParseNode, classType: ClassType, memberName: string, private _getTypeFromClassMemberName(errorNode: ExpressionNode, classType: ClassType, memberName: string,
usage: EvaluatorUsage, flags: MemberAccessFlags): ClassMemberLookup | undefined { usage: EvaluatorUsage, flags: MemberAccessFlags): ClassMemberLookup | undefined {
// If this is a special type (like "List") that has an alias // If this is a special type (like "List") that has an alias
@ -1270,8 +1267,8 @@ export class ExpressionEvaluator {
baseTypeClass.getClassName() === 'Tuple' && baseTypeClass.getClassName() === 'Tuple' &&
baseTypeClass.getTypeArguments()) { baseTypeClass.getTypeArguments()) {
if (node.items.items[0] instanceof NumberNode) { if (node.items.items[0].nodeType === ParseNodeType.Number) {
const numberToken = (node.items.items[0] as NumberNode).token; const numberToken = node.items.items[0].token;
const baseClassTypeArgs = baseTypeClass.getTypeArguments()!; const baseClassTypeArgs = baseTypeClass.getTypeArguments()!;
if (numberToken.isInteger && numberToken.value >= 0 && if (numberToken.isInteger && numberToken.value >= 0 &&
@ -1324,7 +1321,7 @@ export class ExpressionEvaluator {
private _getTypeArg(node: ExpressionNode): TypeResult { private _getTypeArg(node: ExpressionNode): TypeResult {
let typeResult: TypeResult; let typeResult: TypeResult;
if (node instanceof ListNode) { if (node.nodeType === ParseNodeType.List) {
typeResult = { typeResult = {
type: UnknownType.create(), type: UnknownType.create(),
typeList: node.entries.map(entry => this._getTypeFromExpression(entry)), typeList: node.entries.map(entry => this._getTypeFromExpression(entry)),
@ -1361,7 +1358,7 @@ export class ExpressionEvaluator {
{ method: 'get' }, EvaluatorFlags.DoNotSpecialize); { method: 'get' }, EvaluatorFlags.DoNotSpecialize);
// Handle the built-in "super" call specially. // 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 { return {
type: this._getTypeFromSuperCall(node), type: this._getTypeFromSuperCall(node),
node node
@ -1447,8 +1444,9 @@ export class ExpressionEvaluator {
// Python docs indicate that super() isn't valid for // Python docs indicate that super() isn't valid for
// operations other than member accesses. // operations other than member accesses.
if (node.parent! instanceof MemberAccessExpressionNode) { const parentNode = node.parent!;
const memberName = node.parent.memberName.nameToken.value; if (parentNode.nodeType === ParseNodeType.MemberAccess) {
const memberName = parentNode.memberName.nameToken.value;
const lookupResults = TypeUtils.lookUpClassMember( const lookupResults = TypeUtils.lookUpClassMember(
targetClassType, memberName, ClassMemberLookupFlags.SkipOriginalClass); targetClassType, memberName, ClassMemberLookupFlags.SkipOriginalClass);
if (lookupResults && lookupResults.classType instanceof ClassType) { if (lookupResults && lookupResults.classType instanceof ClassType) {
@ -2052,8 +2050,8 @@ export class ExpressionEvaluator {
} }
const firstArg = argList[0]; const firstArg = argList[0];
if (firstArg.valueExpression instanceof StringListNode) { if (firstArg.valueExpression && firstArg.valueExpression.nodeType === ParseNodeType.StringList) {
typeVarName = firstArg.valueExpression.getValue(); typeVarName = firstArg.valueExpression.strings.map(s => s.value).join('');
} else { } else {
this._addError('Expected name of type var as first parameter', this._addError('Expected name of type var as first parameter',
firstArg.valueExpression || errorNode); firstArg.valueExpression || errorNode);
@ -2134,7 +2132,7 @@ export class ExpressionEvaluator {
} }
private _getBooleanValue(node: ExpressionNode): boolean { private _getBooleanValue(node: ExpressionNode): boolean {
if (node instanceof ConstantNode) { if (node.nodeType === ParseNodeType.Constant) {
if (node.token instanceof KeywordToken) { if (node.token instanceof KeywordToken) {
if (node.token.keywordType === KeywordType.False) { if (node.token.keywordType === KeywordType.False) {
return false; return false;
@ -2160,8 +2158,8 @@ export class ExpressionEvaluator {
if (nameArg.argumentCategory !== ArgumentCategory.Simple) { if (nameArg.argumentCategory !== ArgumentCategory.Simple) {
this._addError('Expected enum class name as first parameter', this._addError('Expected enum class name as first parameter',
argList[0].valueExpression || errorNode); argList[0].valueExpression || errorNode);
} else if (nameArg.valueExpression instanceof StringListNode) { } else if (nameArg.valueExpression && nameArg.valueExpression.nodeType === ParseNodeType.StringList) {
className = nameArg.valueExpression.getValue(); className = nameArg.valueExpression.strings.map(s => s.value).join('');
} }
} }
@ -2192,11 +2190,12 @@ export class ExpressionEvaluator {
} else { } else {
const entriesArg = argList[1]; const entriesArg = argList[1];
if (entriesArg.argumentCategory !== ArgumentCategory.Simple || 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); this._addError('Expected enum item string as second parameter', errorNode);
} else { } else {
const entries = entriesArg.valueExpression.getValue().split(' '); const entries = entriesArg.valueExpression.strings.map(s => s.value).join('').split(' ');
entries.forEach(entryName => { entries.forEach(entryName => {
entryName = entryName.trim(); entryName = entryName.trim();
if (entryName) { if (entryName) {
@ -2215,7 +2214,8 @@ export class ExpressionEvaluator {
path: this._fileInfo.filePath, path: this._fileInfo.filePath,
declaredType: entryType, declaredType: entryType,
range: convertOffsetsToRange( range: convertOffsetsToRange(
stringNode.start, stringNode.end, this._fileInfo.lines) stringNode.start, TextRange.getEnd(stringNode),
this._fileInfo.lines)
}; };
newSymbol.addDeclaration(declaration); newSymbol.addDeclaration(declaration);
setSymbolPreservingAccess(classFields, entryName, newSymbol); setSymbolPreservingAccess(classFields, entryName, newSymbol);
@ -2237,8 +2237,8 @@ export class ExpressionEvaluator {
if (argList.length >= 1) { if (argList.length >= 1) {
const nameArg = argList[0]; const nameArg = argList[0];
if (nameArg.argumentCategory === ArgumentCategory.Simple) { if (nameArg.argumentCategory === ArgumentCategory.Simple) {
if (nameArg.valueExpression instanceof StringListNode) { if (nameArg.valueExpression && nameArg.valueExpression.nodeType === ParseNodeType.StringList) {
className = nameArg.valueExpression.getValue(); className = nameArg.valueExpression.strings.map(s => s.value).join('');
} }
} }
} }
@ -2286,8 +2286,8 @@ export class ExpressionEvaluator {
if (nameArg.argumentCategory !== ArgumentCategory.Simple) { if (nameArg.argumentCategory !== ArgumentCategory.Simple) {
this._addError('Expected named tuple class name as first parameter', this._addError('Expected named tuple class name as first parameter',
argList[0].valueExpression || errorNode); argList[0].valueExpression || errorNode);
} else if (nameArg.valueExpression instanceof StringListNode) { } else if (nameArg.valueExpression && nameArg.valueExpression.nodeType === ParseNodeType.StringList) {
className = nameArg.valueExpression.getValue(); className = nameArg.valueExpression.strings.map(s => s.value).join('');
} }
} }
@ -2342,8 +2342,10 @@ export class ExpressionEvaluator {
if (entriesArg.argumentCategory !== ArgumentCategory.Simple) { if (entriesArg.argumentCategory !== ArgumentCategory.Simple) {
addGenericGetAttribute = true; addGenericGetAttribute = true;
} else { } else {
if (!includesTypes && entriesArg.valueExpression instanceof StringListNode) { if (!includesTypes && entriesArg.valueExpression &&
const entries = entriesArg.valueExpression.getValue().split(' '); entriesArg.valueExpression.nodeType === ParseNodeType.StringList) {
const entries = entriesArg.valueExpression.strings.map(s => s.value).join('').split(' ');
entries.forEach(entryName => { entries.forEach(entryName => {
entryName = entryName.trim(); entryName = entryName.trim();
if (entryName) { if (entryName) {
@ -2368,13 +2370,13 @@ export class ExpressionEvaluator {
path: this._fileInfo.filePath, path: this._fileInfo.filePath,
declaredType: entryType, declaredType: entryType,
range: convertOffsetsToRange( range: convertOffsetsToRange(
stringNode.start, stringNode.end, this._fileInfo.lines) stringNode.start, TextRange.getEnd(stringNode), this._fileInfo.lines)
}; };
newSymbol.addDeclaration(declaration); newSymbol.addDeclaration(declaration);
setSymbolPreservingAccess(instanceFields, entryName, newSymbol); setSymbolPreservingAccess(instanceFields, entryName, newSymbol);
} }
}); });
} else if (entriesArg.valueExpression instanceof ListNode) { } else if (entriesArg.valueExpression && entriesArg.valueExpression.nodeType === ParseNodeType.List) {
const entryList = entriesArg.valueExpression; const entryList = entriesArg.valueExpression;
const entryMap: { [name: string]: string } = {}; const entryMap: { [name: string]: string } = {};
@ -2385,7 +2387,7 @@ export class ExpressionEvaluator {
if (includesTypes) { if (includesTypes) {
// Handle the variant that includes name/type tuples. // 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]; entryNameNode = entry.expressions[0];
const entryTypeInfo = this._getTypeFromExpression(entry.expressions[1]); const entryTypeInfo = this._getTypeFromExpression(entry.expressions[1]);
if (entryTypeInfo) { if (entryTypeInfo) {
@ -2400,8 +2402,8 @@ export class ExpressionEvaluator {
entryType = UnknownType.create(); entryType = UnknownType.create();
} }
if (entryNameNode instanceof StringListNode) { if (entryNameNode && entryNameNode.nodeType === ParseNodeType.StringList) {
entryName = entryNameNode.getValue(); entryName = entryNameNode.strings.map(s => s.value).join('');
if (!entryName) { if (!entryName) {
this._addError( this._addError(
'Names within a named tuple cannot be empty', entryNameNode); 'Names within a named tuple cannot be empty', entryNameNode);
@ -2443,7 +2445,8 @@ export class ExpressionEvaluator {
path: this._fileInfo.filePath, path: this._fileInfo.filePath,
declaredType: entryType, declaredType: entryType,
range: convertOffsetsToRange( range: convertOffsetsToRange(
entryNameNode.start, entryNameNode.end, this._fileInfo.lines) entryNameNode.start, TextRange.getEnd(entryNameNode),
this._fileInfo.lines)
}; };
newSymbol.addDeclaration(declaration); newSymbol.addDeclaration(declaration);
} }
@ -2878,7 +2881,7 @@ export class ExpressionEvaluator {
// Infer the set type based on the entries. // Infer the set type based on the entries.
node.entries.forEach(entryNode => { node.entries.forEach(entryNode => {
if (entryNode instanceof ListComprehensionNode) { if (entryNode.nodeType === ParseNodeType.ListComprehension) {
const setEntryType = this._getElementTypeFromListComprehensionExpression(entryNode); const setEntryType = this._getElementTypeFromListComprehensionExpression(entryNode);
entryTypes.push(setEntryType); entryTypes.push(setEntryType);
} else { } else {
@ -2906,15 +2909,14 @@ export class ExpressionEvaluator {
node.entries.forEach(entryNode => { node.entries.forEach(entryNode => {
let addUnknown = true; let addUnknown = true;
if (entryNode instanceof DictionaryKeyEntryNode) { if (entryNode.nodeType === ParseNodeType.DictionaryKeyEntry) {
keyTypes.push(TypeUtils.stripLiteralValue( keyTypes.push(TypeUtils.stripLiteralValue(
this.getType(entryNode.keyExpression))); this.getType(entryNode.keyExpression)));
valueTypes.push(TypeUtils.stripLiteralValue( valueTypes.push(TypeUtils.stripLiteralValue(
this.getType(entryNode.valueExpression))); this.getType(entryNode.valueExpression)));
addUnknown = false; addUnknown = false;
} else if (entryNode instanceof DictionaryExpandEntryNode) { } else if (entryNode.nodeType === ParseNodeType.DictionaryExpandEntry) {
const unexpandedType = this.getType(entryNode.expandExpression); const unexpandedType = this.getType(entryNode.expandExpression);
if (unexpandedType.isAny()) { if (unexpandedType.isAny()) {
addUnknown = false; addUnknown = false;
@ -2936,9 +2938,9 @@ export class ExpressionEvaluator {
} }
} }
} }
} else if (entryNode instanceof ListComprehensionNode) { } else if (entryNode.nodeType === ParseNodeType.ListComprehension) {
const dictEntryType = this._getElementTypeFromListComprehensionExpression( const dictEntryType = this._getElementTypeFromListComprehensionExpression(
node.entries[0] as ListComprehensionNode<DictionaryKeyEntryNode>); node.entries[0] as ListComprehensionNode);
// The result should be a Tuple // The result should be a Tuple
if (dictEntryType instanceof ObjectType) { if (dictEntryType instanceof ObjectType) {
@ -2985,9 +2987,8 @@ export class ExpressionEvaluator {
private _getTypeFromListExpression(node: ListNode): TypeResult { private _getTypeFromListExpression(node: ListNode): TypeResult {
let listEntryType: Type = AnyType.create(); let listEntryType: Type = AnyType.create();
if (node.entries.length === 1 && node.entries[0] instanceof ListComprehensionNode) { if (node.entries.length === 1 && node.entries[0].nodeType === ParseNodeType.ListComprehension) {
listEntryType = this._getElementTypeFromListComprehensionExpression( listEntryType = this._getElementTypeFromListComprehensionExpression(node.entries[0]);
node.entries[0] as ListComprehensionNode<ExpressionNode>);
} else { } else {
const entryTypes = node.entries.map( const entryTypes = node.entries.map(
entry => TypeUtils.stripLiteralValue(this.getType(entry))); entry => TypeUtils.stripLiteralValue(this.getType(entry)));
@ -3106,9 +3107,9 @@ export class ExpressionEvaluator {
private _assignTypeToExpression(targetExpr: ExpressionNode, type: Type, srcExpr: ExpressionNode): boolean { private _assignTypeToExpression(targetExpr: ExpressionNode, type: Type, srcExpr: ExpressionNode): boolean {
let understoodType = true; let understoodType = true;
if (targetExpr instanceof NameNode) { if (targetExpr.nodeType === ParseNodeType.Name) {
this._assignTypeToNameNode(targetExpr, type); 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. // Initialize the array of target types, one for each target.
const targetTypes: Type[][] = new Array(targetExpr.expressions.length); const targetTypes: Type[][] = new Array(targetExpr.expressions.length);
for (let i = 0; i < targetExpr.expressions.length; i++) { 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++) { for (let i = 0; i < node.comprehensions.length; i++) {
const comprehension = node.comprehensions[i]; const comprehension = node.comprehensions[i];
if (comprehension instanceof ListComprehensionForNode) { if (comprehension.nodeType === ParseNodeType.ListComprehensionFor) {
const iterableType = TypeUtils.stripLiteralValue( const iterableType = TypeUtils.stripLiteralValue(
this.getType(comprehension.iterableExpression)); this.getType(comprehension.iterableExpression));
const itemType = this.getTypeFromIterable(iterableType, !!comprehension.isAsync, const itemType = this.getTypeFromIterable(iterableType, !!comprehension.isAsync,
@ -3197,7 +3198,7 @@ export class ExpressionEvaluator {
understoodType = false; understoodType = false;
break; break;
} }
} else if (comprehension instanceof ListComprehensionIfNode) { } else if (comprehension.nodeType === ParseNodeType.ListComprehensionIf) {
// Use the if node (if present) to create a type constraint. // Use the if node (if present) to create a type constraint.
typeConstraints = TypeConstraintBuilder.buildTypeConstraintsForConditional( typeConstraints = TypeConstraintBuilder.buildTypeConstraintsForConditional(
comprehension.testExpression, expr => TypeUtils.stripLiteralValue( comprehension.testExpression, expr => TypeUtils.stripLiteralValue(
@ -3208,7 +3209,7 @@ export class ExpressionEvaluator {
let type = UnknownType.create(); let type = UnknownType.create();
this._useExpressionTypeConstraint(typeConstraints, true, () => { this._useExpressionTypeConstraint(typeConstraints, true, () => {
if (understoodType) { if (understoodType) {
if (node.expression instanceof DictionaryKeyEntryNode) { if (node.expression.nodeType === ParseNodeType.DictionaryKeyEntry) {
// Create a tuple with the key/value types. // Create a tuple with the key/value types.
const keyType = TypeUtils.stripLiteralValue( const keyType = TypeUtils.stripLiteralValue(
this.getType(node.expression.keyExpression)); this.getType(node.expression.keyExpression));
@ -3217,12 +3218,12 @@ export class ExpressionEvaluator {
type = ScopeUtils.getBuiltInObject( type = ScopeUtils.getBuiltInObject(
this._scope, 'Tuple', [keyType, valueType]); 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); const unexpandedType = this.getType(node.expression.expandExpression);
// TODO - need to implement // TODO - need to implement
} else if (node.expression instanceof ExpressionNode) { } else if (isExpressionNode(node)) {
type = TypeUtils.stripLiteralValue(this.getType(node.expression)); type = TypeUtils.stripLiteralValue(this.getType(node.expression as ExpressionNode));
} }
} }
}); });
@ -3315,7 +3316,7 @@ export class ExpressionEvaluator {
} }
// Creates an Optional[X, Y, Z] type. // 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) { if (!typeArgs || typeArgs.length !== 1) {
this._addError(`Expected one type parameter after Optional`, errorNode); this._addError(`Expected one type parameter after Optional`, errorNode);
return UnknownType.create(); return UnknownType.create();
@ -3356,23 +3357,24 @@ export class ExpressionEvaluator {
for (const item of node.items.items) { for (const item of node.items.items) {
let type: Type | undefined; let type: Type | undefined;
if (item instanceof StringListNode) { if (item.nodeType === ParseNodeType.StringList) {
// Note that the contents of the string should not be treated // Note that the contents of the string should not be treated
// as a type annotation, as they normally are for quoted type // as a type annotation, as they normally are for quoted type
// arguments. // arguments.
AnalyzerNodeInfo.setIgnoreTypeAnnotation(item); AnalyzerNodeInfo.setIgnoreTypeAnnotation(item);
const isBytes = (item.strings[0].token.flags & StringTokenFlags.Bytes) !== 0; const isBytes = (item.strings[0].token.flags & StringTokenFlags.Bytes) !== 0;
const value = item.strings.map(s => s.value).join('');
if (isBytes) { if (isBytes) {
type = this._cloneBuiltinTypeWithLiteral('bytes', item.getValue()); type = this._cloneBuiltinTypeWithLiteral('bytes', value);
} else { } 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) { if (item.token.isInteger) {
type = this._cloneBuiltinTypeWithLiteral('int', item.token.value); type = this._cloneBuiltinTypeWithLiteral('int', item.token.value);
} }
} else if (item instanceof ConstantNode) { } else if (item.nodeType === ParseNodeType.Constant) {
if (item.token.keywordType === KeywordType.True) { if (item.token.keywordType === KeywordType.True) {
type = this._cloneBuiltinTypeWithLiteral('bool', true); type = this._cloneBuiltinTypeWithLiteral('bool', true);
} else if (item.token.keywordType === KeywordType.False) { } else if (item.token.keywordType === KeywordType.False) {
@ -3393,7 +3395,7 @@ export class ExpressionEvaluator {
} }
// Creates a ClassVar type. // 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) { if (!typeArgs || typeArgs.length === 0) {
this._addError(`Expected a type parameter after ClassVar`, errorNode); this._addError(`Expected a type parameter after ClassVar`, errorNode);
return UnknownType.create(); return UnknownType.create();
@ -3481,7 +3483,7 @@ export class ExpressionEvaluator {
// Creates a type that represents "Generic[T1, T2, ...]", used in the // Creates a type that represents "Generic[T1, T2, ...]", used in the
// definition of a generic class. // definition of a generic class.
private _createGenericType(errorNode: ExpressionNode, classType: ClassType, private _createGenericType(errorNode: ParseNode, classType: ClassType,
typeArgs?: TypeResult[]): Type { typeArgs?: TypeResult[]): Type {
// Make sure there's at least one type arg. // 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 // Returns the specialized type and a boolean indicating whether
// the type indicates a class type (true) or an object type (false). // the type indicates a class type (true) or an object type (false).
private _createSpecializeClassType(classType: ClassType, typeArgs: TypeResult[] | undefined, private _createSpecializeClassType(classType: ClassType, typeArgs: TypeResult[] | undefined,
errorNode: ExpressionNode): Type { errorNode: ParseNode): Type {
// Handle the special-case classes that are not defined // Handle the special-case classes that are not defined
// in the type stubs. // in the type stubs.

View File

@ -9,9 +9,7 @@
*/ */
import { ExecutionEnvironment } from '../common/configOptions'; import { ExecutionEnvironment } from '../common/configOptions';
import { BinaryExpressionNode, ConstantNode, ExpressionNode, IndexExpressionNode, import { ExpressionNode, NumberNode, ParseNodeType, TupleExpressionNode } from '../parser/parseNodes';
MemberAccessExpressionNode, NameNode, NumberNode, StringListNode,
TupleExpressionNode } from '../parser/parseNodes';
import { KeywordType, OperatorType } from '../parser/tokenizerTypes'; import { KeywordType, OperatorType } from '../parser/tokenizerTypes';
export class ExpressionUtils { export class ExpressionUtils {
@ -20,50 +18,50 @@ export class ExpressionUtils {
static evaluateConstantExpression(node: ExpressionNode, static evaluateConstantExpression(node: ExpressionNode,
execEnv: ExecutionEnvironment): boolean | undefined { execEnv: ExecutionEnvironment): boolean | undefined {
if (node instanceof BinaryExpressionNode) { if (node.nodeType === ParseNodeType.BinaryOperation) {
if (this._isSysVersionInfoExpression(node.leftExpression) && if (this._isSysVersionInfoExpression(node.leftExpression) &&
node.rightExpression instanceof TupleExpressionNode) { node.rightExpression.nodeType === ParseNodeType.Tuple) {
// Handle the special case of "sys.version_info >= (3, x)" // Handle the special case of "sys.version_info >= (3, x)"
const comparisonVersion = this._convertTupleToVersion(node.rightExpression); const comparisonVersion = this._convertTupleToVersion(node.rightExpression);
return this._evaluateNumericBinaryOperation(node.operator, return this._evaluateNumericBinaryOperation(node.operator,
execEnv.pythonVersion, comparisonVersion); execEnv.pythonVersion, comparisonVersion);
} else if (node.leftExpression instanceof IndexExpressionNode && } else if (node.leftExpression.nodeType === ParseNodeType.Index &&
this._isSysVersionInfoExpression(node.leftExpression.baseExpression) && this._isSysVersionInfoExpression(node.leftExpression.baseExpression) &&
node.leftExpression.items.items.length === 1 && node.leftExpression.items.items.length === 1 &&
node.leftExpression.items.items[0] instanceof NumberNode && node.leftExpression.items.items[0].nodeType === ParseNodeType.Number &&
(node.leftExpression.items.items[0] as NumberNode).token.value === 0 && node.leftExpression.items.items[0].token.value === 0 &&
node.rightExpression instanceof NumberNode) { node.rightExpression.nodeType === ParseNodeType.Number) {
// Handle the special case of "sys.version_info[0] >= X" // Handle the special case of "sys.version_info[0] >= X"
return this._evaluateNumericBinaryOperation(node.operator, return this._evaluateNumericBinaryOperation(node.operator,
Math.floor(execEnv.pythonVersion / 256), node.rightExpression.token.value); Math.floor(execEnv.pythonVersion / 256), node.rightExpression.token.value);
} else if (this._isSysPlatformInfoExpression(node.leftExpression) && } else if (this._isSysPlatformInfoExpression(node.leftExpression) &&
node.rightExpression instanceof StringListNode) { node.rightExpression.nodeType === ParseNodeType.StringList) {
// Handle the special case of "sys.platform != 'X'" // 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) { if (execEnv.pythonPlatform !== undefined) {
return this._evaluateStringBinaryOperation(node.operator, return this._evaluateStringBinaryOperation(node.operator,
execEnv.pythonPlatform, comparisonPlatform); execEnv.pythonPlatform, comparisonPlatform);
} }
} else if (this._isOsNameInfoExpression(node.leftExpression) && } else if (this._isOsNameInfoExpression(node.leftExpression) &&
node.rightExpression instanceof StringListNode) { node.rightExpression.nodeType === ParseNodeType.StringList) {
// Handle the special case of "os.name == 'X'" // 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); const expectedOsName = this._getExpectedOsNameFromPlatform(execEnv);
if (expectedOsName !== undefined) { if (expectedOsName !== undefined) {
return this._evaluateStringBinaryOperation(node.operator, return this._evaluateStringBinaryOperation(node.operator,
expectedOsName, comparisonOsName); expectedOsName, comparisonOsName);
} }
} }
} else if (node instanceof ConstantNode) { } else if (node.nodeType === ParseNodeType.Constant) {
if (node.token.keywordType === KeywordType.True) { if (node.token.keywordType === KeywordType.True) {
return true; return true;
} else if (node.token.keywordType === KeywordType.False) { } else if (node.token.keywordType === KeywordType.False) {
return false; return false;
} }
} else if (node instanceof NameNode) { } else if (node.nodeType === ParseNodeType.Name) {
if (node.nameToken.value === 'TYPE_CHECKING') { if (node.nameToken.value === 'TYPE_CHECKING') {
return true; return true;
} }
@ -75,10 +73,10 @@ export class ExpressionUtils {
private static _convertTupleToVersion(node: TupleExpressionNode): number | undefined { private static _convertTupleToVersion(node: TupleExpressionNode): number | undefined {
let comparisonVersion: number | undefined; let comparisonVersion: number | undefined;
if (node.expressions.length === 2) { if (node.expressions.length === 2) {
if (node.expressions[0] instanceof NumberNode && if (node.expressions[0].nodeType === ParseNodeType.Number &&
node.expressions[1] instanceof NumberNode) { node.expressions[1].nodeType === ParseNodeType.Number) {
const majorVersion = node.expressions[0] as NumberNode; const majorVersion = node.expressions[0];
const minorVersion = node.expressions[1] as NumberNode; const minorVersion = node.expressions[1];
comparisonVersion = majorVersion.token.value * 256 + minorVersion.token.value; comparisonVersion = majorVersion.token.value * 256 + minorVersion.token.value;
} }
} else if (node.expressions.length === 1) { } else if (node.expressions.length === 1) {
@ -124,8 +122,8 @@ export class ExpressionUtils {
} }
private static _isSysVersionInfoExpression(node: ExpressionNode): boolean { private static _isSysVersionInfoExpression(node: ExpressionNode): boolean {
if (node instanceof MemberAccessExpressionNode) { if (node.nodeType === ParseNodeType.MemberAccess) {
if (node.leftExpression instanceof NameNode && if (node.leftExpression.nodeType === ParseNodeType.Name &&
node.leftExpression.nameToken.value === 'sys' && node.leftExpression.nameToken.value === 'sys' &&
node.memberName.nameToken.value === 'version_info') { node.memberName.nameToken.value === 'version_info') {
return true; return true;
@ -136,8 +134,8 @@ export class ExpressionUtils {
} }
private static _isSysPlatformInfoExpression(node: ExpressionNode): boolean { private static _isSysPlatformInfoExpression(node: ExpressionNode): boolean {
if (node instanceof MemberAccessExpressionNode) { if (node.nodeType === ParseNodeType.MemberAccess) {
if (node.leftExpression instanceof NameNode && if (node.leftExpression.nodeType === ParseNodeType.Name &&
node.leftExpression.nameToken.value === 'sys' && node.leftExpression.nameToken.value === 'sys' &&
node.memberName.nameToken.value === 'platform') { node.memberName.nameToken.value === 'platform') {
return true; return true;
@ -148,8 +146,8 @@ export class ExpressionUtils {
} }
private static _isOsNameInfoExpression(node: ExpressionNode): boolean { private static _isOsNameInfoExpression(node: ExpressionNode): boolean {
if (node instanceof MemberAccessExpressionNode) { if (node.nodeType === ParseNodeType.MemberAccess) {
if (node.leftExpression instanceof NameNode && if (node.leftExpression.nodeType === ParseNodeType.Name &&
node.leftExpression.nameToken.value === 'os' && node.leftExpression.nameToken.value === 'os' &&
node.memberName.nameToken.value === 'name') { node.memberName.nameToken.value === 'name') {
return true; return true;

View File

@ -11,9 +11,9 @@
import { DiagnosticTextPosition } from '../common/diagnostic'; import { DiagnosticTextPosition } from '../common/diagnostic';
import { TextEditAction } from '../common/editAction'; import { TextEditAction } from '../common/editAction';
import { convertOffsetToPosition } from '../common/positionUtils'; import { convertOffsetToPosition } from '../common/positionUtils';
import { AssignmentNode, ImportAsNode, ImportFromAsNode, ImportFromNode, import { TextRange } from '../common/textRange';
ImportNode, ModuleNameNode, ModuleNode, NameNode, StatementListNode, import { ImportAsNode, ImportFromAsNode, ImportFromNode, ImportNode,
StringListNode } from '../parser/parseNodes'; ModuleNameNode, ModuleNode, ParseNodeType } from '../parser/parseNodes';
import { ParseResults } from '../parser/parser'; import { ParseResults } from '../parser/parser';
import { AnalyzerNodeInfo } from './analyzerNodeInfo'; import { AnalyzerNodeInfo } from './analyzerNodeInfo';
import { ImportResult, ImportType } from './importResult'; import { ImportResult, ImportType } from './importResult';
@ -46,13 +46,13 @@ export class ImportStatementUtils {
let foundFirstImportStatement = false; let foundFirstImportStatement = false;
parseTree.statements.forEach(statement => { parseTree.statements.forEach(statement => {
if (statement instanceof StatementListNode) { if (statement.nodeType === ParseNodeType.StatementList) {
statement.statements.forEach(subStatement => { statement.statements.forEach(subStatement => {
if (subStatement instanceof ImportNode) { if (subStatement.nodeType === ParseNodeType.Import) {
foundFirstImportStatement = true; foundFirstImportStatement = true;
this._processImportNode(subStatement, localImports, followsNonImportStatement); this._processImportNode(subStatement, localImports, followsNonImportStatement);
followsNonImportStatement = false; followsNonImportStatement = false;
} else if (subStatement instanceof ImportFromNode) { } else if (subStatement.nodeType === ParseNodeType.ImportFrom) {
foundFirstImportStatement = true; foundFirstImportStatement = true;
this._processImportFromNode(subStatement, localImports, followsNonImportStatement); this._processImportFromNode(subStatement, localImports, followsNonImportStatement);
followsNonImportStatement = false; followsNonImportStatement = false;
@ -77,7 +77,7 @@ export class ImportStatementUtils {
// assuming we want to keep the imports alphebetized. // assuming we want to keep the imports alphebetized.
let priorImport: ImportFromAsNode | undefined; let priorImport: ImportFromAsNode | undefined;
if (importStatement.node instanceof ImportFromNode) { if (importStatement.node && importStatement.node.nodeType === ParseNodeType.ImportFrom) {
for (const curImport of importStatement.node.imports) { for (const curImport of importStatement.node.imports) {
if (priorImport && curImport.name.nameToken.value > symbolName) { if (priorImport && curImport.name.nameToken.value > symbolName) {
break; break;
@ -87,7 +87,7 @@ export class ImportStatementUtils {
} }
if (priorImport) { if (priorImport) {
const insertionOffset = priorImport.name.end; const insertionOffset = TextRange.getEnd(priorImport.name);
const insertionPosition = convertOffsetToPosition(insertionOffset, parseResults.lines); const insertionPosition = convertOffsetToPosition(insertionOffset, parseResults.lines);
textEditList.push({ textEditList.push({
@ -173,7 +173,7 @@ export class ImportStatementUtils {
} }
insertionPosition = convertOffsetToPosition( insertionPosition = convertOffsetToPosition(
insertBefore ? insertionImport.node.start : insertionImport.node.end, insertBefore ? insertionImport.node.start : TextRange.getEnd(insertionImport.node),
parseResults.lines); parseResults.lines);
} else { } else {
insertionPosition = { line: 0, column: 0 }; insertionPosition = { line: 0, column: 0 };
@ -186,14 +186,14 @@ export class ImportStatementUtils {
for (const statement of parseResults.parseTree.statements) { for (const statement of parseResults.parseTree.statements) {
let stopHere = true; 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]; const simpleStatement = statement.statements[0];
if (simpleStatement instanceof StringListNode) { if (simpleStatement.nodeType === ParseNodeType.StringList) {
// Assume that it's a file header doc string. // Assume that it's a file header doc string.
stopHere = false; stopHere = false;
} else if (simpleStatement instanceof AssignmentNode) { } else if (simpleStatement.nodeType === ParseNodeType.Assignment) {
if (simpleStatement.leftExpression instanceof NameNode) { if (simpleStatement.leftExpression.nodeType === ParseNodeType.Name) {
if (SymbolUtils.isDunderName(simpleStatement.leftExpression.nameToken.value)) { if (SymbolUtils.isDunderName(simpleStatement.leftExpression.nameToken.value)) {
// Assume that it's an assignment of __copyright__, __author__, etc. // Assume that it's an assignment of __copyright__, __author__, etc.
stopHere = false; stopHere = false;
@ -208,7 +208,8 @@ export class ImportStatementUtils {
addNewLineBefore = false; addNewLineBefore = false;
break; break;
} else { } else {
insertionPosition = convertOffsetToPosition(statement.end, insertionPosition = convertOffsetToPosition(
statement.start + statement.length,
parseResults.lines); parseResults.lines);
addNewLineBefore = true; addNewLineBefore = true;
} }
@ -292,7 +293,9 @@ export class ImportStatementUtils {
// Overwrite existing import statements because we always want to prefer // Overwrite existing import statements because we always want to prefer
// 'import from' over 'import'. Also, overwrite existing 'import from' if // 'import from' over 'import'. Also, overwrite existing 'import from' if
// the module name is shorter. // 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; localImports.mapByFilePath[resolvedPath] = localImport;
} }
} }

View File

@ -28,8 +28,8 @@ export class ParseTreeCleanerWalker extends ParseTreeWalker {
this.walk(this._parseTree); this.walk(this._parseTree);
} }
visitNode(node: ParseNode): boolean { visitNode(node: ParseNode) {
AnalyzerNodeInfo.cleanNodeAnalysisInfo(node); AnalyzerNodeInfo.cleanNodeAnalysisInfo(node);
return true; return super.visitNode(node);
} }
} }

View File

@ -11,17 +11,10 @@ import { DiagnosticTextPosition } from '../common/diagnostic';
import { convertPositionToOffset } from '../common/positionUtils'; import { convertPositionToOffset } from '../common/positionUtils';
import { TextRange } from '../common/textRange'; import { TextRange } from '../common/textRange';
import { TextRangeCollection } from '../common/textRangeCollection'; import { TextRangeCollection } from '../common/textRangeCollection';
import { ArgumentCategory, AssignmentNode, AugmentedAssignmentExpressionNode, import { ArgumentCategory, ClassNode, ExpressionNode, FunctionNode, isExpressionNode,
AwaitExpressionNode, BinaryExpressionNode, CallExpressionNode, ClassNode, ModuleNode, ParameterCategory, ParseNode, ParseNodeType } from '../parser/parseNodes';
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 { KeywordType, OperatorType, StringTokenFlags } from '../parser/tokenizerTypes'; import { KeywordType, OperatorType, StringTokenFlags } from '../parser/tokenizerTypes';
import { ParseTreeWalker } from './parseTreeWalker';
export enum PrintExpressionFlags { export enum PrintExpressionFlags {
None = 0, None = 0,
@ -57,13 +50,15 @@ export class ParseTreeUtils {
// Returns the deepest node that contains the specified offset. // Returns the deepest node that contains the specified offset.
static findNodeByOffset(node: ParseNode, offset: number): ParseNode | undefined { 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; return undefined;
} }
const parseTreeWalker = new ParseTreeWalker();
// The range is found within this node. See if we can localize it // The range is found within this node. See if we can localize it
// further by checking its children. // further by checking its children.
const children = node.getChildren(); const children = parseTreeWalker.visitNode(node);
for (const child of children) { for (const child of children) {
if (child) { if (child) {
const containingChild = ParseTreeUtils.findNodeByOffset(child, offset); const containingChild = ParseTreeUtils.findNodeByOffset(child, offset);
@ -77,12 +72,12 @@ export class ParseTreeUtils {
} }
static printExpression(node: ExpressionNode, flags = PrintExpressionFlags.None): string { static printExpression(node: ExpressionNode, flags = PrintExpressionFlags.None): string {
if (node instanceof NameNode) { if (node.nodeType === ParseNodeType.Name) {
return node.nameToken.value; return node.nameToken.value;
} else if (node instanceof MemberAccessExpressionNode) { } else if (node.nodeType === ParseNodeType.MemberAccess) {
return ParseTreeUtils.printExpression(node.leftExpression, flags) + '.' + return ParseTreeUtils.printExpression(node.leftExpression, flags) + '.' +
node.memberName.nameToken.value; node.memberName.nameToken.value;
} else if (node instanceof CallExpressionNode) { } else if (node.nodeType === ParseNodeType.Call) {
return ParseTreeUtils.printExpression(node.leftExpression, flags) + '(' + return ParseTreeUtils.printExpression(node.leftExpression, flags) + '(' +
node.arguments.map(arg => { node.arguments.map(arg => {
let argStr = ''; let argStr = '';
@ -98,20 +93,20 @@ export class ParseTreeUtils {
return argStr; return argStr;
}).join(', ') + }).join(', ') +
')'; ')';
} else if (node instanceof IndexExpressionNode) { } else if (node.nodeType === ParseNodeType.Index) {
return ParseTreeUtils.printExpression(node.baseExpression, flags) + '[' + return ParseTreeUtils.printExpression(node.baseExpression, flags) + '[' +
node.items.items.map(item => this.printExpression(item, flags)).join(', ') + 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) + ' ' + return ParseTreeUtils.printOperator(node.operator) + ' ' +
ParseTreeUtils.printExpression(node.expression, flags); ParseTreeUtils.printExpression(node.expression, flags);
} else if (node instanceof BinaryExpressionNode) { } else if (node.nodeType === ParseNodeType.BinaryOperation) {
return ParseTreeUtils.printExpression(node.leftExpression, flags) + ' ' + return ParseTreeUtils.printExpression(node.leftExpression, flags) + ' ' +
ParseTreeUtils.printOperator(node.operator) + ' ' + ParseTreeUtils.printOperator(node.operator) + ' ' +
ParseTreeUtils.printExpression(node.rightExpression, flags); ParseTreeUtils.printExpression(node.rightExpression, flags);
} else if (node instanceof NumberNode) { } else if (node.nodeType === ParseNodeType.Number) {
return node.token.value.toString(); return node.token.value.toString();
} else if (node instanceof StringListNode) { } else if (node.nodeType === ParseNodeType.StringList) {
if ((flags & PrintExpressionFlags.ForwardDeclarations) && node.typeAnnotation) { if ((flags & PrintExpressionFlags.ForwardDeclarations) && node.typeAnnotation) {
return ParseTreeUtils.printExpression(node.typeAnnotation, flags); return ParseTreeUtils.printExpression(node.typeAnnotation, flags);
} else { } else {
@ -119,7 +114,7 @@ export class ParseTreeUtils {
return ParseTreeUtils.printExpression(str, flags); return ParseTreeUtils.printExpression(str, flags);
}).join(' '); }).join(' ');
} }
} else if (node instanceof StringNode) { } else if (node.nodeType === ParseNodeType.String) {
let exprString = ''; let exprString = '';
if (node.token.flags & StringTokenFlags.Raw) { if (node.token.flags & StringTokenFlags.Raw) {
exprString += 'r'; exprString += 'r';
@ -152,30 +147,30 @@ export class ParseTreeUtils {
} }
return exprString; return exprString;
} else if (node instanceof AssignmentNode) { } else if (node.nodeType === ParseNodeType.Assignment) {
return ParseTreeUtils.printExpression(node.leftExpression, flags) + ' = ' + return ParseTreeUtils.printExpression(node.leftExpression, flags) + ' = ' +
ParseTreeUtils.printExpression(node.rightExpression, flags); ParseTreeUtils.printExpression(node.rightExpression, flags);
} else if (node instanceof TypeAnnotationExpressionNode) { } else if (node.nodeType === ParseNodeType.TypeAnnotation) {
return ParseTreeUtils.printExpression(node.valueExpression, flags) + ': ' + return ParseTreeUtils.printExpression(node.valueExpression, flags) + ': ' +
ParseTreeUtils.printExpression(node.typeAnnotation, flags); ParseTreeUtils.printExpression(node.typeAnnotation, flags);
} else if (node instanceof AugmentedAssignmentExpressionNode) { } else if (node.nodeType === ParseNodeType.AugmentedAssignment) {
return ParseTreeUtils.printExpression(node.leftExpression, flags) + ' ' + return ParseTreeUtils.printExpression(node.leftExpression, flags) + ' ' +
ParseTreeUtils.printOperator(node.operator) + ' ' + ParseTreeUtils.printOperator(node.operator) + ' ' +
ParseTreeUtils.printExpression(node.rightExpression, flags); ParseTreeUtils.printExpression(node.rightExpression, flags);
} else if (node instanceof AwaitExpressionNode) { } else if (node.nodeType === ParseNodeType.Await) {
return 'await ' + ParseTreeUtils.printExpression(node.expression, flags); 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 ' + return ParseTreeUtils.printExpression(node.ifExpression, flags) + ' if ' +
ParseTreeUtils.printExpression(node.testExpression, flags) + ' else ' + ParseTreeUtils.printExpression(node.testExpression, flags) + ' else ' +
ParseTreeUtils.printExpression(node.elseExpression, flags); ParseTreeUtils.printExpression(node.elseExpression, flags);
} else if (node instanceof ListNode) { } else if (node.nodeType === ParseNodeType.List) {
const expressions = node.entries.map(expr => { const expressions = node.entries.map(expr => {
return ParseTreeUtils.printExpression(expr, flags); return ParseTreeUtils.printExpression(expr, flags);
}); });
return `[${ expressions.join(', ') }]`; return `[${ expressions.join(', ') }]`;
} else if (node instanceof UnpackExpressionNode) { } else if (node.nodeType === ParseNodeType.Unpack) {
return '*' + ParseTreeUtils.printExpression(node.expression, flags); return '*' + ParseTreeUtils.printExpression(node.expression, flags);
} else if (node instanceof TupleExpressionNode) { } else if (node.nodeType === ParseNodeType.Tuple) {
const expressions = node.expressions.map(expr => { const expressions = node.expressions.map(expr => {
return ParseTreeUtils.printExpression(expr, flags); return ParseTreeUtils.printExpression(expr, flags);
}); });
@ -183,18 +178,18 @@ export class ParseTreeUtils {
return `(${ expressions[0] }, )`; return `(${ expressions[0] }, )`;
} }
return `(${ expressions.join(', ') })`; return `(${ expressions.join(', ') })`;
} else if (node instanceof YieldExpressionNode) { } else if (node.nodeType === ParseNodeType.Yield) {
return 'yield ' + ParseTreeUtils.printExpression(node.expression, flags); 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); return 'yield from ' + ParseTreeUtils.printExpression(node.expression, flags);
} else if (node instanceof EllipsisNode) { } else if (node.nodeType === ParseNodeType.Ellipsis) {
return '...'; return '...';
} else if (node instanceof ListComprehensionNode) { } else if (node.nodeType === ParseNodeType.ListComprehension) {
let listStr = '<ListExpression>'; let listStr = '<ListExpression>';
if (node.expression instanceof ExpressionNode) { if (isExpressionNode(node.expression)) {
listStr = ParseTreeUtils.printExpression(node.expression, flags); listStr = ParseTreeUtils.printExpression(node.expression as ExpressionNode, flags);
} else if (node.expression instanceof DictionaryKeyEntryNode) { } else if (node.expression.nodeType === ParseNodeType.DictionaryKeyEntry) {
const keyStr = ParseTreeUtils.printExpression(node.expression.keyExpression, flags); const keyStr = ParseTreeUtils.printExpression(node.expression.keyExpression, flags);
const valueStr = ParseTreeUtils.printExpression(node.expression.valueExpression, flags); const valueStr = ParseTreeUtils.printExpression(node.expression.valueExpression, flags);
listStr = `${ keyStr }: ${ valueStr }`; listStr = `${ keyStr }: ${ valueStr }`;
@ -202,7 +197,7 @@ export class ParseTreeUtils {
return listStr + ' ' + return listStr + ' ' +
node.comprehensions.map(expr => { node.comprehensions.map(expr => {
if (expr instanceof ListComprehensionForNode) { if (expr.nodeType === ParseNodeType.ListComprehensionFor) {
return `${ expr.isAsync ? 'async ' : '' }for ` + return `${ expr.isAsync ? 'async ' : '' }for ` +
ParseTreeUtils.printExpression(expr.targetExpression, flags) + ParseTreeUtils.printExpression(expr.targetExpression, flags) +
` in ${ ParseTreeUtils.printExpression(expr.iterableExpression, flags) }`; ` in ${ ParseTreeUtils.printExpression(expr.iterableExpression, flags) }`;
@ -210,7 +205,7 @@ export class ParseTreeUtils {
return `if ${ ParseTreeUtils.printExpression(expr.testExpression, flags) }`; return `if ${ ParseTreeUtils.printExpression(expr.testExpression, flags) }`;
} }
}).join(' '); }).join(' ');
} else if (node instanceof SliceExpressionNode) { } else if (node.nodeType === ParseNodeType.Slice) {
let result = ''; let result = '';
if (node.startValue) { if (node.startValue) {
result += ParseTreeUtils.printExpression(node.startValue, flags); result += ParseTreeUtils.printExpression(node.startValue, flags);
@ -222,7 +217,7 @@ export class ParseTreeUtils {
result += ': ' + ParseTreeUtils.printExpression(node.stepValue, flags); result += ': ' + ParseTreeUtils.printExpression(node.stepValue, flags);
} }
return result; return result;
} else if (node instanceof LambdaNode) { } else if (node.nodeType === ParseNodeType.Lambda) {
return 'lambda ' + node.parameters.map(param => { return 'lambda ' + node.parameters.map(param => {
let paramStr = ''; let paramStr = '';
@ -241,7 +236,7 @@ export class ParseTreeUtils {
} }
return paramStr; return paramStr;
}).join(', ') + ': ' + ParseTreeUtils.printExpression(node.expression, flags); }).join(', ') + ': ' + ParseTreeUtils.printExpression(node.expression, flags);
} else if (node instanceof ConstantNode) { } else if (node.nodeType === ParseNodeType.Constant) {
if (node.token.keywordType === KeywordType.True) { if (node.token.keywordType === KeywordType.True) {
return 'True'; return 'True';
} else if (node.token.keywordType === KeywordType.False) { } else if (node.token.keywordType === KeywordType.False) {
@ -251,18 +246,18 @@ export class ParseTreeUtils {
} else if (node.token.keywordType === KeywordType.None) { } else if (node.token.keywordType === KeywordType.None) {
return 'None'; return 'None';
} }
} else if (node instanceof DictionaryNode) { } else if (node.nodeType === ParseNodeType.Dictionary) {
return `{ ${ node.entries.map(entry => { return `{ ${ node.entries.map(entry => {
if (entry instanceof DictionaryKeyEntryNode) { if (entry.nodeType === ParseNodeType.DictionaryKeyEntry) {
return `${ ParseTreeUtils.printExpression(entry.keyExpression, flags) }: ` + return `${ ParseTreeUtils.printExpression(entry.keyExpression, flags) }: ` +
`${ ParseTreeUtils.printExpression(entry.valueExpression, flags) }`; `${ ParseTreeUtils.printExpression(entry.valueExpression, flags) }`;
} else { } else {
return ParseTreeUtils.printExpression(entry, flags); return ParseTreeUtils.printExpression(entry, flags);
} }
})} }`; })} }`;
} else if (node instanceof DictionaryExpandEntryNode) { } else if (node.nodeType === ParseNodeType.DictionaryExpandEntry) {
return `**${ ParseTreeUtils.printExpression(node.expandExpression, flags) }`; 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(', '); 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 { static getEnclosingClass(node: ParseNode, stopAtFunction = false): ClassNode | undefined {
let curNode = node.parent; let curNode = node.parent;
while (curNode) { while (curNode) {
if (curNode instanceof ClassNode) { if (curNode.nodeType === ParseNodeType.Class) {
return curNode; return curNode;
} }
if (curNode instanceof ModuleNode) { if (curNode.nodeType === ParseNodeType.Module) {
return undefined; return undefined;
} }
if (curNode instanceof FunctionNode) { if (curNode.nodeType === ParseNodeType.Function) {
if (stopAtFunction) { if (stopAtFunction) {
return undefined; return undefined;
} }
@ -349,15 +344,15 @@ export class ParseTreeUtils {
let curNode = node.parent; let curNode = node.parent;
while (curNode) { while (curNode) {
if (curNode instanceof ClassNode) { if (curNode.nodeType === ParseNodeType.Class) {
return curNode; return curNode;
} }
if (curNode instanceof ModuleNode) { if (curNode.nodeType === ParseNodeType.Module) {
return curNode; return curNode;
} }
if (curNode instanceof FunctionNode) { if (curNode.nodeType === ParseNodeType.Function) {
if (stopAtFunction) { if (stopAtFunction) {
return undefined; return undefined;
} }
@ -372,11 +367,11 @@ export class ParseTreeUtils {
static getEnclosingFunction(node: ParseNode): FunctionNode | undefined { static getEnclosingFunction(node: ParseNode): FunctionNode | undefined {
let curNode = node.parent; let curNode = node.parent;
while (curNode) { while (curNode) {
if (curNode instanceof FunctionNode) { if (curNode.nodeType === ParseNodeType.Function) {
return curNode; return curNode;
} }
if (curNode instanceof ClassNode) { if (curNode.nodeType === ParseNodeType.Class) {
return undefined; return undefined;
} }

View File

@ -17,9 +17,9 @@ import { ArgumentNode, AssertNode, AssignmentNode, AugmentedAssignmentExpression
ImportFromAsNode, ImportFromNode, ImportNode, IndexExpressionNode, IndexItemsNode, ImportFromAsNode, ImportFromNode, ImportNode, IndexExpressionNode, IndexItemsNode,
LambdaNode, ListComprehensionForNode, ListComprehensionIfNode, ListComprehensionNode, LambdaNode, ListComprehensionForNode, ListComprehensionIfNode, ListComprehensionNode,
ListNode, MemberAccessExpressionNode, ModuleNameNode, ModuleNode, NameNode, NonlocalNode, ListNode, MemberAccessExpressionNode, ModuleNameNode, ModuleNode, NameNode, NonlocalNode,
NumberNode, ParameterNode, ParseNode, ParseNodeArray, ParseNodeType, PassNode, RaiseNode, NumberNode, ParameterNode, ParseNode, ParseNodeArray, ParseNodeType, PassNode,
ReturnNode, SetNode, SliceExpressionNode, StatementListNode, StringListNode, StringNode, RaiseNode, ReturnNode, SetNode, SliceExpressionNode, StatementListNode, StringListNode,
SuiteNode, TernaryExpressionNode, TryNode, TupleExpressionNode, StringNode, SuiteNode, TernaryExpressionNode, TryNode, TupleExpressionNode,
TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode, WhileNode, TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode, WhileNode,
WithItemNode, WithNode, YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes'; WithItemNode, WithNode, YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes';
@ -27,8 +27,9 @@ import { ArgumentNode, AssertNode, AssignmentNode, AugmentedAssignmentExpression
// visitXXX methods that you want to handle. // visitXXX methods that you want to handle.
export class ParseTreeWalker { export class ParseTreeWalker {
walk(node: ParseNode): void { walk(node: ParseNode): void {
if (this.visitNode(node)) { const childrenToWalk = this.visitNode(node);
this.walkChildren(node); if (childrenToWalk.length > 0) {
this.walkMultiple(childrenToWalk);
} }
} }
@ -40,207 +41,392 @@ export class ParseTreeWalker {
}); });
} }
walkChildren(node: ParseNode) { // Calls the node-specific method (visitXXXX). If the method
node.getChildren().forEach(node => { // returns true, all child nodes for the node are returned.
if (node) { // If the moethod returns false, we assume that the handler
this.walk(node); // has already handled the child nodes, so an empty list is
} // returned.
}); visitNode(node: ParseNode): ParseNodeArray {
}
visitNode(node: ParseNode): boolean {
switch (node.nodeType) { switch (node.nodeType) {
case ParseNodeType.Argument: case ParseNodeType.Argument:
return this.visitArgument(node as ArgumentNode); if (this.visitArgument(node)) {
return [node.valueExpression];
}
break;
case ParseNodeType.Assert: case ParseNodeType.Assert:
return this.visitAssert(node as AssertNode); if (this.visitAssert(node)) {
return [node.testExpression, node.exceptionExpression];
}
break;
case ParseNodeType.Assignment: case ParseNodeType.Assignment:
return this.visitAssignment(node as AssignmentNode); if (this.visitAssignment(node)) {
return [node.leftExpression, node.rightExpression, node.typeAnnotationComment];
}
break;
case ParseNodeType.AugmentedAssignment: case ParseNodeType.AugmentedAssignment:
return this.visitAugmentedAssignment(node as AugmentedAssignmentExpressionNode); if (this.visitAugmentedAssignment(node)) {
return [node.leftExpression, node.rightExpression];
}
break;
case ParseNodeType.Await: case ParseNodeType.Await:
return this.visitAwait(node as AwaitExpressionNode); if (this.visitAwait(node)) {
return [node.expression];
}
break;
case ParseNodeType.BinaryOperation: case ParseNodeType.BinaryOperation:
return this.visitBinaryOperation(node as BinaryExpressionNode); if (this.visitBinaryOperation(node)) {
return [node.leftExpression, node.rightExpression];
}
break;
case ParseNodeType.Break: case ParseNodeType.Break:
return this.visitBreak(node as BreakNode); if (this.visitBreak(node)) {
return [];
}
break;
case ParseNodeType.Call: case ParseNodeType.Call:
return this.visitCall(node as CallExpressionNode); if (this.visitCall(node)) {
return [node.leftExpression, ...node.arguments];
}
break;
case ParseNodeType.Class: 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: case ParseNodeType.Ternary:
return this.visitTernary(node as TernaryExpressionNode); if (this.visitTernary(node)) {
return [node.ifExpression, node.testExpression, node.elseExpression];
}
break;
case ParseNodeType.Constant: case ParseNodeType.Constant:
return this.visitConstant(node as ConstantNode); if (this.visitConstant(node)) {
return [];
}
break;
case ParseNodeType.Continue: case ParseNodeType.Continue:
return this.visitContinue(node as ContinueNode); if (this.visitContinue(node)) {
return [];
}
break;
case ParseNodeType.Decorator: case ParseNodeType.Decorator:
return this.visitDecorator(node as DecoratorNode); if (this.visitDecorator(node)) {
return [node.leftExpression, ...(node.arguments || [])];
}
break;
case ParseNodeType.Del: case ParseNodeType.Del:
return this.visitDel(node as DelNode); if (this.visitDel(node)) {
return node.expressions;
}
break;
case ParseNodeType.Dictionary: case ParseNodeType.Dictionary:
return this.visitDictionary(node as DictionaryNode); if (this.visitDictionary(node)) {
return node.entries;
}
break;
case ParseNodeType.DictionaryKeyEntry: case ParseNodeType.DictionaryKeyEntry:
return this.visitDictionaryKeyEntry(node as DictionaryKeyEntryNode); if (this.visitDictionaryKeyEntry(node)) {
return [node.keyExpression, node.valueExpression];
}
break;
case ParseNodeType.DictionaryExpandEntry: case ParseNodeType.DictionaryExpandEntry:
return this.visitDictionaryExpandEntry(node as DictionaryExpandEntryNode); if (this.visitDictionaryExpandEntry(node)) {
return [node.expandExpression];
}
break;
case ParseNodeType.Error: case ParseNodeType.Error:
return this.visitError(node as ErrorExpressionNode); if (this.visitError(node)) {
return [node.child];
}
break;
case ParseNodeType.If: case ParseNodeType.If:
return this.visitIf(node as IfNode); if (this.visitIf(node)) {
return [node.testExpression, node.ifSuite, node.elseSuite];
}
break;
case ParseNodeType.Import: case ParseNodeType.Import:
return this.visitImport(node as ImportNode); if (this.visitImport(node)) {
return node.list;
}
break;
case ParseNodeType.ImportAs: case ParseNodeType.ImportAs:
return this.visitImportAs(node as ImportAsNode); if (this.visitImportAs(node)) {
return [node.module, node.alias];
}
break;
case ParseNodeType.ImportFrom: case ParseNodeType.ImportFrom:
return this.visitImportFrom(node as ImportFromNode); if (this.visitImportFrom(node)) {
return [node.module, ...node.imports];
}
break;
case ParseNodeType.ImportFromAs: case ParseNodeType.ImportFromAs:
return this.visitImportFromAs(node as ImportFromAsNode); if (this.visitImportFromAs(node)) {
return [node.name, node.alias];
}
break;
case ParseNodeType.Index: case ParseNodeType.Index:
return this.visitIndex(node as IndexExpressionNode); if (this.visitIndex(node)) {
return [node.baseExpression, node.items];
}
break;
case ParseNodeType.IndexItems: case ParseNodeType.IndexItems:
return this.visitIndexItems(node as IndexItemsNode); if (this.visitIndexItems(node)) {
return node.items;
}
break;
case ParseNodeType.Ellipsis: case ParseNodeType.Ellipsis:
return this.visitEllipsis(node as EllipsisNode); if (this.visitEllipsis(node)) {
return [];
}
break;
case ParseNodeType.Except: case ParseNodeType.Except:
return this.visitExcept(node as ExceptNode); if (this.visitExcept(node)) {
return [node.typeExpression, node.name, node.exceptSuite];
}
break;
case ParseNodeType.For: 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: case ParseNodeType.FormatString:
return this.visitFormatString(node as FormatStringNode); if (this.visitFormatString(node)) {
return node.expressions;
}
break;
case ParseNodeType.Function: 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: case ParseNodeType.Global:
return this.visitGlobal(node as GlobalNode); if (this.visitGlobal(node)) {
return node.nameList;
}
break;
case ParseNodeType.Lambda: case ParseNodeType.Lambda:
return this.visitLambda(node as LambdaNode); if (this.visitLambda(node)) {
return [...node.parameters, node.expression];
}
break;
case ParseNodeType.List: case ParseNodeType.List:
return this.visitList(node as ListNode); if (this.visitList(node)) {
return node.entries;
}
break;
case ParseNodeType.ListComprehension: case ParseNodeType.ListComprehension:
return this.visitListComprehension(node as ListComprehensionNode); if (this.visitListComprehension(node)) {
return [node.expression, ...node.comprehensions];
}
break;
case ParseNodeType.ListComprehensionFor: case ParseNodeType.ListComprehensionFor:
return this.visitListComprehensionFor(node as ListComprehensionForNode); if (this.visitListComprehensionFor(node)) {
return [node.targetExpression, node.iterableExpression];
}
break;
case ParseNodeType.ListComprehensionIf: case ParseNodeType.ListComprehensionIf:
return this.visitListComprehensionIf(node as ListComprehensionIfNode); if (this.visitListComprehensionIf(node)) {
return [node.testExpression];
}
break;
case ParseNodeType.MemberAccess: case ParseNodeType.MemberAccess:
return this.visitMemberAccess(node as MemberAccessExpressionNode); if (this.visitMemberAccess(node)) {
return [node.leftExpression, node.memberName];
}
break;
case ParseNodeType.Module: case ParseNodeType.Module:
return this.visitModule(node as ModuleNode); if (this.visitModule(node)) {
return [...node.statements];
}
break;
case ParseNodeType.ModuleName: case ParseNodeType.ModuleName:
return this.visitModuleName(node as ModuleNameNode); if (this.visitModuleName(node)) {
return [];
}
break;
case ParseNodeType.Name: case ParseNodeType.Name:
return this.visitName(node as NameNode); if (this.visitName(node)) {
return [];
}
break;
case ParseNodeType.Nonlocal: case ParseNodeType.Nonlocal:
return this.visitNonlocal(node as NonlocalNode); if (this.visitNonlocal(node)) {
return node.nameList;
}
break;
case ParseNodeType.Number: case ParseNodeType.Number:
return this.visitNumber(node as NumberNode); if (this.visitNumber(node)) {
return [];
}
break;
case ParseNodeType.Parameter: case ParseNodeType.Parameter:
return this.visitParameter(node as ParameterNode); if (this.visitParameter(node)) {
return [node.name, node.typeAnnotation, node.defaultValue];
}
break;
case ParseNodeType.Pass: case ParseNodeType.Pass:
return this.visitPass(node as PassNode); if (this.visitPass(node)) {
return [];
}
break;
case ParseNodeType.Raise: case ParseNodeType.Raise:
return this.visitRaise(node as RaiseNode); if (this.visitRaise(node)) {
return [node.typeExpression, node.valueExpression, node.tracebackExpression];
}
break;
case ParseNodeType.Return: case ParseNodeType.Return:
return this.visitReturn(node as ReturnNode); if (this.visitReturn(node)) {
return [node.returnExpression];
}
break;
case ParseNodeType.Set: case ParseNodeType.Set:
return this.visitSet(node as SetNode); if (this.visitSet(node)) {
return node.entries;
}
break;
case ParseNodeType.Slice: case ParseNodeType.Slice:
return this.visitSlice(node as SliceExpressionNode); if (this.visitSlice(node)) {
return [node.startValue, node.endValue, node.stepValue];
}
break;
case ParseNodeType.StatementList: case ParseNodeType.StatementList:
return this.visitStatementList(node as StatementListNode); if (this.visitStatementList(node)) {
return node.statements;
}
break;
case ParseNodeType.String: case ParseNodeType.String:
return this.visitString(node as StringNode); if (this.visitString(node)) {
return [];
}
break;
case ParseNodeType.StringList: case ParseNodeType.StringList:
return this.visitStringList(node as StringListNode); if (this.visitStringList(node)) {
return [node.typeAnnotation, ...node.strings];
}
break;
case ParseNodeType.Suite: case ParseNodeType.Suite:
return this.visitSuite(node as SuiteNode); if (this.visitSuite(node)) {
return [...node.statements];
}
break;
case ParseNodeType.Tuple: case ParseNodeType.Tuple:
return this.visitTuple(node as TupleExpressionNode); if (this.visitTuple(node)) {
return node.expressions;
}
break;
case ParseNodeType.Try: 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: case ParseNodeType.TypeAnnotation:
return this.visitTypeAnnotation(node as TypeAnnotationExpressionNode); if (this.visitTypeAnnotation(node)) {
return [node.valueExpression, node.typeAnnotation];
}
break;
case ParseNodeType.UnaryOperation: case ParseNodeType.UnaryOperation:
return this.visitUnaryOperation(node as UnaryExpressionNode); if (this.visitUnaryOperation(node)) {
return [node.expression];
}
break;
case ParseNodeType.Unpack: case ParseNodeType.Unpack:
return this.visitUnpack(node as UnpackExpressionNode); if (this.visitUnpack(node)) {
return [node.expression];
}
break;
case ParseNodeType.While: case ParseNodeType.While:
return this.visitWhile(node as WhileNode); if (this.visitWhile(node)) {
return [node.testExpression, node.whileSuite, node.elseSuite];
}
break;
case ParseNodeType.With: case ParseNodeType.With:
return this.visitWith(node as WithNode); if (this.visitWith(node)) {
return [...node.withItems, node.suite];
}
break;
case ParseNodeType.WithItem: case ParseNodeType.WithItem:
return this.visitWithItem(node as WithItemNode); if (this.visitWithItem(node)) {
return [node.expression, node.target];
}
break;
case ParseNodeType.Yield: case ParseNodeType.Yield:
return this.visitYield(node as YieldExpressionNode); if (this.visitYield(node)) {
return [node.expression];
}
break;
case ParseNodeType.YieldFrom: case ParseNodeType.YieldFrom:
return this.visitYieldFrom(node as YieldFromExpressionNode); if (this.visitYieldFrom(node)) {
return [node.expression];
}
break;
case ParseNodeType.None:
default: default:
assert.fail('Unexpected node type'); assert.fail('Unexpected node type');
return true; break;
} }
return [];
} }
// Override these methods as necessary. // Override these methods as necessary.

View File

@ -18,9 +18,9 @@ import { TextRangeDiagnosticSink } from '../common/diagnosticSink';
import { NameBindings, NameBindingType } from '../parser/nameBindings'; import { NameBindings, NameBindingType } from '../parser/nameBindings';
import { AssignmentNode, AugmentedAssignmentExpressionNode, ClassNode, DelNode, import { AssignmentNode, AugmentedAssignmentExpressionNode, ClassNode, DelNode,
ExpressionNode, ForNode, FunctionNode, GlobalNode, ImportAsNode, ExpressionNode, ForNode, FunctionNode, GlobalNode, ImportAsNode,
ImportFromAsNode, ImportFromNode, LambdaNode, ListNode, ModuleNameNode, ModuleNode, ImportFromAsNode, ImportFromNode, LambdaNode, ModuleNameNode, ModuleNode,
NameNode, NonlocalNode, ParseNode, TupleExpressionNode, NonlocalNode, ParseNode, ParseNodeArray, ParseNodeType, TypeAnnotationExpressionNode,
TypeAnnotationExpressionNode, UnpackExpressionNode, WithNode } from '../parser/parseNodes'; WithNode } from '../parser/parseNodes';
import { AnalyzerNodeInfo } from './analyzerNodeInfo'; import { AnalyzerNodeInfo } from './analyzerNodeInfo';
import { ParseTreeWalker } from './parseTreeWalker'; import { ParseTreeWalker } from './parseTreeWalker';
@ -62,17 +62,12 @@ export class PostParseWalker extends ParseTreeWalker {
return this._importedModules; return this._importedModules;
} }
visitNode(node: ParseNode): boolean { visitNode(node: ParseNode) {
const children = node.getChildren(); const children = super.visitNode(node);
// Add the parent link to each of the child nodes. this._addParentLinks(node, children);
children.forEach(child => {
if (child) {
child.parent = node;
}
});
return super.visitNode(node); return children;
} }
visitImportAs(node: ImportAsNode): boolean { visitImportAs(node: ImportAsNode): boolean {
@ -110,22 +105,17 @@ export class PostParseWalker extends ParseTreeWalker {
this._addName(node.name.nameToken.value); this._addName(node.name.nameToken.value);
} }
return false; return true;
} }
visitWith(node: WithNode): boolean { visitWith(node: WithNode): boolean {
node.withItems.forEach(item => {
this.walk(item);
});
node.withItems.forEach(item => { node.withItems.forEach(item => {
if (item.target) { if (item.target) {
this._addPossibleTupleNamedTarget(item.target); this._addPossibleTupleNamedTarget(item.target);
} }
}); });
this.walk(node.suite); return true;
return false;
} }
visitFunction(node: FunctionNode): boolean { visitFunction(node: FunctionNode): boolean {
@ -154,6 +144,12 @@ export class PostParseWalker extends ParseTreeWalker {
this.walk(node.suite); 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; return false;
} }
@ -171,6 +167,11 @@ export class PostParseWalker extends ParseTreeWalker {
this.walk(node.suite); 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; return false;
} }
@ -192,6 +193,11 @@ export class PostParseWalker extends ParseTreeWalker {
this.walk(node.expression); 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; return false;
} }
@ -264,20 +270,29 @@ export class PostParseWalker extends ParseTreeWalker {
return true; 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) { private _addPossibleTupleNamedTarget(node: ExpressionNode) {
if (node instanceof NameNode) { if (node.nodeType === ParseNodeType.Name) {
this._addName(node.nameToken.value); this._addName(node.nameToken.value);
} else if (node instanceof TupleExpressionNode) { } else if (node.nodeType === ParseNodeType.Tuple) {
node.expressions.forEach(expr => { node.expressions.forEach(expr => {
this._addPossibleTupleNamedTarget(expr); this._addPossibleTupleNamedTarget(expr);
}); });
} else if (node instanceof ListNode) { } else if (node.nodeType === ParseNodeType.List) {
node.entries.forEach(expr => { node.entries.forEach(expr => {
this._addPossibleTupleNamedTarget(expr); this._addPossibleTupleNamedTarget(expr);
}); });
} else if (node instanceof TypeAnnotationExpressionNode) { } else if (node.nodeType === ParseNodeType.TypeAnnotation) {
this._addPossibleTupleNamedTarget(node.valueExpression); this._addPossibleTupleNamedTarget(node.valueExpression);
} else if (node instanceof UnpackExpressionNode) { } else if (node.nodeType === ParseNodeType.Unpack) {
this._addPossibleTupleNamedTarget(node.expression); this._addPossibleTupleNamedTarget(node.expression);
} }
} }

View File

@ -23,11 +23,11 @@ import { DiagnosticLevel } from '../common/configOptions';
import { CreateTypeStubFileAction } from '../common/diagnostic'; import { CreateTypeStubFileAction } from '../common/diagnostic';
import { PythonVersion } from '../common/pythonVersion'; import { PythonVersion } from '../common/pythonVersion';
import { TextRange } from '../common/textRange'; import { TextRange } from '../common/textRange';
import { AwaitExpressionNode, ClassNode, ErrorExpressionNode, import { AwaitExpressionNode, ClassNode, ErrorExpressionNode, ExpressionNode, FunctionNode,
ExpressionNode, FunctionNode, GlobalNode, IfNode, LambdaNode, ModuleNameNode, GlobalNode, IfNode, LambdaNode, ModuleNameNode, ModuleNode, NonlocalNode, ParseNode,
ModuleNode, NonlocalNode, RaiseNode, StatementListNode, StatementNode, ParseNodeType, RaiseNode, StatementNode, StringListNode, SuiteNode, TryNode,
StringListNode, SuiteNode, TryNode, TypeAnnotationExpressionNode, WhileNode, TypeAnnotationExpressionNode, WhileNode, YieldExpressionNode,
YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes'; YieldFromExpressionNode } from '../parser/parseNodes';
import { StringTokenUtils, UnescapeErrorType } from '../parser/stringTokenUtils'; import { StringTokenUtils, UnescapeErrorType } from '../parser/stringTokenUtils';
import { StringTokenFlags } from '../parser/tokenizerTypes'; import { StringTokenFlags } from '../parser/tokenizerTypes';
import { ScopeUtils } from '../scopeUtils'; 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. // 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 // We can't simply use this._currentScope because functions within a class use
// the scope of the containing function or module when they execute. // the scope of the containing function or module when they execute.
let functionOrModuleNode = node.parent; let functionOrModuleNode: ParseNode | undefined = node.parent;
while (functionOrModuleNode) { while (functionOrModuleNode) {
if (functionOrModuleNode instanceof ModuleNode || if (functionOrModuleNode.nodeType === ParseNodeType.Module ||
functionOrModuleNode instanceof FunctionNode) { functionOrModuleNode.nodeType === ParseNodeType.Function) {
break; break;
} }
@ -399,7 +399,7 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker {
unescapedResult.unescapeErrors.forEach(error => { unescapedResult.unescapeErrors.forEach(error => {
const start = stringNode.token.start + stringNode.token.prefixLength + const start = stringNode.token.start + stringNode.token.prefixLength +
stringNode.token.quoteMarkLength + error.offset; stringNode.token.quoteMarkLength + error.offset;
const textRange = new TextRange(start, error.length); const textRange = { start, length: error.length };
if (error.errorType === UnescapeErrorType.InvalidEscapeSequence) { if (error.errorType === UnescapeErrorType.InvalidEscapeSequence) {
this._addDiagnostic(this._fileInfo.diagnosticSettings.reportInvalidStringEscapeSequence, this._addDiagnostic(this._fileInfo.diagnosticSettings.reportInvalidStringEscapeSequence,
@ -503,19 +503,19 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker {
return undefined; return undefined;
} }
if (!(statemetns[0] instanceof StatementListNode)) { if (statemetns[0].nodeType !== ParseNodeType.StatementList) {
return undefined; return undefined;
} }
// If the first statement in the suite isn't a StringNode, // If the first statement in the suite isn't a StringNode,
// assume there is no docString. // assume there is no docString.
const statementList = statemetns[0] as StatementListNode; const statementList = statemetns[0];
if (statementList.statements.length === 0 || if (statementList.statements.length === 0 ||
!(statementList.statements[0] instanceof StringListNode)) { statementList.statements[0].nodeType !== ParseNodeType.StringList) {
return undefined; return undefined;
} }
const docStringNode = statementList.statements[0] as StringListNode; const docStringNode = statementList.statements[0];
const docStringToken = docStringNode.strings[0].token; const docStringToken = docStringNode.strings[0].token;
// Ignore f-strings. // Ignore f-strings.
@ -532,7 +532,7 @@ export abstract class SemanticAnalyzer extends ParseTreeWalker {
if (!functionNode) { if (!functionNode) {
this._addError( this._addError(
`'yield' not allowed outside of a function`, node); `'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 // PEP 525 indicates that 'yield from' is not allowed in an
// async function. // async function.
this._addError( this._addError(
@ -605,7 +605,8 @@ export class ModuleScopeAnalyzer extends SemanticAnalyzer {
assert(nameBindings !== undefined); assert(nameBindings !== undefined);
this._addNamesToScope(nameBindings!.getGlobalNames()); 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. // Associate the module's scope with the module type.
const moduleType = new ModuleType(this._currentScope.getSymbolTable(), const moduleType = new ModuleType(this._currentScope.getSymbolTable(),

View File

@ -467,18 +467,14 @@ export class SourceFile {
this._analysisJob.parseResults.tokens, configOptions.diagnosticSettings, this._analysisJob.parseResults.tokens, configOptions.diagnosticSettings,
useStrict); useStrict);
} catch (e) { } catch (e) {
let message: string; const message: string = (e.stack ? e.stack.toString() : undefined) ||
if (e instanceof Error) { (typeof e.message === 'string' ? e.message : undefined) ||
message = e.stack || e.message; JSON.stringify(e);
} else {
message = JSON.stringify(e);
}
this._console.log( this._console.log(
`An internal error occurred while parsing ${ this.getFilePath() }: ` + message); `An internal error occurred while parsing ${ this.getFilePath() }: ` + message);
this._analysisJob.parseResults = { this._analysisJob.parseResults = {
parseTree: new ModuleNode(new TextRange(0, 0)), parseTree: ModuleNode.create({ start: 0, length: 0 }),
futureImports: new StringMap<boolean>(), futureImports: new StringMap<boolean>(),
tokens: new TextRangeCollection<Token>([]), tokens: new TextRangeCollection<Token>([]),
lines: new TextRangeCollection<TextRange>([]), lines: new TextRangeCollection<TextRange>([]),
@ -650,13 +646,9 @@ export class SourceFile {
assert(moduleType instanceof ModuleType); assert(moduleType instanceof ModuleType);
this._analysisJob.moduleType = moduleType as ModuleType; this._analysisJob.moduleType = moduleType as ModuleType;
} catch (e) { } catch (e) {
let message: string; const message: string = (e.stack ? e.stack.toString() : undefined) ||
if (e instanceof Error) { (typeof e.message === 'string' ? e.message : undefined) ||
message = e.stack || e.message; JSON.stringify(e);
} else {
message = JSON.stringify(e);
}
this._console.log( this._console.log(
`An internal error occurred while performing semantic analysis for ${ this.getFilePath() }: ` + message); `An internal error occurred while performing semantic analysis for ${ this.getFilePath() }: ` + message);
@ -701,13 +693,9 @@ export class SourceFile {
} }
}); });
} catch (e) { } catch (e) {
let message: string; const message: string = (e.stack ? e.stack.toString() : undefined) ||
if (e instanceof Error) { (typeof e.message === 'string' ? e.message : undefined) ||
message = e.stack || e.message; JSON.stringify(e);
} else {
message = JSON.stringify(e);
}
this._console.log( this._console.log(
`An internal error occurred while while performing type analysis for ${ this.getFilePath() }: ` + message); `An internal error occurred while while performing type analysis for ${ this.getFilePath() }: ` + message);
const diagSink = new DiagnosticSink(); const diagSink = new DiagnosticSink();

View File

@ -17,16 +17,16 @@ import { convertOffsetsToRange } from '../common/positionUtils';
import { PythonVersion } from '../common/pythonVersion'; import { PythonVersion } from '../common/pythonVersion';
import { TextRange } from '../common/textRange'; import { TextRange } from '../common/textRange';
import { AssertNode, AssignmentNode, AugmentedAssignmentExpressionNode, import { AssertNode, AssignmentNode, AugmentedAssignmentExpressionNode,
BinaryExpressionNode, BreakNode, CallExpressionNode, ClassNode, ConstantNode, BinaryExpressionNode, BreakNode, CallExpressionNode, ClassNode,
ContinueNode, DecoratorNode, DelNode, ErrorExpressionNode, ExceptNode, ExpressionNode, ContinueNode, DecoratorNode, DelNode, ErrorExpressionNode, ExceptNode, ExpressionNode,
FormatStringNode, ForNode, FunctionNode, IfNode, ImportAsNode, FormatStringNode, ForNode, FunctionNode, IfNode,
ImportFromNode, IndexExpressionNode, LambdaNode, ListComprehensionForNode, ImportAsNode, ImportFromNode, IndexExpressionNode, LambdaNode,
ListComprehensionNode, ListNode, MemberAccessExpressionNode, ModuleNode, NameNode, ListComprehensionNode, MemberAccessExpressionNode, ModuleNode,
ParameterCategory, ParseNode, RaiseNode, ReturnNode, SliceExpressionNode, NameNode, ParameterCategory, ParameterNode, ParseNode, ParseNodeType,
StringListNode, StringNode, SuiteNode, TernaryExpressionNode, TryNode, RaiseNode, ReturnNode, SliceExpressionNode, StringListNode,
TupleExpressionNode, TypeAnnotationExpressionNode, UnaryExpressionNode, SuiteNode, TernaryExpressionNode, TryNode,
UnpackExpressionNode, WhileNode, WithNode, YieldExpressionNode, TupleExpressionNode, TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode,
YieldFromExpressionNode } from '../parser/parseNodes'; WhileNode, WithNode, YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes';
import { KeywordType } from '../parser/tokenizerTypes'; import { KeywordType } from '../parser/tokenizerTypes';
import { ScopeUtils } from '../scopeUtils'; import { ScopeUtils } from '../scopeUtils';
import { AnalyzerFileInfo } from './analyzerFileInfo'; import { AnalyzerFileInfo } from './analyzerFileInfo';
@ -288,7 +288,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
node: node.name, node: node.name,
declaredType: decoratedType, declaredType: decoratedType,
path: this._fileInfo.filePath, 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); 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 annotatedType: Type | undefined;
let defaultValueType: 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' // 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 // the type checker should assume that the type is optional (i.e. a union
// of the specified type and 'None'). // 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) { if (param.defaultValue.token.keywordType === KeywordType.None) {
isNoneWithoutOptional = true; isNoneWithoutOptional = true;
@ -514,7 +515,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
category: DeclarationCategory.Parameter, category: DeclarationCategory.Parameter,
node: paramNode, node: paramNode,
path: this._fileInfo.filePath, 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 declaredType: specializedParamType
}; };
assert(paramNode !== undefined && paramNode.name !== undefined); assert(paramNode !== undefined && paramNode.name !== undefined);
@ -542,7 +544,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
category: containingClassNode ? DeclarationCategory.Method : DeclarationCategory.Function, category: containingClassNode ? DeclarationCategory.Method : DeclarationCategory.Function,
node: node.name, node: node.name,
path: this._fileInfo.filePath, 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 declaredType: decoratedType
}; };
this._assignTypeToNameNode(node.name, decoratedType, declaration); this._assignTypeToNameNode(node.name, decoratedType, declaration);
@ -579,7 +582,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
category: DeclarationCategory.Parameter, category: DeclarationCategory.Parameter,
node: param, node: param,
path: this._fileInfo.filePath, 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(); const paramType = UnknownType.create();
this._addTypeSourceToNameNode(param.name, paramType, declaration); this._addTypeSourceToNameNode(param.name, paramType, declaration);
@ -602,7 +606,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
functionType.getInferredReturnType().addSource( functionType.getInferredReturnType().addSource(
returnType, AnalyzerNodeInfo.getTypeSourceId(node.expression)); returnType, AnalyzerNodeInfo.getTypeSourceId(node.expression));
this.walkChildren(node.expression); // Walk the children.
this.walkMultiple([...node.parameters, node.expression]);
}); });
// Cache the function type. // Cache the function type.
@ -684,7 +689,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
// when complete. // when complete.
this._enterTemporaryScope(() => { this._enterTemporaryScope(() => {
node.comprehensions.forEach(compr => { node.comprehensions.forEach(compr => {
if (compr instanceof ListComprehensionForNode) { if (compr.nodeType === ParseNodeType.ListComprehensionFor) {
this.walk(compr.iterableExpression); this.walk(compr.iterableExpression);
const iteratorType = this._getTypeOfExpression(compr.iterableExpression); const iteratorType = this._getTypeOfExpression(compr.iterableExpression);
@ -987,7 +992,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
category: DeclarationCategory.Variable, category: DeclarationCategory.Variable,
node: node.name, node: node.name,
path: this._fileInfo.filePath, 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._addNamedTargetToCurrentScope(node.name);
this._assignTypeToNameNode(node.name, exceptionType, declaration); 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. // If this is an enum, transform the type as required.
let effectiveType = srcType; let effectiveType = srcType;
if (node.leftExpression instanceof NameNode && !node.typeAnnotationComment) { if (node.leftExpression.nodeType === ParseNodeType.Name && !node.typeAnnotationComment) {
effectiveType = this._transformTypeForPossibleEnumClass( effectiveType = this._transformTypeForPossibleEnumClass(
node.leftExpression, effectiveType); node.leftExpression, effectiveType);
} }
@ -1139,7 +1145,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
// Did the caller pass an optional assert message as a second parameter? // Did the caller pass an optional assert message as a second parameter?
// If so, strip it off and include only the test. // 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]; assertTestExpression = node.testExpression.expressions[0];
} }
@ -1217,7 +1223,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
visitFormatString(node: FormatStringNode): boolean { visitFormatString(node: FormatStringNode): boolean {
node.expressions.forEach(formatExpr => { node.expressions.forEach(formatExpr => {
this._getTypeOfExpression(formatExpr.expression, this._getTypeOfExpression(formatExpr,
EvaluatorFlags.AllowForwardReferences); EvaluatorFlags.AllowForwardReferences);
}); });
@ -1261,7 +1267,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
this._markExpressionAccessed(expr); this._markExpressionAccessed(expr);
this._evaluateExpressionForDeletion(expr); this._evaluateExpressionForDeletion(expr);
if (expr instanceof NameNode) { if (expr.nodeType === ParseNodeType.Name) {
const symbolWithScope = this._currentScope.lookUpSymbolRecursive(expr.nameToken.value); const symbolWithScope = this._currentScope.lookUpSymbolRecursive(expr.nameToken.value);
if (symbolWithScope) { if (symbolWithScope) {
if (symbolWithScope.symbol.hasDeclarations()) { if (symbolWithScope.symbol.hasDeclarations()) {
@ -1507,7 +1513,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
let typeHintType = this._getTypeOfAnnotation(node.typeAnnotation); let typeHintType = this._getTypeOfAnnotation(node.typeAnnotation);
// If this is within an enum, transform the type. // 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( typeHintType = this._transformTypeForPossibleEnumClass(
node.valueExpression, typeHintType); node.valueExpression, typeHintType);
} }
@ -1552,8 +1558,9 @@ export class TypeAnalyzer extends ParseTreeWalker {
// Create a text range that covers the next statement through // Create a text range that covers the next statement through
// the end of the suite. // the end of the suite.
const start = node.statements[index + 1].start; const start = node.statements[index + 1].start;
const end = node.statements[node.statements.length - 1].end; const lastStatement = node.statements[node.statements.length - 1];
this._addUnusedCode(new TextRange(start, end - start)); const end = TextRange.getEnd(lastStatement);
this._addUnusedCode({ start, length: end - start });
} }
// Note that we already reported this so we don't do it again. // Note that we already reported this so we don't do it again.
@ -1572,7 +1579,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
return; return;
} }
if (!(node.leftExpression instanceof NameNode) || if (node.leftExpression.nodeType !== ParseNodeType.Name ||
node.leftExpression.nameToken.value !== 'isinstance' || node.leftExpression.nameToken.value !== 'isinstance' ||
node.arguments.length !== 2) { node.arguments.length !== 2) {
return; return;
@ -1687,7 +1694,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
return false; return false;
} }
if (node.leftExpression instanceof NameNode) { if (node.leftExpression.nodeType === ParseNodeType.Name) {
const assignedName = node.leftExpression.nameToken.value; const assignedName = node.leftExpression.nameToken.value;
let specialType: Type | undefined; let specialType: Type | undefined;
@ -1754,14 +1761,14 @@ export class TypeAnalyzer extends ParseTreeWalker {
node: node.leftExpression, node: node.leftExpression,
path: this._fileInfo.filePath, path: this._fileInfo.filePath,
range: convertOffsetsToRange(node.leftExpression.start, 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._assignTypeToNameNode(node.leftExpression, specialType, declaration);
this._updateExpressionTypeForNode(node.leftExpression, specialType); this._updateExpressionTypeForNode(node.leftExpression, specialType);
return true; return true;
} }
} else if (node.leftExpression instanceof TypeAnnotationExpressionNode && } else if (node.leftExpression.nodeType === ParseNodeType.TypeAnnotation &&
node.leftExpression.valueExpression instanceof NameNode) { node.leftExpression.valueExpression.nodeType === ParseNodeType.Name) {
const nameNode = node.leftExpression.valueExpression; const nameNode = node.leftExpression.valueExpression;
const assignedName = nameNode.nameToken.value; const assignedName = nameNode.nameToken.value;
@ -1804,7 +1811,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
node: nameNode, node: nameNode,
path: this._fileInfo.filePath, path: this._fileInfo.filePath,
range: convertOffsetsToRange(nameNode.start, range: convertOffsetsToRange(nameNode.start,
nameNode.end, this._fileInfo.lines) TextRange.getEnd(nameNode), this._fileInfo.lines)
}; };
this._assignTypeToNameNode(nameNode, specialType, declaration); this._assignTypeToNameNode(nameNode, specialType, declaration);
this._updateExpressionTypeForNode(nameNode, specialType); this._updateExpressionTypeForNode(nameNode, specialType);
@ -1869,7 +1876,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
let declarationHandled = false; let declarationHandled = false;
if (target instanceof NameNode) { if (target.nodeType === ParseNodeType.Name) {
const name = target.nameToken; const name = target.nameToken;
const declaration: Declaration = { const declaration: Declaration = {
category: DeclarationCategory.Variable, category: DeclarationCategory.Variable,
@ -1877,7 +1884,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
isConstant: SymbolUtils.isConstantName(name.value), isConstant: SymbolUtils.isConstantName(name.value),
path: this._fileInfo.filePath, path: this._fileInfo.filePath,
declaredType, 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); const symbolWithScope = this._currentScope.lookUpSymbolRecursive(name.value);
@ -1886,11 +1893,11 @@ export class TypeAnalyzer extends ParseTreeWalker {
} }
AnalyzerNodeInfo.setDeclarations(target, [declaration]); AnalyzerNodeInfo.setDeclarations(target, [declaration]);
declarationHandled = true; declarationHandled = true;
} else if (target instanceof MemberAccessExpressionNode) { } else if (target.nodeType === ParseNodeType.MemberAccess) {
const targetNode = target.leftExpression; const targetNode = target.leftExpression;
// Handle member accesses (e.g. self.x or cls.y). // 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. // Determine whether we're writing to a class or instance member.
const enclosingClassNode = ParseTreeUtils.getEnclosingClass(target); const enclosingClassNode = ParseTreeUtils.getEnclosingClass(target);
@ -2028,14 +2035,14 @@ export class TypeAnalyzer extends ParseTreeWalker {
if (primaryDeclaration.node && if (primaryDeclaration.node &&
primaryDeclaration.node.parent && primaryDeclaration.node.parent &&
primaryDeclaration.node.parent === classOrModuleNode && primaryDeclaration.node.parent === classOrModuleNode &&
classOrModuleNode instanceof ClassNode) { classOrModuleNode.nodeType === ParseNodeType.Class) {
classOrModuleNode = ParseTreeUtils.getEnclosingClassOrModule(classOrModuleNode); classOrModuleNode = ParseTreeUtils.getEnclosingClassOrModule(classOrModuleNode);
} }
// If it's a class member, check whether it's a legal protected access. // If it's a class member, check whether it's a legal protected access.
let isProtectedAccess = false; let isProtectedAccess = false;
if (classOrModuleNode instanceof ClassNode) { if (classOrModuleNode && classOrModuleNode.nodeType === ParseNodeType.Class) {
if (isProtectedName) { if (isProtectedName) {
const declarationClassType = AnalyzerNodeInfo.getExpressionType(classOrModuleNode); const declarationClassType = AnalyzerNodeInfo.getExpressionType(classOrModuleNode);
if (declarationClassType && declarationClassType instanceof ClassType) { if (declarationClassType && declarationClassType instanceof ClassType) {
@ -2065,7 +2072,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
`'${ nameValue }' is protected and used outside of a derived class`, `'${ nameValue }' is protected and used outside of a derived class`,
node); node);
} else { } else {
const scopeName = classOrModuleNode instanceof ClassNode ? const scopeName = classOrModuleNode.nodeType === ParseNodeType.Class ?
'class' : 'module'; 'class' : 'module';
this._addDiagnostic(this._fileInfo.diagnosticSettings.reportPrivateUsage, this._addDiagnostic(this._fileInfo.diagnosticSettings.reportPrivateUsage,
@ -2325,7 +2332,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
} }
// Handle property setters and deleters. // Handle property setters and deleters.
if (decoratorNode.leftExpression instanceof MemberAccessExpressionNode) { if (decoratorNode.leftExpression.nodeType === ParseNodeType.MemberAccess) {
const baseType = this._getTypeOfExpression(decoratorNode.leftExpression.leftExpression); const baseType = this._getTypeOfExpression(decoratorNode.leftExpression.leftExpression);
if (baseType instanceof PropertyType) { if (baseType instanceof PropertyType) {
const memberName = decoratorNode.leftExpression.memberName.nameToken.value; const memberName = decoratorNode.leftExpression.memberName.nameToken.value;
@ -2736,7 +2743,9 @@ export class TypeAnalyzer extends ParseTreeWalker {
node: node.memberName, node: node.memberName,
isConstant, isConstant,
path: this._fileInfo.filePath, 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) { if (typeAnnotationNode) {
@ -3021,7 +3030,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
} }
private _markExpressionAccessed(target: ExpressionNode) { private _markExpressionAccessed(target: ExpressionNode) {
if (target instanceof NameNode) { if (target.nodeType === ParseNodeType.Name) {
const nameValue = target.nameToken.value; const nameValue = target.nameToken.value;
const symbolWithScope = this._currentScope.lookUpSymbolRecursive(nameValue); const symbolWithScope = this._currentScope.lookUpSymbolRecursive(nameValue);
@ -3032,14 +3041,15 @@ export class TypeAnalyzer extends ParseTreeWalker {
} }
private _assignTypeToExpression(target: ExpressionNode, srcType: Type, srcExpr: ExpressionNode): void { private _assignTypeToExpression(target: ExpressionNode, srcType: Type, srcExpr: ExpressionNode): void {
if (target instanceof NameNode) { if (target.nodeType === ParseNodeType.Name) {
const name = target.nameToken; const name = target.nameToken;
const declaration: Declaration = { const declaration: Declaration = {
category: DeclarationCategory.Variable, category: DeclarationCategory.Variable,
node: target, node: target,
isConstant: SymbolUtils.isConstantName(name.value), isConstant: SymbolUtils.isConstantName(name.value),
path: this._fileInfo.filePath, 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. // 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 // It's common for modules to include the expression
// __all__ = ['a', 'b', 'c'] // __all__ = ['a', 'b', 'c']
// We will mark the symbols referenced by these strings as accessed. // We will mark the symbols referenced by these strings as accessed.
if (srcExpr instanceof ListNode) { if (srcExpr.nodeType === ParseNodeType.List) {
srcExpr.entries.forEach(entryExpr => { srcExpr.entries.forEach(entryExpr => {
if (entryExpr instanceof StringListNode || entryExpr instanceof StringNode) { if (entryExpr.nodeType === ParseNodeType.StringList || entryExpr.nodeType === ParseNodeType.String) {
const symbolName = entryExpr.getValue(); const symbolName = entryExpr.nodeType === ParseNodeType.String ?
entryExpr.value :
entryExpr.strings.map(s => s.value).join('');
const symbolInScope = this._currentScope.lookUpSymbolRecursive(symbolName); const symbolInScope = this._currentScope.lookUpSymbolRecursive(symbolName);
if (symbolInScope) { if (symbolInScope) {
this._setSymbolAccessed(symbolInScope.symbol); this._setSymbolAccessed(symbolInScope.symbol);
@ -3065,11 +3077,11 @@ export class TypeAnalyzer extends ParseTreeWalker {
target, srcType, srcExpr); target, srcType, srcExpr);
this._assignTypeToNameNode(target, srcType, declaration, srcExpr); this._assignTypeToNameNode(target, srcType, declaration, srcExpr);
} else if (target instanceof MemberAccessExpressionNode) { } else if (target.nodeType === ParseNodeType.MemberAccess) {
const targetNode = target.leftExpression; const targetNode = target.leftExpression;
// Handle member accesses (e.g. self.x or cls.y). // 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. // Determine whether we're writing to a class or instance member.
const enclosingClassNode = ParseTreeUtils.getEnclosingClass(target); 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. // Initialize the array of target types, one for each target.
const targetTypes: Type[][] = new Array(target.expressions.length); const targetTypes: Type[][] = new Array(target.expressions.length);
for (let i = 0; i < target.expressions.length; i++) { for (let i = 0; i < target.expressions.length; i++) {
@ -3113,7 +3125,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
} }
const targetEndsWithUnpackOperator = target.expressions.length > 0 && 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 (targetEndsWithUnpackOperator) {
if (entryCount >= target.expressions.length) { if (entryCount >= target.expressions.length) {
@ -3169,7 +3181,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
const targetType = typeList.length === 0 ? UnknownType.create() : TypeUtils.combineTypes(typeList); const targetType = typeList.length === 0 ? UnknownType.create() : TypeUtils.combineTypes(typeList);
this._assignTypeToExpression(expr, targetType, srcExpr); this._assignTypeToExpression(expr, targetType, srcExpr);
}); });
} else if (target instanceof TypeAnnotationExpressionNode) { } else if (target.nodeType === ParseNodeType.TypeAnnotation) {
const typeHintType = this._getTypeOfAnnotation(target.typeAnnotation); const typeHintType = this._getTypeOfAnnotation(target.typeAnnotation);
const diagAddendum = new DiagnosticAddendum(); const diagAddendum = new DiagnosticAddendum();
if (TypeUtils.canAssignType(typeHintType, srcType, diagAddendum)) { if (TypeUtils.canAssignType(typeHintType, srcType, diagAddendum)) {
@ -3177,15 +3189,16 @@ export class TypeAnalyzer extends ParseTreeWalker {
} }
this._assignTypeToExpression(target.valueExpression, srcType, srcExpr); this._assignTypeToExpression(target.valueExpression, srcType, srcExpr);
} else if (target instanceof UnpackExpressionNode) { } else if (target.nodeType === ParseNodeType.Unpack) {
if (target.expression instanceof NameNode) { if (target.expression.nodeType === ParseNodeType.Name) {
const name = target.expression.nameToken; const name = target.expression.nameToken;
const declaration: Declaration = { const declaration: Declaration = {
category: DeclarationCategory.Variable, category: DeclarationCategory.Variable,
node: target.expression, node: target.expression,
isConstant: SymbolUtils.isConstantName(name.value), isConstant: SymbolUtils.isConstantName(name.value),
path: this._fileInfo.filePath, 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()) { if (!srcType.isAny()) {
@ -3199,7 +3212,7 @@ export class TypeAnalyzer extends ParseTreeWalker {
} }
this._assignTypeToNameNode(target.expression, srcType, declaration, srcExpr); this._assignTypeToNameNode(target.expression, srcType, declaration, srcExpr);
} }
} else if (target instanceof ListNode) { } else if (target.nodeType === ParseNodeType.List) {
target.entries.forEach(entry => { target.entries.forEach(entry => {
this._assignTypeToExpression(entry, UnknownType.create(), srcExpr); this._assignTypeToExpression(entry, UnknownType.create(), srcExpr);
}); });
@ -3210,20 +3223,20 @@ export class TypeAnalyzer extends ParseTreeWalker {
} }
private _addNamedTargetToCurrentScope(node: ExpressionNode) { private _addNamedTargetToCurrentScope(node: ExpressionNode) {
if (node instanceof NameNode) { if (node.nodeType === ParseNodeType.Name) {
const symbol = this._currentScope.addSymbol(node.nameToken.value, true); const symbol = this._currentScope.addSymbol(node.nameToken.value, true);
// Mark the symbol as accessed. These symbols are not persisted // Mark the symbol as accessed. These symbols are not persisted
// between analysis passes, so we never have an opportunity to // between analysis passes, so we never have an opportunity to
// mark them as accessed. // mark them as accessed.
symbol.setIsAcccessed(); symbol.setIsAcccessed();
} else if (node instanceof TypeAnnotationExpressionNode) { } else if (node.nodeType === ParseNodeType.TypeAnnotation) {
this._addNamedTargetToCurrentScope(node.valueExpression); this._addNamedTargetToCurrentScope(node.valueExpression);
} else if (node instanceof TupleExpressionNode) { } else if (node.nodeType === ParseNodeType.Tuple) {
node.expressions.forEach(expr => { node.expressions.forEach(expr => {
this._addNamedTargetToCurrentScope(expr); this._addNamedTargetToCurrentScope(expr);
}); });
} else if (node instanceof ListNode) { } else if (node.nodeType === ParseNodeType.List) {
node.entries.forEach(expr => { node.entries.forEach(expr => {
this._addNamedTargetToCurrentScope(expr); this._addNamedTargetToCurrentScope(expr);
}); });
@ -3292,9 +3305,9 @@ export class TypeAnalyzer extends ParseTreeWalker {
// Is this module ever accessed? // Is this module ever accessed?
if (targetSymbol && !targetSymbol.isAccessed()) { if (targetSymbol && !targetSymbol.isAccessed()) {
const multipartName = nameParts.map(np => np.nameToken.value).join('.'); 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) { if (nameParts.length > 1) {
textRange.extend(nameParts[nameParts.length - 1]); TextRange.extend(textRange, nameParts[nameParts.length - 1]);
} }
this._fileInfo.diagnosticSink.addUnusedCodeWithTextRange( this._fileInfo.diagnosticSink.addUnusedCodeWithTextRange(
`'${ multipartName }' is not accessed`, textRange); `'${ multipartName }' is not accessed`, textRange);
@ -3581,6 +3594,12 @@ export class TypeAnalyzer extends ParseTreeWalker {
const newScope = AnalyzerNodeInfo.getScope(node); const newScope = AnalyzerNodeInfo.getScope(node);
assert(newScope !== undefined); 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; let prevParent: Scope | undefined;
if (!newScope!.isIndependentlyExecutable()) { if (!newScope!.isIndependentlyExecutable()) {
// Temporary reparent the scope in case it is contained // Temporary reparent the scope in case it is contained
@ -3608,6 +3627,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
newScope!.setParent(prevParent); newScope!.setParent(prevParent);
} }
this._defaultValueInitializerExpression = wasDefaultValueInitizer;
return newScope!; return newScope!;
} }

View File

@ -12,10 +12,7 @@
* None within that scope. * None within that scope.
*/ */
import { ArgumentCategory, BinaryExpressionNode, CallExpressionNode, ConstantNode, import { ArgumentCategory, ExpressionNode, ParseNodeType } from '../parser/parseNodes';
ExpressionNode, MemberAccessExpressionNode, NameNode,
TypeAnnotationExpressionNode,
UnaryExpressionNode } from '../parser/parseNodes';
import { KeywordType, OperatorType } from '../parser/tokenizerTypes'; import { KeywordType, OperatorType } from '../parser/tokenizerTypes';
import { ClassType, NeverType, NoneType, ObjectType, Type, TypeCategory, UnionType } from './types'; import { ClassType, NeverType, NoneType, ObjectType, Type, TypeCategory, UnionType } from './types';
import { TypeUtils } from './typeUtils'; import { TypeUtils } from './typeUtils';
@ -102,9 +99,9 @@ export class TypeConstraint {
// For now, we support only simple names and member access chains // For now, we support only simple names and member access chains
// that include only simple names (e.g. "A.B.C.D"). // that include only simple names (e.g. "A.B.C.D").
static isSupportedExpression(expression: ExpressionNode) { static isSupportedExpression(expression: ExpressionNode) {
if (expression instanceof NameNode) { if (expression.nodeType === ParseNodeType.Name) {
return true; return true;
} else if (expression instanceof MemberAccessExpressionNode) { } else if (expression.nodeType === ParseNodeType.MemberAccess) {
if (!this.isSupportedExpression(expression.leftExpression)) { if (!this.isSupportedExpression(expression.leftExpression)) {
return false; return false;
} }
@ -122,12 +119,12 @@ export class TypeConstraint {
private _doesExpressionMatchRecursive(expression1: ExpressionNode, private _doesExpressionMatchRecursive(expression1: ExpressionNode,
expression2: ExpressionNode): boolean { expression2: ExpressionNode): boolean {
if (expression1 instanceof NameNode) { if (expression1.nodeType === ParseNodeType.Name) {
if (expression2 instanceof NameNode) { if (expression2.nodeType === ParseNodeType.Name) {
return expression1.nameToken.value === expression2.nameToken.value; return expression1.nameToken.value === expression2.nameToken.value;
} }
} else if (expression1 instanceof MemberAccessExpressionNode) { } else if (expression1.nodeType === ParseNodeType.MemberAccess) {
if (expression2 instanceof MemberAccessExpressionNode) { if (expression2.nodeType === ParseNodeType.MemberAccess) {
return this._doesExpressionMatchRecursive(expression1.leftExpression, expression2.leftExpression) && return this._doesExpressionMatchRecursive(expression1.leftExpression, expression2.leftExpression) &&
this._doesExpressionMatchRecursive(expression1.memberName, expression2.memberName); this._doesExpressionMatchRecursive(expression1.memberName, expression2.memberName);
} }
@ -145,7 +142,7 @@ export class TypeConstraintBuilder {
typeEvaluator: (node: ExpressionNode) => Type): typeEvaluator: (node: ExpressionNode) => Type):
ConditionalTypeConstraintResults | undefined { ConditionalTypeConstraintResults | undefined {
if (testExpression instanceof BinaryExpressionNode) { if (testExpression.nodeType === ParseNodeType.BinaryOperation) {
const results: ConditionalTypeConstraintResults = { const results: ConditionalTypeConstraintResults = {
ifConstraints: [], ifConstraints: [],
elseConstraints: [] elseConstraints: []
@ -157,7 +154,7 @@ export class TypeConstraintBuilder {
// Look for "X is None" or "X is not None". These are commonly-used // Look for "X is None" or "X is not None". These are commonly-used
// patterns used in control flow. // patterns used in control flow.
if (TypeConstraint.isSupportedExpression(testExpression.leftExpression)) { if (TypeConstraint.isSupportedExpression(testExpression.leftExpression)) {
if (testExpression.rightExpression instanceof ConstantNode && if (testExpression.rightExpression.nodeType === ParseNodeType.Constant &&
testExpression.rightExpression.token.keywordType === KeywordType.None) { testExpression.rightExpression.token.keywordType === KeywordType.None) {
const originalType = typeEvaluator(testExpression.leftExpression); const originalType = typeEvaluator(testExpression.leftExpression);
@ -175,7 +172,7 @@ export class TypeConstraintBuilder {
} }
// Look for "type(X) is Y" or "type(X) is not Y". // 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); const callType = typeEvaluator(testExpression.leftExpression.leftExpression);
if (callType instanceof ClassType && callType.isBuiltIn() && if (callType instanceof ClassType && callType.isBuiltIn() &&
callType.getClassName() === 'type' && callType.getClassName() === 'type' &&
@ -242,7 +239,7 @@ export class TypeConstraintBuilder {
} }
return results; return results;
} }
} else if (testExpression instanceof UnaryExpressionNode) { } else if (testExpression.nodeType === ParseNodeType.UnaryOperation) {
if (testExpression.operator === OperatorType.Not) { if (testExpression.operator === OperatorType.Not) {
const constraints = this.buildTypeConstraintsForConditional( const constraints = this.buildTypeConstraintsForConditional(
testExpression.expression, typeEvaluator); testExpression.expression, typeEvaluator);
@ -255,8 +252,8 @@ export class TypeConstraintBuilder {
}; };
} }
} }
} else if (testExpression instanceof NameNode || } else if (testExpression.nodeType === ParseNodeType.Name ||
testExpression instanceof MemberAccessExpressionNode) { testExpression.nodeType === ParseNodeType.MemberAccess) {
if (TypeConstraint.isSupportedExpression(testExpression)) { if (TypeConstraint.isSupportedExpression(testExpression)) {
const originalType = typeEvaluator(testExpression); const originalType = typeEvaluator(testExpression);
@ -269,8 +266,8 @@ export class TypeConstraintBuilder {
elseConstraints: [falseConstraint] elseConstraints: [falseConstraint]
}; };
} }
} else if (testExpression instanceof CallExpressionNode) { } else if (testExpression.nodeType === ParseNodeType.Call) {
if (testExpression.leftExpression instanceof NameNode && if (testExpression.leftExpression.nodeType === ParseNodeType.Name &&
testExpression.leftExpression.nameToken.value === 'isinstance' && testExpression.leftExpression.nameToken.value === 'isinstance' &&
testExpression.arguments.length === 2) { testExpression.arguments.length === 2) {
@ -329,7 +326,7 @@ export class TypeConstraintBuilder {
static buildTypeConstraintForAssignment(targetNode: ExpressionNode, static buildTypeConstraintForAssignment(targetNode: ExpressionNode,
assignmentType: Type): TypeConstraint | undefined { assignmentType: Type): TypeConstraint | undefined {
if (targetNode instanceof TypeAnnotationExpressionNode) { if (targetNode.nodeType === ParseNodeType.TypeAnnotation) {
if (TypeConstraint.isSupportedExpression(targetNode.valueExpression)) { if (TypeConstraint.isSupportedExpression(targetNode.valueExpression)) {
return new TypeConstraint(targetNode.valueExpression, assignmentType); return new TypeConstraint(targetNode.valueExpression, assignmentType);
} }

View File

@ -13,8 +13,9 @@ import * as fs from 'fs';
import { ArgumentCategory, ArgumentNode, AssignmentNode, AugmentedAssignmentExpressionNode, import { ArgumentCategory, ArgumentNode, AssignmentNode, AugmentedAssignmentExpressionNode,
ClassNode, DecoratorNode, ExpressionNode, ForNode, FunctionNode, IfNode, ClassNode, DecoratorNode, ExpressionNode, ForNode, FunctionNode, IfNode,
ImportFromNode, ImportNode, MemberAccessExpressionNode, ModuleNameNode, NameNode, ImportFromNode, ImportNode, MemberAccessExpressionNode, ModuleNameNode, NameNode,
ParameterCategory, ParameterNode, ParseNode, StatementListNode, ParameterCategory, ParameterNode, ParseNode, ParseNodeType,
StringListNode, StringNode, TryNode, TypeAnnotationExpressionNode, WhileNode, StatementListNode, StringListNode, StringNode, TryNode, TypeAnnotationExpressionNode,
WhileNode,
WithNode } from '../parser/parseNodes'; WithNode } from '../parser/parseNodes';
import { AnalyzerNodeInfo } from './analyzerNodeInfo'; import { AnalyzerNodeInfo } from './analyzerNodeInfo';
import { ParseTreeUtils, PrintExpressionFlags } from './parseTreeUtils'; import { ParseTreeUtils, PrintExpressionFlags } from './parseTreeUtils';
@ -86,7 +87,7 @@ class ImportSymbolWalker extends ParseTreeWalker {
visitString(node: StringNode) { visitString(node: StringNode) {
if (this._treatStringsAsSymbols) { if (this._treatStringsAsSymbols) {
const value = node.getValue(); const value = node.value;
this._markNameAccessed(node, value); this._markNameAccessed(node, value);
} }
@ -173,7 +174,7 @@ export class TypeStubWriter extends ParseTreeWalker {
this._emitSuite(() => { this._emitSuite(() => {
this._classNestCount++; this._classNestCount++;
this.walkChildren(node); this.walk(node.suite);
this._classNestCount--; this._classNestCount--;
}); });
@ -228,7 +229,7 @@ export class TypeStubWriter extends ParseTreeWalker {
this._emitSuite(() => { this._emitSuite(() => {
// Don't emit any nested functions. // Don't emit any nested functions.
this._functionNestCount++; this._functionNestCount++;
this.walkChildren(node); this.walk(node.suite);
this._functionNestCount--; this._functionNestCount--;
}); });
@ -273,13 +274,18 @@ export class TypeStubWriter extends ParseTreeWalker {
this._emittedSuite = true; this._emittedSuite = true;
this._emitLine('if ' + this._printExpression(node.testExpression) + ':'); this._emitLine('if ' + this._printExpression(node.testExpression) + ':');
this._emitSuite(() => { this._emitSuite(() => {
this.walkChildren(node.ifSuite); this.walkMultiple(node.ifSuite.statements);
}); });
if (node.elseSuite) { const elseSuite = node.elseSuite;
if (elseSuite) {
this._emitLine('else:'); this._emitLine('else:');
this._emitSuite(() => { 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--; this._ifNestCount--;
@ -291,7 +297,7 @@ export class TypeStubWriter extends ParseTreeWalker {
visitAssignment(node: AssignmentNode) { visitAssignment(node: AssignmentNode) {
let line = ''; let line = '';
if (node.leftExpression instanceof NameNode) { if (node.leftExpression.nodeType === ParseNodeType.Name) {
if (this._functionNestCount === 0) { if (this._functionNestCount === 0) {
line = this._printExpression(node.leftExpression); line = this._printExpression(node.leftExpression);
} }
@ -299,9 +305,9 @@ export class TypeStubWriter extends ParseTreeWalker {
if (node.leftExpression.nameToken.value === '__all__') { if (node.leftExpression.nameToken.value === '__all__') {
this._emitLine(this._printExpression(node, false, true)); 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; const baseExpression = node.leftExpression.leftExpression;
if (baseExpression instanceof NameNode) { if (baseExpression.nodeType === ParseNodeType.Name) {
if (baseExpression.nameToken.value === 'self') { if (baseExpression.nameToken.value === 'self') {
const memberName = node.leftExpression.memberName.nameToken.value; const memberName = node.leftExpression.memberName.nameToken.value;
if (!SymbolUtils.isProtectedName(memberName) && if (!SymbolUtils.isProtectedName(memberName) &&
@ -342,7 +348,7 @@ export class TypeStubWriter extends ParseTreeWalker {
visitAugmentedAssignment(node: AugmentedAssignmentExpressionNode) { visitAugmentedAssignment(node: AugmentedAssignmentExpressionNode) {
if (this._classNestCount === 0 && this._functionNestCount === 0) { if (this._classNestCount === 0 && this._functionNestCount === 0) {
if (node.leftExpression instanceof NameNode) { if (node.leftExpression.nodeType === ParseNodeType.Name) {
if (node.leftExpression.nameToken.value === '__all__') { if (node.leftExpression.nameToken.value === '__all__') {
this._emitLine(this._printExpression(node, false, true)); this._emitLine(this._printExpression(node, false, true));
} }
@ -355,11 +361,11 @@ export class TypeStubWriter extends ParseTreeWalker {
visitTypeAnnotation(node: TypeAnnotationExpressionNode) { visitTypeAnnotation(node: TypeAnnotationExpressionNode) {
if (this._functionNestCount === 0) { if (this._functionNestCount === 0) {
let line = ''; let line = '';
if (node.valueExpression instanceof NameNode) { if (node.valueExpression.nodeType === ParseNodeType.Name) {
line = this._printExpression(node.valueExpression); line = this._printExpression(node.valueExpression);
} else if (node.valueExpression instanceof MemberAccessExpressionNode) { } else if (node.valueExpression.nodeType === ParseNodeType.MemberAccess) {
const baseExpression = node.valueExpression.leftExpression; const baseExpression = node.valueExpression.leftExpression;
if (baseExpression instanceof NameNode) { if (baseExpression.nodeType === ParseNodeType.Name) {
if (baseExpression.nameToken.value === 'self') { if (baseExpression.nameToken.value === 'self') {
const memberName = node.valueExpression.memberName.nameToken.value; const memberName = node.valueExpression.memberName.nameToken.value;
if (!SymbolUtils.isProtectedName(memberName) && if (!SymbolUtils.isProtectedName(memberName) &&
@ -438,7 +444,7 @@ export class TypeStubWriter extends ParseTreeWalker {
} }
visitStatementList(node: StatementListNode) { 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 // Is this the first statement in a suite? If it's a string
// literal, assume it's a doc string and emit it. // literal, assume it's a doc string and emit it.
if (!this._emittedSuite && this._emitDocString) { if (!this._emittedSuite && this._emitDocString) {
@ -449,7 +455,7 @@ export class TypeStubWriter extends ParseTreeWalker {
// Don't emit a doc string after the first statement. // Don't emit a doc string after the first statement.
this._emitDocString = false; this._emitDocString = false;
this.walkChildren(node); this.walkMultiple(node.statements);
return false; return false;
} }

View File

@ -71,14 +71,14 @@ export class TextRangeDiagnosticSink extends DiagnosticSink {
} }
addErrorWithTextRange(message: string, range: TextRange) { 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) { 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) { 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));
} }
} }

View File

@ -7,43 +7,44 @@
* Specifies the range of text within a larger string. * Specifies the range of text within a larger string.
*/ */
export class TextRange { export interface TextRange {
start: number; start: number;
length: number; length: number;
}
constructor(start: number, length: number) { export namespace TextRange {
export function create(start: number, length: number): TextRange {
if (start < 0) { if (start < 0) {
throw new Error('start must be non-negative'); throw new Error('start must be non-negative');
} }
if (length < 0) { if (length < 0) {
throw new Error('length must be non-negative'); throw new Error('length must be non-negative');
} }
this.start = start; return { start, length };
this.length = length;
} }
get end(): number { export function getEnd(range: TextRange): number {
return this.start + this.length; return range.start + range.length;
} }
contains(position: number): boolean { export function contains(range: TextRange, position: number): boolean {
return position >= this.start && position < this.end; return position >= range.start && position < getEnd(range);
} }
extend(range: TextRange | TextRange[] | undefined) { export function extend(range: TextRange, extension: TextRange | TextRange[] | undefined) {
if (range) { if (extension) {
if (Array.isArray(range)) { if (Array.isArray(extension)) {
range.forEach(r => { extension.forEach(r => {
this.extend(r); extend(range, r);
}); });
} else { } else {
if (range.start < this.start) { if (extension.start < range.start) {
this.length += this.start - range.start; range.length += range.start - extension.start;
this.start = range.start; range.start = extension.start;
} }
if (range.end > this.end) { if (getEnd(extension) > getEnd(range)) {
this.length += range.end - this.end; range.length += getEnd(extension) - getEnd(range);
} }
} }
} }

View File

@ -25,7 +25,8 @@ export class TextRangeCollection<T extends TextRange> {
} }
get end(): number { 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 { get length(): number {
@ -102,10 +103,13 @@ export class TextRangeCollection<T extends TextRange> {
const mid = Math.floor(min + (max - min) / 2); const mid = Math.floor(min + (max - min) / 2);
const item = this._items[mid]; const item = this._items[mid];
if (item.contains(position)) { if (TextRange.contains(item, position)) {
return mid; 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; return -1;
} }

View File

@ -9,8 +9,7 @@
*/ */
import { CompletionItem, CompletionItemKind, CompletionList, import { CompletionItem, CompletionItemKind, CompletionList,
MarkupKind, MarkupKind, TextEdit } from 'vscode-languageserver';
TextEdit } from 'vscode-languageserver';
import { ImportMap } from '../analyzer/analyzerFileInfo'; import { ImportMap } from '../analyzer/analyzerFileInfo';
import { AnalyzerNodeInfo } from '../analyzer/analyzerNodeInfo'; import { AnalyzerNodeInfo } from '../analyzer/analyzerNodeInfo';
@ -32,10 +31,10 @@ import { TextEditAction } from '../common/editAction';
import { getFileName, stripFileExtension } from '../common/pathUtils'; import { getFileName, stripFileExtension } from '../common/pathUtils';
import { convertPositionToOffset } from '../common/positionUtils'; import { convertPositionToOffset } from '../common/positionUtils';
import { StringUtils } from '../common/stringUtils'; import { StringUtils } from '../common/stringUtils';
import { TextRange } from '../common/textRange';
import { ErrorExpressionCategory, ErrorExpressionNode, import { ErrorExpressionCategory, ErrorExpressionNode,
ExpressionNode, ImportFromAsNode, ImportFromNode, ExpressionNode, ImportFromNode, isExpressionNode, ModuleNameNode,
MemberAccessExpressionNode, ModuleNameNode, ModuleNode, NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
ParseNode, StringListNode, SuiteNode } from '../parser/parseNodes';
import { ParseResults } from '../parser/parser'; import { ParseResults } from '../parser/parser';
import { TokenType } from '../parser/tokenizerTypes'; import { TokenType } from '../parser/tokenizerTypes';
@ -230,7 +229,7 @@ export class CompletionProvider {
// precendence. // precendence.
let errorNode: ParseNode | undefined = node; let errorNode: ParseNode | undefined = node;
while (errorNode) { while (errorNode) {
if (errorNode instanceof ErrorExpressionNode) { if (errorNode.nodeType === ParseNodeType.Error) {
break; break;
} }
@ -242,50 +241,50 @@ export class CompletionProvider {
let curNode = errorNode || node; let curNode = errorNode || node;
while (true) { while (true) {
// Don't offer completions inside of a string node. // Don't offer completions inside of a string node.
if (curNode instanceof StringListNode) { if (curNode.nodeType === ParseNodeType.StringList) {
return undefined; return undefined;
} }
if (curNode instanceof ModuleNameNode) { if (curNode.nodeType === ParseNodeType.ModuleName) {
return this._getImportModuleCompletions(curNode); return this._getImportModuleCompletions(curNode);
} }
if (curNode instanceof ErrorExpressionNode) { if (curNode.nodeType === ParseNodeType.Error) {
return this._getExpressionErrorCompletions(curNode, priorWord); return this._getExpressionErrorCompletions(curNode, priorWord);
} }
if (curNode instanceof MemberAccessExpressionNode) { if (curNode.nodeType === ParseNodeType.MemberAccess) {
return this._getMemberAccessCompletions(curNode.leftExpression, priorWord); 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 // Are we within a "from X import Y as Z" statement and
// more specifically within the "Y"? // more specifically within the "Y"?
if (curNode.parent instanceof ImportFromAsNode) { if (curNode.parent && curNode.parent.nodeType === ParseNodeType.ImportFromAs) {
const parentNode = curNode.parent.parent; const parentNode = curNode.parent.parent;
if (parentNode instanceof ImportFromNode) { if (parentNode && parentNode.nodeType === ParseNodeType.ImportFrom) {
if (curNode.parent.name === curNode) { if (curNode.parent.name === curNode) {
return this._getImportFromCompletions(parentNode, priorWord); return this._getImportFromCompletions(parentNode, priorWord);
} else { } else {
return this._getImportFromCompletions(parentNode, ''); return this._getImportFromCompletions(parentNode, '');
} }
} }
} else if (curNode.parent instanceof MemberAccessExpressionNode) { } else if (curNode.parent && curNode.parent.nodeType === ParseNodeType.MemberAccess) {
return this._getMemberAccessCompletions( return this._getMemberAccessCompletions(
curNode.parent.leftExpression, priorWord); curNode.parent.leftExpression, priorWord);
} }
} }
if (curNode instanceof ImportFromNode) { if (curNode.nodeType === ParseNodeType.ImportFrom) {
return this._getImportFromCompletions(curNode, priorWord); return this._getImportFromCompletions(curNode, priorWord);
} }
if (curNode instanceof ExpressionNode) { if (isExpressionNode(curNode)) {
return this._getExpressionCompletions(curNode, priorWord); 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); 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 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; return false;
} }
@ -342,7 +341,7 @@ export class CompletionProvider {
} }
case ErrorExpressionCategory.MissingMemberAccessName: { case ErrorExpressionCategory.MissingMemberAccessName: {
if (node.child instanceof ExpressionNode) { if (node.child && isExpressionNode(node.child)) {
return this._getMemberAccessCompletions(node.child, priorWord); return this._getMemberAccessCompletions(node.child, priorWord);
} }
break; break;
@ -482,7 +481,7 @@ export class CompletionProvider {
// Does an 'import from' statement already exist? If so, we'll reuse it. // Does an 'import from' statement already exist? If so, we'll reuse it.
const importStatement = importStatements.mapByFilePath[filePath]; const importStatement = importStatements.mapByFilePath[filePath];
if (importStatement && importStatement.node instanceof ImportFromNode) { if (importStatement && importStatement.node.nodeType === ParseNodeType.ImportFrom) {
return ImportStatementUtils.getTextEditsForAutoImportSymbolAddition( return ImportStatementUtils.getTextEditsForAutoImportSymbolAddition(
symbolName, importStatement, this._parseResults); 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 // If we're in the middle of a "from X import Y" statement, offer
// the "import" keyword as a completion. // 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 keyword = 'import';
const completionItem = CompletionItem.create(keyword); const completionItem = CompletionItem.create(keyword);
completionItem.kind = CompletionItemKind.Keyword; completionItem.kind = CompletionItemKind.Keyword;

View File

@ -15,7 +15,8 @@ import { ParseTreeUtils } from '../analyzer/parseTreeUtils';
import { DiagnosticTextPosition, DiagnosticTextRange, DocumentTextRange } from '../common/diagnostic'; import { DiagnosticTextPosition, DiagnosticTextRange, DocumentTextRange } from '../common/diagnostic';
import { isFile } from '../common/pathUtils'; import { isFile } from '../common/pathUtils';
import { convertPositionToOffset } from '../common/positionUtils'; 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'; import { ParseResults } from '../parser/parser';
const _startOfFilePosition: DiagnosticTextPosition = { line: 0, column: 0 }; const _startOfFilePosition: DiagnosticTextPosition = { line: 0, column: 0 };
@ -35,7 +36,7 @@ export class DefinitionProvider {
return undefined; return undefined;
} }
if (node instanceof ModuleNameNode) { if (node.nodeType === ParseNodeType.ModuleName) {
// If this is an imported module name, try to map the position // If this is an imported module name, try to map the position
// to the resolved import path. // to the resolved import path.
const importInfo = AnalyzerNodeInfo.getImportInfo(node); const importInfo = AnalyzerNodeInfo.getImportInfo(node);
@ -44,7 +45,7 @@ export class DefinitionProvider {
} }
const pathOffset = node.nameParts.findIndex(range => { const pathOffset = node.nameParts.findIndex(range => {
return offset >= range.start && offset < range.end; return offset >= range.start && offset < TextRange.getEnd(range);
}); });
if (pathOffset < 0) { if (pathOffset < 0) {

View File

@ -18,7 +18,8 @@ import { ClassType, FunctionType, ModuleType, OverloadedFunctionType,
Type, UnknownType } from '../analyzer/types'; Type, UnknownType } from '../analyzer/types';
import { DiagnosticTextPosition, DiagnosticTextRange } from '../common/diagnostic'; import { DiagnosticTextPosition, DiagnosticTextRange } from '../common/diagnostic';
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils'; 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'; import { ParseResults } from '../parser/parser';
export interface HoverTextPart { export interface HoverTextPart {
@ -49,11 +50,11 @@ export class HoverProvider {
parts: [], parts: [],
range: { range: {
start: convertOffsetToPosition(node.start, parseResults.lines), 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 // If this is an imported module name, try to map the position
// to the resolved import path. // to the resolved import path.
const importInfo = AnalyzerNodeInfo.getImportInfo(node); const importInfo = AnalyzerNodeInfo.getImportInfo(node);
@ -62,7 +63,7 @@ export class HoverProvider {
} }
let pathOffset = node.nameParts.findIndex(range => { let pathOffset = node.nameParts.findIndex(range => {
return offset >= range.start && offset < range.end; return offset >= range.start && offset < TextRange.getEnd(range);
}); });
if (pathOffset < 0) { if (pathOffset < 0) {
@ -104,7 +105,7 @@ export class HoverProvider {
switch (declaration.category) { switch (declaration.category) {
case DeclarationCategory.Variable: { case DeclarationCategory.Variable: {
if (node instanceof NameNode) { if (node.nodeType === ParseNodeType.Name) {
this._addResultsPart(results, '(variable) ' + node.nameToken.value + this._addResultsPart(results, '(variable) ' + node.nameToken.value +
this._getTypeText(node), true); this._getTypeText(node), true);
this._addDocumentationPart(results, node); this._addDocumentationPart(results, node);
@ -114,7 +115,7 @@ export class HoverProvider {
} }
case DeclarationCategory.Parameter: { case DeclarationCategory.Parameter: {
if (node instanceof NameNode) { if (node.nodeType === ParseNodeType.Name) {
this._addResultsPart(results, '(parameter) ' + node.nameToken.value + this._addResultsPart(results, '(parameter) ' + node.nameToken.value +
this._getTypeText(node), true); this._getTypeText(node), true);
this._addDocumentationPart(results, node); this._addDocumentationPart(results, node);
@ -124,7 +125,7 @@ export class HoverProvider {
} }
case DeclarationCategory.Class: { case DeclarationCategory.Class: {
if (node instanceof NameNode) { if (node.nodeType === ParseNodeType.Name) {
this._addResultsPart(results, '(class) ' + this._getTypeText(node), true); this._addResultsPart(results, '(class) ' + this._getTypeText(node), true);
this._addDocumentationPart(results, node); this._addDocumentationPart(results, node);
return results; return results;
@ -133,7 +134,7 @@ export class HoverProvider {
} }
case DeclarationCategory.Function: { case DeclarationCategory.Function: {
if (node instanceof NameNode) { if (node.nodeType === ParseNodeType.Name) {
this._addResultsPart(results, '(function) ' + node.nameToken.value + this._addResultsPart(results, '(function) ' + node.nameToken.value +
this._getTypeText(node), true); this._getTypeText(node), true);
this._addDocumentationPart(results, node); this._addDocumentationPart(results, node);
@ -143,7 +144,7 @@ export class HoverProvider {
} }
case DeclarationCategory.Method: { case DeclarationCategory.Method: {
if (node instanceof NameNode) { if (node.nodeType === ParseNodeType.Name) {
this._addResultsPart(results, '(method) ' + node.nameToken.value + this._addResultsPart(results, '(method) ' + node.nameToken.value +
this._getTypeText(node), true); this._getTypeText(node), true);
this._addDocumentationPart(results, node); this._addDocumentationPart(results, node);
@ -153,7 +154,7 @@ export class HoverProvider {
} }
case DeclarationCategory.Module: { case DeclarationCategory.Module: {
if (node instanceof NameNode) { if (node.nodeType === ParseNodeType.Name) {
this._addResultsPart(results, '(module) ' + node.nameToken.value, true); this._addResultsPart(results, '(module) ' + node.nameToken.value, true);
this._addDocumentationPart(results, node); this._addDocumentationPart(results, node);
return results; return results;
@ -164,12 +165,10 @@ export class HoverProvider {
} }
// If we had no declaration, see if we can provide a minimal tooltip. // If we had no declaration, see if we can provide a minimal tooltip.
if (node instanceof NameNode) { if (node.nodeType === ParseNodeType.Name) {
if (node instanceof NameNode) { this._addResultsPart(results, node.nameToken.value + this._getTypeText(node), true);
this._addResultsPart(results, node.nameToken.value + this._getTypeText(node), true); this._addDocumentationPart(results, node);
this._addDocumentationPart(results, node); return results;
return results;
}
} }
return undefined; return undefined;

View File

@ -13,7 +13,8 @@ import { ImportStatement, ImportStatementUtils } from '../analyzer/importStateme
import { DiagnosticTextRange } from '../common/diagnostic'; import { DiagnosticTextRange } from '../common/diagnostic';
import { TextEditAction } from '../common/editAction'; import { TextEditAction } from '../common/editAction';
import { convertOffsetToPosition } from '../common/positionUtils'; 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'; import { ParseResults } from '../parser/parser';
const _maxLineLength = 80; const _maxLineLength = 80;
@ -99,11 +100,12 @@ export class ImportSorter {
statementLimit = statements.length; statementLimit = statements.length;
} }
const lastStatement = statements[statementLimit - 1].node;
return { return {
start: convertOffsetToPosition( start: convertOffsetToPosition(
statements[0].node.start, this._parseResults.lines), statements[0].node.start, this._parseResults.lines),
end: convertOffsetToPosition( 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, statements[secondaryBlockStart].node.start,
this._parseResults.lines), this._parseResults.lines),
end: convertOffsetToPosition( end: convertOffsetToPosition(
statements[secondaryBlockLimit - 1].node.end, TextRange.getEnd(statements[secondaryBlockLimit - 1].node),
this._parseResults.lines) this._parseResults.lines)
}, },
replacementText: '' replacementText: ''
@ -154,7 +156,7 @@ export class ImportSorter {
} }
let importLine: string; let importLine: string;
if (statement.node instanceof ImportNode) { if (statement.node.nodeType === ParseNodeType.Import) {
importLine = this._formatImportNode(statement.subnode!, importLine = this._formatImportNode(statement.subnode!,
statement.moduleName); statement.moduleName);
} else { } else {

View File

@ -12,7 +12,8 @@ import { ImportStatementUtils } from '../analyzer/importStatementUtils';
import { ParseTreeUtils } from '../analyzer/parseTreeUtils'; import { ParseTreeUtils } from '../analyzer/parseTreeUtils';
import { TextEditAction } from '../common/editAction'; import { TextEditAction } from '../common/editAction';
import { convertOffsetToPosition } from '../common/positionUtils'; 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 { ParseResults } from '../parser/parser';
import { commandAddMissingOptionalToParam, commandOrderImports } from './commands'; import { commandAddMissingOptionalToParam, commandOrderImports } from './commands';
import { ImportSorter } from './importSorter'; import { ImportSorter } from './importSorter';
@ -40,7 +41,7 @@ function _addMissingOptionalToParam(parseResults: ParseResults,
let node: ParseNode | undefined = ParseTreeUtils.findNodeByOffset(parseResults.parseTree, offset); let node: ParseNode | undefined = ParseTreeUtils.findNodeByOffset(parseResults.parseTree, offset);
while (node) { while (node) {
if (node instanceof ParameterNode) { if (node.nodeType === ParseNodeType.Parameter) {
break; break;
} }
@ -56,7 +57,7 @@ function _addMissingOptionalToParam(parseResults: ParseResults,
const startPos = convertOffsetToPosition( const startPos = convertOffsetToPosition(
node.typeAnnotation.start, parseResults.lines); node.typeAnnotation.start, parseResults.lines);
const endPos = convertOffsetToPosition( const endPos = convertOffsetToPosition(
node.typeAnnotation.end, parseResults.lines); TextRange.getEnd(node.typeAnnotation), parseResults.lines);
editActions.push({ editActions.push({
range: { start: startPos, end: startPos }, range: { start: startPos, end: startPos },
@ -74,7 +75,7 @@ function _addMissingOptionalToParam(parseResults: ParseResults,
imp => imp.moduleName === 'typing'); imp => imp.moduleName === 'typing');
// If there's an existing import statement, insert into it. // 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( const additionalEditActions = ImportStatementUtils.getTextEditsForAutoImportSymbolAddition(
'Optional', importStatement, parseResults); 'Optional', importStatement, parseResults);
editActions.push(...additionalEditActions); editActions.push(...additionalEditActions);

View File

@ -15,6 +15,7 @@ import { ParseTreeWalker } from '../analyzer/parseTreeWalker';
import { Symbol } from '../analyzer/symbol'; import { Symbol } from '../analyzer/symbol';
import { DiagnosticTextPosition, DocumentTextRange } from '../common/diagnostic'; import { DiagnosticTextPosition, DocumentTextRange } from '../common/diagnostic';
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils'; import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
import { TextRange } from '../common/textRange';
import { NameNode, ParseNode } from '../parser/parseNodes'; import { NameNode, ParseNode } from '../parser/parseNodes';
import { ParseResults } from '../parser/parser'; import { ParseResults } from '../parser/parser';
@ -75,7 +76,7 @@ class FindReferencesTreeWalker extends ParseTreeWalker {
path: this._filePath, path: this._filePath,
range: { range: {
start: convertOffsetToPosition(node.start, this._parseResults.lines), start: convertOffsetToPosition(node.start, this._parseResults.lines),
end: convertOffsetToPosition(node.end, this._parseResults.lines) end: convertOffsetToPosition(TextRange.getEnd(node), this._parseResults.lines)
} }
}); });
} }

View File

@ -16,7 +16,8 @@ import { ClassType, FunctionType, ObjectType,
import { ClassMemberLookupFlags, TypeUtils } from '../analyzer/typeUtils'; import { ClassMemberLookupFlags, TypeUtils } from '../analyzer/typeUtils';
import { DiagnosticTextPosition } from '../common/diagnostic'; import { DiagnosticTextPosition } from '../common/diagnostic';
import { convertPositionToOffset } from '../common/positionUtils'; 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'; import { ParseResults } from '../parser/parser';
export interface ParamInfo { export interface ParamInfo {
@ -74,7 +75,7 @@ export class SignatureHelpProvider {
return undefined; return undefined;
} }
if (offset > callNode.end) { if (offset > TextRange.getEnd(callNode)) {
return undefined; return undefined;
} }
@ -87,7 +88,7 @@ export class SignatureHelpProvider {
let activeParameter = 0; let activeParameter = 0;
const args = callNode.arguments; const args = callNode.arguments;
for (let i = args.length - 1; i >= 0; i--) { 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; activeParameter = i + 1;
break; break;
} }
@ -193,7 +194,7 @@ export class SignatureHelpProvider {
let curNode: ParseNode | undefined = node; let curNode: ParseNode | undefined = node;
while (curNode !== undefined) { while (curNode !== undefined) {
if (curNode instanceof CallExpressionNode) { if (curNode.nodeType === ParseNodeType.Call) {
return curNode; return curNode;
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -405,7 +405,7 @@ export class Tokenizer {
private _addLineRange() { private _addLineRange() {
const lineLength = this._cs.position - this._prevLineStart; const lineLength = this._cs.position - this._prevLineStart;
if (lineLength > 0) { if (lineLength > 0) {
this._lineRanges.push(new TextRange(this._prevLineStart, lineLength)); this._lineRanges.push({ start: this._prevLineStart, length: lineLength });
} }
this._prevLineStart = this._cs.position; this._prevLineStart = this._cs.position;

View File

@ -158,25 +158,29 @@ export enum StringTokenFlags {
Unterminated = 0x1000 Unterminated = 0x1000
} }
export class Comment extends TextRange { export class Comment {
readonly value: string; readonly value: string;
readonly range: TextRange;
constructor(start: number, length: number, value: string) { constructor(start: number, length: number, value: string) {
super(start, length); this.range = { start, length };
this.value = value; this.value = value;
} }
} }
export class Token extends TextRange implements Token { export class Token implements TextRange {
readonly type: TokenType; readonly type: TokenType;
// Comments prior to the token. // Comments prior to the token.
readonly start: number;
readonly length: number;
readonly comments?: Comment[]; readonly comments?: Comment[];
constructor(type: TokenType, start: number, length: number, constructor(type: TokenType, start: number, length: number,
comments: Comment[] | undefined) { comments: Comment[] | undefined) {
super(start, length); this.start = start;
this.length = length;
this.type = type; this.type = type;
this.comments = comments; this.comments = comments;
} }

View File

@ -7,19 +7,20 @@
import * as assert from 'assert'; import * as assert from 'assert';
import { ParseTreeWalker } from '../analyzer/parseTreeWalker'; 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 { export class TestWalker extends ParseTreeWalker {
constructor() { constructor() {
super(); super();
} }
visitNode(node: ParseNode): boolean { visitNode(node: ParseNode) {
const children = node.getChildren(); const children = super.visitNode(node);
this._verifyParentChildLinks(node, children); this._verifyParentChildLinks(node, children);
this._verifyChildRanges(node, children); this._verifyChildRanges(node, children);
return super.visitNode(node); return children;
} }
// Make sure that all of the children point to their parent. // 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 // There are a few exceptions we need to deal with here. Comment
// annotations can occur outside of an assignment node's range. // annotations can occur outside of an assignment node's range.
if (node instanceof AssignmentNode) { if (node.nodeType === ParseNodeType.Assignment) {
if (child === node.typeAnnotationComment) { if (child === node.typeAnnotationComment) {
skipCheck = true; skipCheck = true;
} }
} }
if (node instanceof StringListNode) { if (node.nodeType === ParseNodeType.StringList) {
if (child === node.typeAnnotation) { if (child === node.typeAnnotation) {
skipCheck = true; skipCheck = true;
} }
@ -58,10 +59,10 @@ export class TestWalker extends ParseTreeWalker {
if (!skipCheck) { if (!skipCheck) {
// Make sure the child is contained within the parent. // 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) { if (prevNode) {
// Make sure the child is after the previous child. // Make sure the child is after the previous child.
assert(child.start >= prevNode.end); assert(child.start >= TextRange.getEnd(prevNode));
} }
prevNode = child; prevNode = child;