From 2fede097f30a8f893384ec60a72c70e0f32c163a Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 24 Aug 2017 08:03:05 -0700 Subject: [PATCH] Add helper functions for adding an item to an array only if it's not already contained (#17833) * Add helper functions for adding an item to an array only if it's not already contained * One more use of appendIfUnique --- src/compiler/checker.ts | 65 +++++++++-------------------------------- src/compiler/core.ts | 26 +++++++++++++++++ src/compiler/factory.ts | 8 ++--- 3 files changed, 42 insertions(+), 57 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 82f41bc31e6..4a07f349c9a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1881,10 +1881,9 @@ namespace ts { // The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example, // module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error. function visit(symbol: Symbol): SymbolTable { - if (!(symbol && symbol.flags & SymbolFlags.HasExports && !contains(visitedSymbols, symbol))) { + if (!(symbol && symbol.flags & SymbolFlags.HasExports && pushIfUnique(visitedSymbols, symbol))) { return; } - visitedSymbols.push(symbol); const symbols = cloneMap(symbol.exports); // All export * declarations are collected in an __export symbol by the binder const exportStars = symbol.exports.get(InternalSymbolName.ExportStar); @@ -2060,10 +2059,10 @@ namespace ts { } function getAccessibleSymbolChainFromSymbolTableWorker(symbols: SymbolTable, visitedSymbolTables: SymbolTable[]): Symbol[] { - if (contains(visitedSymbolTables, symbols)) { + if (!pushIfUnique(visitedSymbolTables, symbols)) { return undefined; } - visitedSymbolTables.push(symbols); + const result = trySymbolTable(symbols); visitedSymbolTables.pop(); return result; @@ -2276,14 +2275,7 @@ namespace ts { // since we will do the emitting later in trackSymbol. if (shouldComputeAliasToMakeVisible) { getNodeLinks(declaration).isVisible = true; - if (aliasesToMakeVisible) { - if (!contains(aliasesToMakeVisible, anyImportSyntax)) { - aliasesToMakeVisible.push(anyImportSyntax); - } - } - else { - aliasesToMakeVisible = [anyImportSyntax]; - } + aliasesToMakeVisible = appendIfUnique(aliasesToMakeVisible, anyImportSyntax); } return true; } @@ -3981,9 +3973,7 @@ namespace ts { forEach(declarations, declaration => { getNodeLinks(declaration).isVisible = true; const resultNode = getAnyImportSyntax(declaration) || declaration; - if (!contains(result, resultNode)) { - result.push(resultNode); - } + pushIfUnique(result, resultNode); if (isInternalModuleImportEqualsDeclaration(declaration)) { // Add the referenced top container visible @@ -4793,12 +4783,7 @@ namespace ts { function appendTypeParameters(typeParameters: TypeParameter[], declarations: ReadonlyArray): TypeParameter[] { for (const declaration of declarations) { const tp = getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration)); - if (!typeParameters) { - typeParameters = [tp]; - } - else if (!contains(typeParameters, tp)) { - typeParameters.push(tp); - } + typeParameters = appendIfUnique(typeParameters, tp); } return typeParameters; } @@ -5521,9 +5506,7 @@ namespace ts { if (!match) { return undefined; } - if (!contains(result, match)) { - (result || (result = [])).push(match); - } + result = appendIfUnique(result, match); } return result; } @@ -6073,12 +6056,7 @@ namespace ts { const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0; if (prop && !(modifiers & excludeModifiers)) { commonFlags &= prop.flags; - if (!props) { - props = [prop]; - } - else if (!contains(props, prop)) { - props.push(prop); - } + props = appendIfUnique(props, prop); checkFlags |= (isReadonlySymbol(prop) ? CheckFlags.Readonly : 0) | (!(modifiers & ModifierFlags.NonPublicAccessibilityModifier) ? CheckFlags.ContainsPublic : 0) | (modifiers & ModifierFlags.Protected ? CheckFlags.ContainsProtected : 0) | @@ -6237,12 +6215,7 @@ namespace ts { let result: TypeParameter[]; forEach(getEffectiveTypeParameterDeclarations(declaration), node => { const tp = getDeclaredTypeOfTypeParameter(node.symbol); - if (!contains(result, tp)) { - if (!result) { - result = []; - } - result.push(tp); - } + result = appendIfUnique(result, tp); }); return result; } @@ -11715,9 +11688,7 @@ namespace ts { if (type === declaredType && declaredType === initialType) { return type; } - if (!contains(antecedentTypes, type)) { - antecedentTypes.push(type); - } + pushIfUnique(antecedentTypes, type); // If an antecedent type is not a subset of the declared type, we need to perform // subtype reduction. This happens when a "foreign" type is injected into the control // flow using the instanceof operator or a user defined type predicate. @@ -11783,9 +11754,7 @@ namespace ts { if (cached) { return cached; } - if (!contains(antecedentTypes, type)) { - antecedentTypes.push(type); - } + pushIfUnique(antecedentTypes, type); // If an antecedent type is not a subset of the declared type, we need to perform // subtype reduction. This happens when a "foreign" type is injected into the control // flow using the instanceof operator or a user defined type predicate. @@ -16826,9 +16795,7 @@ namespace ts { ? Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member : Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); } - if (!contains(aggregatedTypes, type)) { - aggregatedTypes.push(type); - } + pushIfUnique(aggregatedTypes, type); } }); @@ -16880,9 +16847,7 @@ namespace ts { if (type.flags & TypeFlags.Never) { hasReturnOfTypeNever = true; } - else if (!contains(aggregatedTypes, type)) { - aggregatedTypes.push(type); - } + pushIfUnique(aggregatedTypes, type); } else { hasReturnWithNoExpression = true; @@ -16893,9 +16858,7 @@ namespace ts { return undefined; } if (strictNullChecks && aggregatedTypes.length && hasReturnWithNoExpression) { - if (!contains(aggregatedTypes, undefinedType)) { - aggregatedTypes.push(undefinedType); - } + pushIfUnique(aggregatedTypes, undefinedType); } return aggregatedTypes; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index eae2e5ee336..07824ab5d04 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -771,6 +771,32 @@ namespace ts { return to; } + /** + * @return Whether the value was added. + */ + export function pushIfUnique(array: T[], toAdd: T): boolean { + if (contains(array, toAdd)) { + return false; + } + else { + array.push(toAdd); + return true; + } + } + + /** + * Unlike `pushIfUnique`, this can take `undefined` as an input, and returns a new array. + */ + export function appendIfUnique(array: T[] | undefined, toAdd: T): T[] { + if (array) { + pushIfUnique(array, toAdd); + return array; + } + else { + return [toAdd]; + } + } + /** * Stable sort of an array. Elements equal to each other maintain their relative position in the array. */ diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index daec1bce1e8..1d92c0f5315 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2636,9 +2636,7 @@ namespace ts { if (some(helpers)) { const emitNode = getOrCreateEmitNode(node); for (const helper of helpers) { - if (!contains(emitNode.helpers, helper)) { - emitNode.helpers = append(emitNode.helpers, helper); - } + emitNode.helpers = appendIfUnique(emitNode.helpers, helper); } } return node; @@ -2680,9 +2678,7 @@ namespace ts { const helper = sourceEmitHelpers[i]; if (predicate(helper)) { helpersRemoved++; - if (!contains(targetEmitNode.helpers, helper)) { - targetEmitNode.helpers = append(targetEmitNode.helpers, helper); - } + targetEmitNode.helpers = appendIfUnique(targetEmitNode.helpers, helper); } else if (helpersRemoved > 0) { sourceEmitHelpers[i - helpersRemoved] = helper;