Avoid creating code flow nodes for expressions that are not supported.

This commit is contained in:
Eric Traut 2019-11-16 10:07:12 -08:00
parent 676a4b4b76
commit f95ed3c705
3 changed files with 43 additions and 36 deletions

View File

@ -37,7 +37,8 @@ import { KeywordType, OperatorType } from '../parser/tokenizerTypes';
import { AnalyzerFileInfo } from './analyzerFileInfo';
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
import { FlowAssignment, FlowAssignmentAlias, FlowCall, FlowCondition, FlowFlags, FlowLabel,
FlowNode, FlowPostFinally, FlowPreFinallyGate, FlowWildcardImport, getUniqueFlowNodeId } from './codeFlow';
FlowNode, FlowPostFinally, FlowPreFinallyGate, FlowWildcardImport, getUniqueFlowNodeId,
isCodeFlowSupportedForReference } from './codeFlow';
import { AliasDeclaration, ClassDeclaration, DeclarationType, FunctionDeclaration,
IntrinsicType, ModuleLoaderActions, VariableDeclaration } from './declaration';
import { ImplicitImport, ImportResult, ImportType } from './importResult';
@ -1480,7 +1481,7 @@ export class Binder extends ParseTreeWalker {
switch (expression.nodeType) {
case ParseNodeType.Name:
case ParseNodeType.MemberAccess: {
return true;
return isCodeFlowSupportedForReference(expression);
}
case ParseNodeType.BinaryOperation: {
@ -1599,7 +1600,7 @@ export class Binder extends ParseTreeWalker {
}
const prevFlowNode = this._currentFlowNode;
if (!this._isCodeUnreachable()) {
if (!this._isCodeUnreachable() && isCodeFlowSupportedForReference(node)) {
const flowNode: FlowAssignment = {
flags: FlowFlags.Assignment,
id: getUniqueFlowNodeId(),

View File

@ -13,7 +13,10 @@
* TypeScript compiler.
*/
import { CallNode, ExpressionNode, ImportFromNode, MemberAccessNode, NameNode } from '../parser/parseNodes';
import * as assert from 'assert';
import { CallNode, ExpressionNode, ImportFromNode, MemberAccessNode, NameNode,
ParseNodeType } from '../parser/parseNodes';
export enum FlowFlags {
Unreachable = 1 << 0, // Unreachable code
@ -100,3 +103,35 @@ export interface FlowPostFinally extends FlowNode {
antecedent: FlowNode;
preFinallyGate: FlowPreFinallyGate;
}
export function isCodeFlowSupportedForReference(reference: ExpressionNode): boolean {
if (reference.nodeType === ParseNodeType.Name) {
return true;
}
if (reference.nodeType === ParseNodeType.MemberAccess) {
return isCodeFlowSupportedForReference(reference.leftExpression);
}
return false;
}
export function createKeyForReference(reference: NameNode | MemberAccessNode,
targetSymbolId: number): string {
let key;
if (reference.nodeType === ParseNodeType.Name) {
key = reference.nameToken.value;
} else {
key = reference.memberName.nameToken.value;
let leftNode = reference.leftExpression;
while (leftNode.nodeType === ParseNodeType.MemberAccess) {
key = leftNode.memberName.nameToken.value + '.' + key;
leftNode = leftNode.leftExpression;
}
assert(leftNode.nodeType === ParseNodeType.Name);
key = (leftNode as NameNode).nameToken.value + '.' + key;
}
return key + '.' + targetSymbolId.toString();
}

View File

@ -33,8 +33,9 @@ import { ArgumentCategory, AssignmentNode, AugmentedAssignmentNode, BinaryOperat
import { KeywordType, OperatorType, StringTokenFlags, TokenType } from '../parser/tokenizerTypes';
import { AnalyzerFileInfo, ImportLookup, ImportLookupResult } from './analyzerFileInfo';
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
import { FlowAssignment, FlowAssignmentAlias, FlowCall, FlowCondition, FlowFlags, FlowLabel,
FlowNode, FlowPostFinally, FlowPreFinallyGate, FlowWildcardImport } from './codeFlow';
import { createKeyForReference, FlowAssignment, FlowAssignmentAlias, FlowCall, FlowCondition, FlowFlags,
FlowLabel, FlowNode, FlowPostFinally, FlowPreFinallyGate, FlowWildcardImport,
isCodeFlowSupportedForReference } from './codeFlow';
import { AliasDeclaration, Declaration, DeclarationType, FunctionDeclaration,
ModuleLoaderActions, VariableDeclaration } from './declaration';
import * as ParseTreeUtils from './parseTreeUtils';
@ -6537,18 +6538,6 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
return undefined;
}
function isCodeFlowSupportedForReference(reference: ExpressionNode): boolean {
if (reference.nodeType === ParseNodeType.Name) {
return true;
}
if (reference.nodeType === ParseNodeType.MemberAccess) {
return isCodeFlowSupportedForReference(reference.leftExpression);
}
return false;
}
function getTypeFromWildcardImport(flowNode: FlowWildcardImport, name: string): Type {
const importInfo = AnalyzerNodeInfo.getImportInfo(flowNode.node.module);
assert(importInfo && importInfo.isImportFound);
@ -6627,24 +6616,6 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
return analyzer.getFlowType(reference, targetSymbolId, initialType);
}
function createKeyForReference(reference: NameNode | MemberAccessNode, targetSymbolId: number): string {
let key;
if (reference.nodeType === ParseNodeType.Name) {
key = reference.nameToken.value;
} else {
key = reference.memberName.nameToken.value;
let leftNode = reference.leftExpression;
while (leftNode.nodeType === ParseNodeType.MemberAccess) {
key = leftNode.memberName.nameToken.value + '.' + key;
leftNode = leftNode.leftExpression;
}
assert(leftNode.nodeType === ParseNodeType.Name);
key = (leftNode as NameNode).nameToken.value + '.' + key;
}
return key + '.' + targetSymbolId.toString();
}
// Creates a new code flow analyzer that can be used to narrow the types
// of the expressions within an execution context. Each code flow analyzer
// instance maintains a cache of types it has already determined.