Skip to content

[Transforms] Merge master on 08/05 #10182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Aug 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 54 additions & 32 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ namespace ts {
const flowLoopKeys: string[] = [];
const flowLoopTypes: Type[][] = [];
const visitedFlowNodes: FlowNode[] = [];
const visitedFlowTypes: Type[] = [];
const visitedFlowTypes: FlowType[] = [];
const potentialThisCollisions: Node[] = [];
const awaitedTypeStack: number[] = [];

Expand Down Expand Up @@ -1136,6 +1136,10 @@ namespace ts {
else {
symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text);
}
// If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default
if (!symbolFromVariable && allowSyntheticDefaultImports && name.text === "default") {
symbolFromVariable = resolveExternalModuleSymbol(moduleSymbol) || resolveSymbol(moduleSymbol);
}
// if symbolFromVariable is export - get its final target
symbolFromVariable = resolveSymbol(symbolFromVariable);
const symbolFromModule = getExportOfModule(targetSymbol, name.text);
Expand Down Expand Up @@ -8094,21 +8098,33 @@ namespace ts {
f(type) ? type : neverType;
}

function isIncomplete(flowType: FlowType) {
return flowType.flags === 0;
}

function getTypeFromFlowType(flowType: FlowType) {
return flowType.flags === 0 ? (<IncompleteType>flowType).type : <Type>flowType;
}

function createFlowType(type: Type, incomplete: boolean): FlowType {
return incomplete ? { flags: 0, type } : type;
}

function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) {
let key: string;
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
return declaredType;
}
const initialType = assumeInitialized ? declaredType : includeFalsyTypes(declaredType, TypeFlags.Undefined);
const visitedFlowStart = visitedFlowCount;
const result = getTypeAtFlowNode(reference.flowNode);
const result = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode));
visitedFlowCount = visitedFlowStart;
if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(result, TypeFacts.NEUndefinedOrNull) === neverType) {
return declaredType;
}
return result;

