From 69980ab8ddf05377bc078d56f1ec6403fd6e8cdd Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Sat, 16 Nov 2019 23:17:08 -0800 Subject: [PATCH] Fixed crashing bug in type stub writer. --- server/src/analyzer/analyzerNodeInfo.ts | 18 ++++++++++++++++- server/src/analyzer/checker.ts | 20 ++----------------- server/src/analyzer/service.ts | 20 ++++++++++++++++--- server/src/analyzer/typeStubWriter.ts | 20 ++++++++++++++++--- .../src/languageService/referencesProvider.ts | 18 +---------------- 5 files changed, 54 insertions(+), 42 deletions(-) diff --git a/server/src/analyzer/analyzerNodeInfo.ts b/server/src/analyzer/analyzerNodeInfo.ts index af53bdc6f..8e23acb14 100644 --- a/server/src/analyzer/analyzerNodeInfo.ts +++ b/server/src/analyzer/analyzerNodeInfo.ts @@ -13,7 +13,7 @@ import { ClassNode, FunctionNode, LambdaNode, ListComprehensionNode, ModuleNode, ParseNode } from '../parser/parseNodes'; import { AnalyzerFileInfo } from './analyzerFileInfo'; -import { FlowNode } from './codeFlow'; +import { FlowFlags, FlowNode } from './codeFlow'; import { Declaration } from './declaration'; import { ImportResult } from './importResult'; import { Scope } from './scope'; @@ -119,3 +119,19 @@ export function setFileInfo(node: ModuleNode, fileInfo: AnalyzerFileInfo) { const analyzerNode = node as AnalyzerNodeInfo; analyzerNode.fileInfo = fileInfo; } + +export function isCodeUnreachable(node: ParseNode): boolean { + let curNode: ParseNode | undefined = node; + + // Walk up the parse tree until we find a node with + // an associated flow node. + while (curNode) { + const flowNode = getFlowNode(curNode); + if (flowNode) { + return !!(flowNode.flags & FlowFlags.Unreachable); + } + curNode = curNode.parent; + } + + return false; +} diff --git a/server/src/analyzer/checker.ts b/server/src/analyzer/checker.ts index 3a733f139..67658fd37 100644 --- a/server/src/analyzer/checker.ts +++ b/server/src/analyzer/checker.ts @@ -70,7 +70,7 @@ export class Checker extends ParseTreeWalker { } walk(node: ParseNode) { - if (!this._isCodeUnreachable(node)) { + if (!AnalyzerNodeInfo.isCodeUnreachable(node)) { super.walk(node); } } @@ -991,7 +991,7 @@ export class Checker extends ParseTreeWalker { }; suiteNode.statements.forEach(statement => { - if (!this._isCodeUnreachable(statement)) { + if (!AnalyzerNodeInfo.isCodeUnreachable(statement)) { if (statement.nodeType === ParseNodeType.StatementList) { for (const substatement of statement.statements) { if (substatement.nodeType !== ParseNodeType.TypeAnnotation && @@ -1009,22 +1009,6 @@ export class Checker extends ParseTreeWalker { }); } - private _isCodeUnreachable(node: ParseNode): boolean { - let curNode: ParseNode | undefined = node; - - // Walk up the parse tree until we find a node with - // an associated flow node. - while (curNode) { - const flowNode = AnalyzerNodeInfo.getFlowNode(curNode); - if (flowNode) { - return !!(flowNode.flags & FlowFlags.Unreachable); - } - curNode = curNode.parent; - } - - return false; - } - private _validateFunctionReturn(node: FunctionNode, functionType: FunctionType) { // Stub files are allowed not to return an actual value, // so skip this if it's a stub file. diff --git a/server/src/analyzer/service.ts b/server/src/analyzer/service.ts index b8459a57d..23d7542cd 100644 --- a/server/src/analyzer/service.ts +++ b/server/src/analyzer/service.ts @@ -571,9 +571,23 @@ export class AnalyzerService { // don't include those. const resolvedPath = importResult.resolvedPaths[ importResult.resolvedPaths.length - 1]; - this._typeStubTargetPath = importResult.resolvedPaths[0]; - if (isFile(this._typeStubTargetPath)) { - this._typeStubTargetPath = getDirectoryPath(this._typeStubTargetPath); + + // Get the directory that contains the root package. + let targetPath = getDirectoryPath(resolvedPath); + for (let i = importResult.resolvedPaths.length - 2; i >= 0; i--) { + const resolvedPath = importResult.resolvedPaths[i]; + if (resolvedPath) { + targetPath = getDirectoryPath(resolvedPath); + } else { + // If there was no file corresponding to this portion + // of the name path, assume that it's contained + // within its parent directory. + targetPath = getDirectoryPath(targetPath); + } + } + + if (isDirectory(targetPath)) { + this._typeStubTargetPath = targetPath; } if (!resolvedPath) { diff --git a/server/src/analyzer/typeStubWriter.ts b/server/src/analyzer/typeStubWriter.ts index 444c00e27..cb15d4dda 100644 --- a/server/src/analyzer/typeStubWriter.ts +++ b/server/src/analyzer/typeStubWriter.ts @@ -13,8 +13,9 @@ import * as fs from 'fs'; import { ArgumentCategory, ArgumentNode, AssignmentNode, AugmentedAssignmentNode, ClassNode, DecoratorNode, ExpressionNode, ForNode, FunctionNode, IfNode, ImportFromNode, ImportNode, ModuleNameNode, NameNode, ParameterCategory, ParameterNode, - ParseNodeType, StatementListNode, StringNode, TryNode, TypeAnnotationNode, - WhileNode, WithNode } from '../parser/parseNodes'; + ParseNode, ParseNodeType, StatementListNode, StringNode, TryNode, + TypeAnnotationNode, WhileNode, WithNode } from '../parser/parseNodes'; +import * as AnalyzerNodeInfo from './analyzerNodeInfo'; import * as ParseTreeUtils from './parseTreeUtils'; import { ParseTreeWalker } from './parseTreeWalker'; import { getScopeForNode } from './scopeUtils'; @@ -47,10 +48,11 @@ interface TrackedImportSymbol { } class TrackedImportFrom extends TrackedImport { + symbols: TrackedImportSymbol[] = []; + constructor(importName: string, public isWildcardImport: boolean, public node?: ImportFromNode) { super(importName); } - symbols: TrackedImportSymbol[] = []; addSymbol(symbol: Symbol | undefined, name: string, alias: string | undefined, isAccessed = false) { @@ -78,6 +80,12 @@ class ImportSymbolWalker extends ParseTreeWalker { this.walk(node); } + walk(node: ParseNode) { + if (!AnalyzerNodeInfo.isCodeUnreachable(node)) { + super.walk(node); + } + } + visitName(node: NameNode) { this._accessedImportedSymbols.set(node.value, true); return true; @@ -129,6 +137,12 @@ export class TypeStubWriter extends ParseTreeWalker { this._writeFile(); } + walk(node: ParseNode) { + if (!AnalyzerNodeInfo.isCodeUnreachable(node)) { + super.walk(node); + } + } + visitClass(node: ClassNode) { const className = node.name.value; diff --git a/server/src/languageService/referencesProvider.ts b/server/src/languageService/referencesProvider.ts index 3bcd7110c..3ee8e099f 100644 --- a/server/src/languageService/referencesProvider.ts +++ b/server/src/languageService/referencesProvider.ts @@ -52,7 +52,7 @@ class FindReferencesTreeWalker extends ParseTreeWalker { } walk(node: ParseNode) { - if (!this._isCodeUnreachable(node)) { + if (!AnalyzerNodeInfo.isCodeUnreachable(node)) { super.walk(node); } } @@ -90,22 +90,6 @@ class FindReferencesTreeWalker extends ParseTreeWalker { return this._referencesResult.declarations.some(decl => DeclarationUtils.areDeclarationsSame(decl, resolvedDecl)); } - - private _isCodeUnreachable(node: ParseNode): boolean { - let curNode: ParseNode | undefined = node; - - // Walk up the parse tree until we find a node with - // an associated flow node. - while (curNode) { - const flowNode = AnalyzerNodeInfo.getFlowNode(curNode); - if (flowNode) { - return !!(flowNode.flags & FlowFlags.Unreachable); - } - curNode = curNode.parent; - } - - return false; - } } export class ReferencesProvider {