function getTypeAtFlowNode(flow: FlowNode): Type {
function getTypeAtFlowNode(flow: FlowNode): FlowType {
while (true) {
if (flow.flags & FlowFlags.Shared) {
// We cache results of flow type resolution for shared nodes that were previously visited in
Expand All @@ -8120,7 +8136,7 @@ namespace ts {
}
}
}
let type: Type;
let type: FlowType;
if (flow.flags & FlowFlags.Assignment) {
type = getTypeAtFlowAssignment(<FlowAssignment>flow);
if (!type) {
Expand Down Expand Up @@ -8188,41 +8204,44 @@ namespace ts {
return undefined;
}

function getTypeAtFlowCondition(flow: FlowCondition) {
let type = getTypeAtFlowNode(flow.antecedent);
function getTypeAtFlowCondition(flow: FlowCondition): FlowType {
const flowType = getTypeAtFlowNode(flow.antecedent);
let type = getTypeFromFlowType(flowType);
if (type !== neverType) {
// If we have an antecedent type (meaning we're reachable in some way), we first
// attempt to narrow the antecedent type. If that produces the nothing type, then
// we take the type guard as an indication that control could reach here in a
// manner not understood by the control flow analyzer (e.g. a function argument
// has an invalid type, or a nested function has possibly made an assignment to a
// captured variable). We proceed by reverting to the declared type and then
// attempt to narrow the antecedent type. If that produces the never type, and if
// the antecedent type is incomplete (i.e. a transient type in a loop), then we
// take the type guard as an indication that control *could* reach here once we
// have the complete type. We proceed by reverting to the declared type and then
// narrow that.
const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0;
type = narrowType(type, flow.expression, assumeTrue);
if (type === neverType) {
if (type === neverType && isIncomplete(flowType)) {
type = narrowType(declaredType, flow.expression, assumeTrue);
}
}
return type;
return createFlowType(type, isIncomplete(flowType));
}

function getTypeAtSwitchClause(flow: FlowSwitchClause) {
const type = getTypeAtFlowNode(flow.antecedent);
function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType {
const flowType = getTypeAtFlowNode(flow.antecedent);
let type = getTypeFromFlowType(flowType);
const expr = flow.switchStatement.expression;
if (isMatchingReference(reference, expr)) {
return narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
}
if (isMatchingPropertyAccess(expr)) {
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
else if (isMatchingPropertyAccess(expr)) {
type = narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
}
return type;
return createFlowType(type, isIncomplete(flowType));
}

function getTypeAtFlowBranchLabel(flow: FlowLabel) {
function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType {
const antecedentTypes: Type[] = [];
let seenIncomplete = false;
for (const antecedent of flow.antecedents) {
const type = getTypeAtFlowNode(antecedent);
const flowType = getTypeAtFlowNode(antecedent);
const type = getTypeFromFlowType(flowType);
// If the type at a particular antecedent path is the declared type and the
// reference is known to always be assigned (i.e. when declared and initial types
// are the same), there is no reason to process more antecedents since the only
Expand All @@ -8233,11 +8252,14 @@ namespace ts {
if (!contains(antecedentTypes, type)) {
antecedentTypes.push(type);
}
if (isIncomplete(flowType)) {
seenIncomplete = true;
}
}
return getUnionType(antecedentTypes);
return createFlowType(getUnionType(antecedentTypes), seenIncomplete);
}

function getTypeAtFlowLoopLabel(flow: FlowLabel) {
function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType {
// If we have previously computed the control flow type for the reference at
// this flow loop junction, return the cached type.
const id = getFlowNodeId(flow);
Expand All @@ -8249,12 +8271,12 @@ namespace ts {
return cache[key];
}
// If this flow loop junction and reference are already being processed, return
// the union of the types computed for each branch so far. We should never see
// an empty array here because the first antecedent of a loop junction is always
// the non-looping control flow path that leads to the top.
// the union of the types computed for each branch so far, marked as incomplete.
// We should never see an empty array here because the first antecedent of a loop
// junction is always the non-looping control flow path that leads to the top.
for (let i = flowLoopStart; i < flowLoopCount; i++) {
if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key) {
return getUnionType(flowLoopTypes[i]);
return createFlowType(getUnionType(flowLoopTypes[i]), /*incomplete*/ true);
}
}
// Add the flow loop junction and reference to the in-process stack and analyze
Expand All @@ -8265,7 +8287,7 @@ namespace ts {
flowLoopTypes[flowLoopCount] = antecedentTypes;
for (const antecedent of flow.antecedents) {
flowLoopCount++;
const type = getTypeAtFlowNode(antecedent);
const type = getTypeFromFlowType(getTypeAtFlowNode(antecedent));
flowLoopCount--;
// If we see a value appear in the cache it is a sign that control flow analysis
// was restarted and completed by checkExpressionCached. We can simply pick up
Expand Down Expand Up @@ -10068,7 +10090,7 @@ namespace ts {
for (const prop of props) {
// Is there a corresponding property in the element attributes type? Skip checking of properties
// that have already been assigned to, as these are not actually pushed into the resulting type
if (!nameTable[prop.name]) {
if (!hasProperty(nameTable, prop.name)) {
const targetPropSym = getPropertyOfType(elementAttributesType, prop.name);
if (targetPropSym) {
const msg = chainDiagnosticMessages(undefined, Diagnostics.Property_0_of_JSX_spread_attribute_is_not_assignable_to_target_property, prop.name);
Expand Down Expand Up @@ -10414,7 +10436,7 @@ namespace ts {
const targetProperties = getPropertiesOfType(targetAttributesType);
for (let i = 0; i < targetProperties.length; i++) {
if (!(targetProperties[i].flags & SymbolFlags.Optional) &&
nameTable[targetProperties[i].name] === undefined) {
!hasProperty(nameTable, targetProperties[i].name)) {

error(node, Diagnostics.Property_0_is_missing_in_type_1, targetProperties[i].name, typeToString(targetAttributesType));
}
Expand Down Expand Up @@ -19845,7 +19867,7 @@ namespace ts {
}

function checkGrammarTopLevelElementForRequiredDeclareModifier(node: Node): boolean {
// A declare modifier is required for any top level .d.ts declaration except export=, export default,
// A declare modifier is required for any top level .d.ts declaration except export=, export default, export as namespace
// interfaces and imports categories:
//
// DeclarationElement:
Expand All @@ -19863,8 +19885,8 @@ namespace ts {
node.kind === SyntaxKind.ImportEqualsDeclaration ||
node.kind === SyntaxKind.ExportDeclaration ||
node.kind === SyntaxKind.ExportAssignment ||
node.kind === SyntaxKind.NamespaceExportDeclaration ||
getModifierFlags(node) & (ModifierFlags.Ambient | ModifierFlags.Export | ModifierFlags.Default)) {

return false;
}

Expand Down
8 changes: 6 additions & 2 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,8 +499,12 @@ namespace ts {
return keys;
}

export function getProperty<T>(map: Map<T>, key: string): T {
return hasOwnProperty.call(map, key) ? map[key] : undefined;
export function getProperty<T>(map: Map<T>, key: string): T | undefined {
return hasProperty(map, key) ? map[key] : undefined;
}

export function getOrUpdateProperty<T>(map: Map<T>, key: string, makeValue: () => T): T {
return hasProperty(map, key) ? map[key] : map[key] = makeValue();
}

export function isEmpty<T>(map: Map<T>) {
Expand Down
19 changes: 9 additions & 10 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1056,19 +1056,15 @@ namespace ts {
return resolutions;
}

function getInferredTypesRoot(options: CompilerOptions, rootFiles: string[], host: CompilerHost) {
return computeCommonSourceDirectoryOfFilenames(rootFiles, host.getCurrentDirectory(), f => host.getCanonicalFileName(f));
}

/**
* Given a set of options and a set of root files, returns the set of type directive names
* Given a set of options, returns the set of type directive names
* that should be included for this program automatically.
* This list could either come from the config file,
* or from enumerating the types root + initial secondary types lookup location.
* More type directives might appear in the program later as a result of loading actual source files;
* this list is only the set of defaults that are implicitly included.
*/
export function getAutomaticTypeDirectiveNames(options: CompilerOptions, rootFiles: string[], host: CompilerHost): string[] {
export function getAutomaticTypeDirectiveNames(options: CompilerOptions, host: ModuleResolutionHost): string[] {
// Use explicit type list from tsconfig.json
if (options.types) {
return options.types;
Expand All @@ -1081,7 +1077,10 @@ namespace ts {
if (typeRoots) {
for (const root of typeRoots) {
if (host.directoryExists(root)) {
result = result.concat(host.getDirectories(root));
for (const typeDirectivePath of host.getDirectories(root)) {
// Return just the type directive names
result = result.concat(getBaseFileName(normalizePath(typeDirectivePath)));
}
}
}
}
Expand Down Expand Up @@ -1156,11 +1155,11 @@ namespace ts {
forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false));

// load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
const typeReferences: string[] = getAutomaticTypeDirectiveNames(options, rootNames, host);
const typeReferences: string[] = getAutomaticTypeDirectiveNames(options, host);

if (typeReferences) {
const inferredRoot = getInferredTypesRoot(options, rootNames, host);
const containingFilename = combinePaths(inferredRoot, "__inferred type names__.ts");
// This containingFilename needs to match with the one used in managed-side
const containingFilename = combinePaths(host.getCurrentDirectory(), "__inferred type names__.ts");
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename);
for (let i = 0; i < typeReferences.length; i++) {
processTypeReferenceDirective(typeReferences[i], resolutions[i]);
Expand Down
11 changes: 11 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1680,6 +1680,16 @@ namespace ts {
antecedent: FlowNode;
}

export type FlowType = Type | IncompleteType;

// Incomplete types occur during control flow analysis of loops. An IncompleteType
// is distinguished from a regular type by a flags value of zero. Incomplete type
// objects are internal to the getFlowTypeOfRefecence function and never escape it.
export interface IncompleteType {
flags: TypeFlags; // No flags set
type: Type; // The type marked incomplete
}

export interface AmdDependency {
path: string;
name: string;
Expand Down Expand Up @@ -2978,6 +2988,7 @@ namespace ts {
directoryExists?(directoryName: string): boolean;
realpath?(path: string): string;
getCurrentDirectory?(): string;
getDirectories?(path: string): string[];
}

export interface ResolvedModule {
Expand Down
8 changes: 2 additions & 6 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3521,12 +3521,7 @@ namespace ts {
// export { x, y }
for (const specifier of (<ExportDeclaration>node).exportClause.elements) {
const name = (specifier.propertyName || specifier.name).text;
if (!exportSpecifiers[name]) {
exportSpecifiers[name] = [specifier];
}
else {
exportSpecifiers[name].push(specifier);
}
getOrUpdateProperty(exportSpecifiers, name, () => []).push(specifier);
}
}
break;
Expand Down Expand Up @@ -3965,6 +3960,7 @@ namespace ts {
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.MethodSignature
|| kind === SyntaxKind.ModuleDeclaration
|| kind === SyntaxKind.NamespaceExportDeclaration
|| kind === SyntaxKind.NamespaceImport
|| kind === SyntaxKind.Parameter
|| kind === SyntaxKind.PropertyAssignment
Expand Down
11 changes: 9 additions & 2 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1891,7 +1891,7 @@ namespace ts {
};
}

// Cache host information about scrip Should be refreshed
// Cache host information about script should be refreshed
// at each language service public entry point, since we don't know when
// set of scripts handled by the host changes.
class HostCache {
Expand Down Expand Up @@ -4835,7 +4835,14 @@ namespace ts {
}
if (symbolFlags & SymbolFlags.Alias) {
addNewLineIfDisplayPartsExist();
displayParts.push(keywordPart(SyntaxKind.ImportKeyword));
if (symbol.declarations[0].kind === SyntaxKind.NamespaceExportDeclaration) {
displayParts.push(keywordPart(SyntaxKind.ExportKeyword));
displayParts.push(spacePart());
displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword));
}
else {
displayParts.push(keywordPart(SyntaxKind.ImportKeyword));
}
displayParts.push(spacePart());
addFullSymbolName(symbol);
ts.forEach(symbol.declarations, declaration => {
Expand Down
Loading