diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 10e11e66eb0..fb24f4cc45e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -129,8 +129,8 @@ namespace ts { let Symbol: { new (flags: SymbolFlags, name: string): Symbol }; let classifiableNames: Map; - const unreachableFlow: FlowNode = { kind: FlowKind.Unreachable }; - const reportedUnreachableFlow: FlowNode = { kind: FlowKind.Unreachable }; + const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; + const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; function bindSourceFile(f: SourceFile, opts: CompilerOptions) { file = f; @@ -471,7 +471,7 @@ namespace ts { savedActiveLabels = activeLabels; hasExplicitReturn = false; - currentFlow = { kind: FlowKind.Start }; + currentFlow = { flags: FlowFlags.Start }; currentBreakTarget = undefined; currentContinueTarget = undefined; activeLabels = undefined; @@ -483,7 +483,7 @@ namespace ts { bindReachableStatement(node); - if (currentFlow.kind !== FlowKind.Unreachable && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { + if (!(currentFlow.flags & FlowFlags.Unreachable) && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { flags |= NodeFlags.HasImplicitReturn; if (hasExplicitReturn) { flags |= NodeFlags.HasExplicitReturn; @@ -639,55 +639,80 @@ namespace ts { return false; } - function createFlowLabel(): FlowLabel { + function createBranchLabel(): FlowLabel { return { - kind: FlowKind.Label, + flags: FlowFlags.BranchLabel, antecedents: undefined }; } - function createFlowLoopLabel(): FlowLabel { + function createLoopLabel(): FlowLabel { return { - kind: FlowKind.LoopLabel, + flags: FlowFlags.LoopLabel, antecedents: undefined }; } + function setFlowNodeReferenced(flow: FlowNode) { + // On first reference we set the Referenced flag, thereafter we set the Shared flag + flow.flags |= flow.flags & FlowFlags.Referenced ? FlowFlags.Shared : FlowFlags.Referenced; + } + function addAntecedent(label: FlowLabel, antecedent: FlowNode): void { - if (antecedent.kind !== FlowKind.Unreachable && !contains(label.antecedents, antecedent)) { + if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) { (label.antecedents || (label.antecedents = [])).push(antecedent); + setFlowNodeReferenced(antecedent); } } - function createFlowCondition(antecedent: FlowNode, expression: Expression, assumeTrue: boolean): FlowNode { - if (antecedent.kind === FlowKind.Unreachable) { + function createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression): FlowNode { + if (antecedent.flags & FlowFlags.Unreachable) { return antecedent; } if (!expression) { - return assumeTrue ? antecedent : unreachableFlow; + return flags & FlowFlags.TrueCondition ? antecedent : unreachableFlow; } - if (expression.kind === SyntaxKind.TrueKeyword && !assumeTrue || expression.kind === SyntaxKind.FalseKeyword && assumeTrue) { + if (expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition || + expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) { return unreachableFlow; } if (!isNarrowingExpression(expression)) { return antecedent; } + setFlowNodeReferenced(antecedent); return { - kind: FlowKind.Condition, + flags, antecedent, expression, - assumeTrue }; } function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode { + setFlowNodeReferenced(antecedent); return { - kind: FlowKind.Assignment, + flags: FlowFlags.Assignment, antecedent, node }; } + function skipSimpleConditionalFlow(flow: FlowNode) { + // We skip over simple conditional flows of the form 'x ? aaa : bbb', where 'aaa' and 'bbb' contain + // no constructs that affect control flow type analysis. Such simple flows have no effect on the + // code paths that follow and ignoring them means we'll do less work. + if (flow.flags & FlowFlags.BranchLabel && (flow).antecedents.length === 2) { + const a = (flow).antecedents[0]; + const b = (flow).antecedents[1]; + if ((a.flags & FlowFlags.TrueCondition && b.flags & FlowFlags.FalseCondition || + a.flags & FlowFlags.FalseCondition && b.flags & FlowFlags.TrueCondition) && + (a).antecedent === (b).antecedent && + (a).expression === (b).expression) { + return (a).antecedent; + } + } + return flow; + } + function finishFlowLabel(flow: FlowLabel): FlowNode { const antecedents = flow.antecedents; if (!antecedents) { @@ -696,7 +721,7 @@ namespace ts { if (antecedents.length === 1) { return antecedents[0]; } - return flow; + return skipSimpleConditionalFlow(flow); } function isStatementCondition(node: Node) { @@ -747,8 +772,8 @@ namespace ts { currentTrueTarget = saveTrueTarget; currentFalseTarget = saveFalseTarget; if (!node || !isLogicalExpression(node)) { - addAntecedent(trueTarget, createFlowCondition(currentFlow, node, /*assumeTrue*/ true)); - addAntecedent(falseTarget, createFlowCondition(currentFlow, node, /*assumeTrue*/ false)); + addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); + addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); } } @@ -763,9 +788,9 @@ namespace ts { } function bindWhileStatement(node: WhileStatement): void { - const preWhileLabel = createFlowLoopLabel(); - const preBodyLabel = createFlowLabel(); - const postWhileLabel = createFlowLabel(); + const preWhileLabel = createLoopLabel(); + const preBodyLabel = createBranchLabel(); + const postWhileLabel = createBranchLabel(); addAntecedent(preWhileLabel, currentFlow); currentFlow = preWhileLabel; bindCondition(node.expression, preBodyLabel, postWhileLabel); @@ -776,9 +801,9 @@ namespace ts { } function bindDoStatement(node: DoStatement): void { - const preDoLabel = createFlowLoopLabel(); - const preConditionLabel = createFlowLabel(); - const postDoLabel = createFlowLabel(); + const preDoLabel = createLoopLabel(); + const preConditionLabel = createBranchLabel(); + const postDoLabel = createBranchLabel(); addAntecedent(preDoLabel, currentFlow); currentFlow = preDoLabel; bindIterativeStatement(node.statement, postDoLabel, preConditionLabel); @@ -789,9 +814,9 @@ namespace ts { } function bindForStatement(node: ForStatement): void { - const preLoopLabel = createFlowLoopLabel(); - const preBodyLabel = createFlowLabel(); - const postLoopLabel = createFlowLabel(); + const preLoopLabel = createLoopLabel(); + const preBodyLabel = createBranchLabel(); + const postLoopLabel = createBranchLabel(); bind(node.initializer); addAntecedent(preLoopLabel, currentFlow); currentFlow = preLoopLabel; @@ -804,8 +829,8 @@ namespace ts { } function bindForInOrForOfStatement(node: ForInStatement | ForOfStatement): void { - const preLoopLabel = createFlowLoopLabel(); - const postLoopLabel = createFlowLabel(); + const preLoopLabel = createLoopLabel(); + const postLoopLabel = createBranchLabel(); addAntecedent(preLoopLabel, currentFlow); currentFlow = preLoopLabel; bind(node.expression); @@ -820,9 +845,9 @@ namespace ts { } function bindIfStatement(node: IfStatement): void { - const thenLabel = createFlowLabel(); - const elseLabel = createFlowLabel(); - const postIfLabel = createFlowLabel(); + const thenLabel = createBranchLabel(); + const elseLabel = createBranchLabel(); + const postIfLabel = createBranchLabel(); bindCondition(node.expression, thenLabel, elseLabel); currentFlow = finishFlowLabel(thenLabel); bind(node.thenStatement); @@ -875,7 +900,7 @@ namespace ts { } function bindTryStatement(node: TryStatement): void { - const postFinallyLabel = createFlowLabel(); + const postFinallyLabel = createBranchLabel(); const preTryFlow = currentFlow; // TODO: Every statement in try block is potentially an exit point! bind(node.tryBlock); @@ -893,7 +918,7 @@ namespace ts { } function bindSwitchStatement(node: SwitchStatement): void { - const postSwitchLabel = createFlowLabel(); + const postSwitchLabel = createBranchLabel(); bind(node.expression); const saveBreakTarget = currentBreakTarget; const savePreSwitchCaseFlow = preSwitchCaseFlow; @@ -915,17 +940,17 @@ namespace ts { for (let i = 0; i < clauses.length; i++) { const clause = clauses[i]; if (clause.statements.length) { - if (currentFlow.kind === FlowKind.Unreachable) { + if (currentFlow.flags & FlowFlags.Unreachable) { currentFlow = preSwitchCaseFlow; } else { - const preCaseLabel = createFlowLabel(); + const preCaseLabel = createBranchLabel(); addAntecedent(preCaseLabel, preSwitchCaseFlow); addAntecedent(preCaseLabel, currentFlow); currentFlow = finishFlowLabel(preCaseLabel); } bind(clause); - if (currentFlow.kind !== FlowKind.Unreachable && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { + if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); } } @@ -951,8 +976,8 @@ namespace ts { } function bindLabeledStatement(node: LabeledStatement): void { - const preStatementLabel = createFlowLoopLabel(); - const postStatementLabel = createFlowLabel(); + const preStatementLabel = createLoopLabel(); + const postStatementLabel = createBranchLabel(); bind(node.label); addAntecedent(preStatementLabel, currentFlow); const activeLabel = pushActiveLabel(node.label.text, postStatementLabel, preStatementLabel); @@ -1001,7 +1026,7 @@ namespace ts { } function bindLogicalExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) { - const preRightLabel = createFlowLabel(); + const preRightLabel = createBranchLabel(); if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) { bindCondition(node.left, preRightLabel, falseTarget); } @@ -1031,7 +1056,7 @@ namespace ts { const operator = node.operatorToken.kind; if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) { if (isTopLevelLogicalExpression(node)) { - const postExpressionLabel = createFlowLabel(); + const postExpressionLabel = createBranchLabel(); bindLogicalExpression(node, postExpressionLabel, postExpressionLabel); currentFlow = finishFlowLabel(postExpressionLabel); } @@ -1048,9 +1073,9 @@ namespace ts { } function bindConditionalExpressionFlow(node: ConditionalExpression) { - const trueLabel = createFlowLabel(); - const falseLabel = createFlowLabel(); - const postExpressionLabel = createFlowLabel(); + const trueLabel = createBranchLabel(); + const falseLabel = createBranchLabel(); + const postExpressionLabel = createBranchLabel(); bindCondition(node.condition, trueLabel, falseLabel); currentFlow = finishFlowLabel(trueLabel); bind(node.whenTrue); @@ -2065,7 +2090,7 @@ namespace ts { } function checkUnreachable(node: Node): boolean { - if (currentFlow.kind !== FlowKind.Unreachable) { + if (!(currentFlow.flags & FlowFlags.Unreachable)) { return false; } if (currentFlow === unreachableFlow) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 504cb17def2..b03aa99e7d9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -183,6 +183,7 @@ namespace ts { let flowStackStart = 0; let flowStackCount = 0; + let visitedFlowCount = 0; const tupleTypes: Map = {}; const unionTypes: Map = {}; @@ -199,6 +200,8 @@ namespace ts { const flowTypeCaches: Map[] = []; const flowStackNodes: FlowNode[] = []; const flowStackCacheKeys: string[] = []; + const visitedFlowNodes: FlowNode[] = []; + const visitedFlowTypes: Type[] = []; const potentialThisCollisions: Node[] = []; const awaitedTypeStack: number[] = []; @@ -7581,34 +7584,57 @@ namespace ts { if (!reference.flowNode || declaredType === initialType && !(declaredType.flags & TypeFlags.Narrowable)) { return declaredType; } - return getTypeAtFlowNode(reference.flowNode); + const visitedFlowStart = visitedFlowCount; + const result = getTypeAtFlowNode(reference.flowNode); + visitedFlowCount = visitedFlowStart; + return result; function getTypeAtFlowNode(flow: FlowNode): Type { while (true) { - switch (flow.kind) { - case FlowKind.Assignment: - const type = getTypeAtFlowAssignment(flow); - if (!type) { - flow = (flow).antecedent; - continue; + if (flow.flags & FlowFlags.Shared) { + // We cache results of flow type resolution for shared nodes that were previously visited in + // the same getFlowTypeOfReference invocation. A node is considered shared when it is the + // antecedent of more than one node. + for (let i = visitedFlowStart; i < visitedFlowCount; i++) { + if (visitedFlowNodes[i] === flow) { + return visitedFlowTypes[i]; } - return type; - case FlowKind.Condition: - return getTypeAtFlowCondition(flow); - case FlowKind.Label: - case FlowKind.LoopLabel: - if ((flow).antecedents.length === 1) { - flow = (flow).antecedents[0]; - continue; - } - return getTypeAtFlowLabel(flow); - case FlowKind.Unreachable: - // Unreachable code errors are reported in the binding phase. Here we - // simply return the declared type to reduce follow-on errors. - return declaredType; + } } - // At the top of the flow we have the initial type - return initialType; + let type: Type; + if (flow.flags & FlowFlags.Assignment) { + type = getTypeAtFlowAssignment(flow); + if (!type) { + flow = (flow).antecedent; + continue; + } + } + else if (flow.flags & FlowFlags.Condition) { + type = getTypeAtFlowCondition(flow); + } + else if (flow.flags & FlowFlags.Label) { + if ((flow).antecedents.length === 1) { + flow = (flow).antecedents[0]; + continue; + } + type = getTypeAtFlowLabel(flow); + } + else if (flow.flags & FlowFlags.Unreachable) { + // Unreachable code errors are reported in the binding phase. Here we + // simply return the declared type to reduce follow-on errors. + type = declaredType; + } + else { + // At the top of the flow we have the initial type. + type = initialType; + } + if (flow.flags & FlowFlags.Shared) { + // Record visited node and the associated type in the cache. + visitedFlowNodes[visitedFlowCount] = flow; + visitedFlowTypes[visitedFlowCount] = type; + visitedFlowCount++; + } + return type; } } @@ -7644,7 +7670,7 @@ namespace ts { function getTypeAtFlowCondition(flow: FlowCondition) { const type = getTypeAtFlowNode(flow.antecedent); - return type && narrowType(type, flow.expression, flow.assumeTrue); + return type && narrowType(type, flow.expression, (flow.flags & FlowFlags.TrueCondition) !== 0); } function getTypeAtFlowNodeCached(flow: FlowNode) { @@ -7676,7 +7702,7 @@ namespace ts { function getTypeAtFlowLabel(flow: FlowLabel) { const antecedentTypes: Type[] = []; for (const antecedent of flow.antecedents) { - const type = flow.kind === FlowKind.LoopLabel ? + const type = flow.flags & FlowFlags.LoopLabel ? getTypeAtFlowNodeCached(antecedent) : getTypeAtFlowNode(antecedent); if (!type) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d0d411a710e..db9bd2469db 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1515,17 +1515,22 @@ namespace ts { isBracketed: boolean; } - export const enum FlowKind { - Unreachable, - Start, - Label, - LoopLabel, - Assignment, - Condition + export const enum FlowFlags { + Unreachable = 1 << 0, // Unreachable code + Start = 1 << 1, // Start of flow graph + BranchLabel = 1 << 2, // Non-looping junction + LoopLabel = 1 << 3, // Looping junction + Assignment = 1 << 4, // Assignment + TrueCondition = 1 << 5, // Condition known to be true + FalseCondition = 1 << 6, // Condition known to be false + Referenced = 1 << 7, // Referenced as antecedent once + Shared = 1 << 8, // Referenced as antecedent more than once + Label = BranchLabel | LoopLabel, + Condition = TrueCondition | FalseCondition } export interface FlowNode { - kind: FlowKind; // Node kind + flags: FlowFlags; id?: number; // Node id used by flow type cache in checker } @@ -1545,7 +1550,6 @@ namespace ts { // node's location in the control flow. export interface FlowCondition extends FlowNode { expression: Expression; - assumeTrue: boolean; antecedent: FlowNode; } diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.types b/tests/baselines/reference/controlFlowBinaryOrExpression.types index 8c1cd32d8d9..c7651a09d1e 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.types +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.types @@ -87,7 +87,7 @@ if (isNodeList(sourceObj)) { if (isHTMLCollection(sourceObj)) { >isHTMLCollection(sourceObj) : boolean >isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection ->sourceObj : HTMLCollection | { a: string; } +>sourceObj : { a: string; } | HTMLCollection sourceObj.length; >sourceObj.length : number @@ -99,7 +99,7 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { >isNodeList(sourceObj) || isHTMLCollection(sourceObj) : boolean >isNodeList(sourceObj) : boolean >isNodeList : (sourceObj: any) => sourceObj is NodeList ->sourceObj : HTMLCollection | { a: string; } +>sourceObj : { a: string; } | HTMLCollection >isHTMLCollection(sourceObj) : boolean >isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection >sourceObj : { a: string; } diff --git a/tests/baselines/reference/controlFlowCaching.js b/tests/baselines/reference/controlFlowCaching.js new file mode 100644 index 00000000000..fc102b8e823 --- /dev/null +++ b/tests/baselines/reference/controlFlowCaching.js @@ -0,0 +1,120 @@ +//// [controlFlowCaching.ts] + +// Repro for #8401 + +function f(dim, offsets, arr, acommon, centerAnchorLimit, g, has, lin) { + var isRtl = this._isRtl(); // chart mirroring + // prepare variable + var o = this.opt, ta = this.chart.theme.axis, position = o.position, + leftBottom = position !== "rightOrTop", rotation = o.rotation % 360, + start, stop, titlePos, titleRotation = 0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, + labelGap = this.chart.theme.axis.tick.labelGap, + taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), + taTitleFont = o.titleFont || (ta.title && ta.title.font), + taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", + taTitleFontColor = o.titleFontColor || (ta.title && ta.title.fontColor) || "black", + taTitleGap = (o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15, + taTitleOrientation = o.titleOrientation || (ta.title && ta.title.orientation) || "axis", + taMajorTick = this.chart.theme.getTick("major", o), + taMinorTick = this.chart.theme.getTick("minor", o), + taMicroTick = this.chart.theme.getTick("micro", o), + + taStroke = "stroke" in o ? o.stroke : ta.stroke, + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, + cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), + sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), + tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; + if (rotation < 0) { + rotation += 360; + } + var cachedLabelW = this._getMaxLabelSize(); + cachedLabelW = cachedLabelW && cachedLabelW.majLabelW; + titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, + taMinorTick.length > 0 ? taMinorTick.length : 0) + + tsize + taTitleGap; + axisVector = { x: isRtl ? -1 : 1, y: 0 }; // chart mirroring + switch (rotation) { + default: + if (rotation < (90 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? size : 0; + } else if (rotation < (90 + centerAnchorLimit)) { + labelOffset.x = -size * 0.4; + } else if (rotation < 180) { + labelOffset.y = leftBottom ? 0 : -size; + } else if (rotation < (270 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? 0 : -size; + } else if (rotation < (270 + centerAnchorLimit)) { + labelOffset.y = leftBottom ? size * 0.4 : 0; + } else { + labelOffset.y = leftBottom ? size : 0; + } + } + + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; + titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); + switch (labelAlign) { + case "start": + labelAlign = "end"; + break; + case "end": + labelAlign = "start"; + break; + case "middle": + labelOffset.y -= size; + break; + } + + let _ = rotation; +} + + +//// [controlFlowCaching.js] +// Repro for #8401 +function f(dim, offsets, arr, acommon, centerAnchorLimit, g, has, lin) { + var isRtl = this._isRtl(); // chart mirroring + // prepare variable + var o = this.opt, ta = this.chart.theme.axis, position = o.position, leftBottom = position !== "rightOrTop", rotation = o.rotation % 360, start, stop, titlePos, titleRotation = 0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, labelGap = this.chart.theme.axis.tick.labelGap, taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), taTitleFont = o.titleFont || (ta.title && ta.title.font), taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", taTitleFontColor = o.titleFontColor || (ta.title && ta.title.fontColor) || "black", taTitleGap = (o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15, taTitleOrientation = o.titleOrientation || (ta.title && ta.title.orientation) || "axis", taMajorTick = this.chart.theme.getTick("major", o), taMinorTick = this.chart.theme.getTick("minor", o), taMicroTick = this.chart.theme.getTick("micro", o), taStroke = "stroke" in o ? o.stroke : ta.stroke, size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; + if (rotation < 0) { + rotation += 360; + } + var cachedLabelW = this._getMaxLabelSize(); + cachedLabelW = cachedLabelW && cachedLabelW.majLabelW; + titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) + + tsize + taTitleGap; + axisVector = { x: isRtl ? -1 : 1, y: 0 }; // chart mirroring + switch (rotation) { + default: + if (rotation < (90 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? size : 0; + } + else if (rotation < (90 + centerAnchorLimit)) { + labelOffset.x = -size * 0.4; + } + else if (rotation < 180) { + labelOffset.y = leftBottom ? 0 : -size; + } + else if (rotation < (270 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? 0 : -size; + } + else if (rotation < (270 + centerAnchorLimit)) { + labelOffset.y = leftBottom ? size * 0.4 : 0; + } + else { + labelOffset.y = leftBottom ? size : 0; + } + } + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; + titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); + switch (labelAlign) { + case "start": + labelAlign = "end"; + break; + case "end": + labelAlign = "start"; + break; + case "middle": + labelOffset.y -= size; + break; + } + var _ = rotation; +} diff --git a/tests/baselines/reference/controlFlowCaching.symbols b/tests/baselines/reference/controlFlowCaching.symbols new file mode 100644 index 00000000000..74534ce75a1 --- /dev/null +++ b/tests/baselines/reference/controlFlowCaching.symbols @@ -0,0 +1,279 @@ +=== tests/cases/compiler/controlFlowCaching.ts === + +// Repro for #8401 + +function f(dim, offsets, arr, acommon, centerAnchorLimit, g, has, lin) { +>f : Symbol(f, Decl(controlFlowCaching.ts, 0, 0)) +>dim : Symbol(dim, Decl(controlFlowCaching.ts, 3, 11)) +>offsets : Symbol(offsets, Decl(controlFlowCaching.ts, 3, 15)) +>arr : Symbol(arr, Decl(controlFlowCaching.ts, 3, 24)) +>acommon : Symbol(acommon, Decl(controlFlowCaching.ts, 3, 29)) +>centerAnchorLimit : Symbol(centerAnchorLimit, Decl(controlFlowCaching.ts, 3, 38)) +>g : Symbol(g, Decl(controlFlowCaching.ts, 3, 57)) +>has : Symbol(has, Decl(controlFlowCaching.ts, 3, 60)) +>lin : Symbol(lin, Decl(controlFlowCaching.ts, 3, 65)) + + var isRtl = this._isRtl(); // chart mirroring +>isRtl : Symbol(isRtl, Decl(controlFlowCaching.ts, 4, 7)) + + // prepare variable + var o = this.opt, ta = this.chart.theme.axis, position = o.position, +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>position : Symbol(position, Decl(controlFlowCaching.ts, 6, 49)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) + + leftBottom = position !== "rightOrTop", rotation = o.rotation % 360, +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>position : Symbol(position, Decl(controlFlowCaching.ts, 6, 49)) +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) + + start, stop, titlePos, titleRotation = 0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, +>start : Symbol(start, Decl(controlFlowCaching.ts, 7, 76)) +>stop : Symbol(stop, Decl(controlFlowCaching.ts, 8, 14)) +>titlePos : Symbol(titlePos, Decl(controlFlowCaching.ts, 8, 20)) +>titleRotation : Symbol(titleRotation, Decl(controlFlowCaching.ts, 8, 30)) +>titleOffset : Symbol(titleOffset, Decl(controlFlowCaching.ts, 8, 49)) +>axisVector : Symbol(axisVector, Decl(controlFlowCaching.ts, 8, 62)) +>tickVector : Symbol(tickVector, Decl(controlFlowCaching.ts, 8, 74)) +>anchorOffset : Symbol(anchorOffset, Decl(controlFlowCaching.ts, 8, 86)) +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>labelAlign : Symbol(labelAlign, Decl(controlFlowCaching.ts, 8, 113)) + + labelGap = this.chart.theme.axis.tick.labelGap, +>labelGap : Symbol(labelGap, Decl(controlFlowCaching.ts, 8, 125)) + + taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), +>taFont : Symbol(taFont, Decl(controlFlowCaching.ts, 9, 55)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taTitleFont = o.titleFont || (ta.title && ta.title.font), +>taTitleFont : Symbol(taTitleFont, Decl(controlFlowCaching.ts, 10, 92)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", +>taFontColor : Symbol(taFontColor, Decl(controlFlowCaching.ts, 11, 65)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taTitleFontColor = o.titleFontColor || (ta.title && ta.title.fontColor) || "black", +>taTitleFontColor : Symbol(taTitleFontColor, Decl(controlFlowCaching.ts, 12, 123)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taTitleGap = (o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15, +>taTitleGap : Symbol(taTitleGap, Decl(controlFlowCaching.ts, 13, 91)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taTitleOrientation = o.titleOrientation || (ta.title && ta.title.orientation) || "axis", +>taTitleOrientation : Symbol(taTitleOrientation, Decl(controlFlowCaching.ts, 14, 92)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taMajorTick = this.chart.theme.getTick("major", o), +>taMajorTick : Symbol(taMajorTick, Decl(controlFlowCaching.ts, 15, 96)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) + + taMinorTick = this.chart.theme.getTick("minor", o), +>taMinorTick : Symbol(taMinorTick, Decl(controlFlowCaching.ts, 16, 59)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) + + taMicroTick = this.chart.theme.getTick("micro", o), +>taMicroTick : Symbol(taMicroTick, Decl(controlFlowCaching.ts, 17, 59)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) + + taStroke = "stroke" in o ? o.stroke : ta.stroke, +>taStroke : Symbol(taStroke, Decl(controlFlowCaching.ts, 18, 59)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) +>taFont : Symbol(taFont, Decl(controlFlowCaching.ts, 9, 55)) +>g : Symbol(g, Decl(controlFlowCaching.ts, 3, 57)) +>g : Symbol(g, Decl(controlFlowCaching.ts, 3, 57)) +>taFont : Symbol(taFont, Decl(controlFlowCaching.ts, 9, 55)) + + cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), +>cosr : Symbol(cosr, Decl(controlFlowCaching.ts, 21, 79)) +>Math.abs : Symbol(Math.abs, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>abs : Symbol(Math.abs, Decl(lib.d.ts, --, --)) +>Math.cos : Symbol(Math.cos, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>cos : Symbol(Math.cos, Decl(lib.d.ts, --, --)) +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>Math.PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) + + sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), +>sinr : Symbol(sinr, Decl(controlFlowCaching.ts, 22, 60)) +>Math.abs : Symbol(Math.abs, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>abs : Symbol(Math.abs, Decl(lib.d.ts, --, --)) +>Math.sin : Symbol(Math.sin, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>sin : Symbol(Math.sin, Decl(lib.d.ts, --, --)) +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>Math.PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) + + tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; +>tsize : Symbol(tsize, Decl(controlFlowCaching.ts, 23, 60)) +>taTitleFont : Symbol(taTitleFont, Decl(controlFlowCaching.ts, 10, 92)) +>g : Symbol(g, Decl(controlFlowCaching.ts, 3, 57)) +>g : Symbol(g, Decl(controlFlowCaching.ts, 3, 57)) +>taTitleFont : Symbol(taTitleFont, Decl(controlFlowCaching.ts, 10, 92)) + + if (rotation < 0) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) + + rotation += 360; +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) + } + var cachedLabelW = this._getMaxLabelSize(); +>cachedLabelW : Symbol(cachedLabelW, Decl(controlFlowCaching.ts, 28, 7)) + + cachedLabelW = cachedLabelW && cachedLabelW.majLabelW; +>cachedLabelW : Symbol(cachedLabelW, Decl(controlFlowCaching.ts, 28, 7)) +>cachedLabelW : Symbol(cachedLabelW, Decl(controlFlowCaching.ts, 28, 7)) +>cachedLabelW : Symbol(cachedLabelW, Decl(controlFlowCaching.ts, 28, 7)) + + titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, +>titleOffset : Symbol(titleOffset, Decl(controlFlowCaching.ts, 8, 49)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) +>cosr : Symbol(cosr, Decl(controlFlowCaching.ts, 21, 79)) +>cachedLabelW : Symbol(cachedLabelW, Decl(controlFlowCaching.ts, 28, 7)) +>sinr : Symbol(sinr, Decl(controlFlowCaching.ts, 22, 60)) +>labelGap : Symbol(labelGap, Decl(controlFlowCaching.ts, 8, 125)) +>Math.max : Symbol(Math.max, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>max : Symbol(Math.max, Decl(lib.d.ts, --, --)) +>taMajorTick : Symbol(taMajorTick, Decl(controlFlowCaching.ts, 15, 96)) +>taMajorTick : Symbol(taMajorTick, Decl(controlFlowCaching.ts, 15, 96)) + + taMinorTick.length > 0 ? taMinorTick.length : 0) + +>taMinorTick : Symbol(taMinorTick, Decl(controlFlowCaching.ts, 16, 59)) +>taMinorTick : Symbol(taMinorTick, Decl(controlFlowCaching.ts, 16, 59)) + + tsize + taTitleGap; +>tsize : Symbol(tsize, Decl(controlFlowCaching.ts, 23, 60)) +>taTitleGap : Symbol(taTitleGap, Decl(controlFlowCaching.ts, 13, 91)) + + axisVector = { x: isRtl ? -1 : 1, y: 0 }; // chart mirroring +>axisVector : Symbol(axisVector, Decl(controlFlowCaching.ts, 8, 62)) +>x : Symbol(x, Decl(controlFlowCaching.ts, 33, 18)) +>isRtl : Symbol(isRtl, Decl(controlFlowCaching.ts, 4, 7)) +>y : Symbol(y, Decl(controlFlowCaching.ts, 33, 37)) + + switch (rotation) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) + + default: + if (rotation < (90 - centerAnchorLimit)) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>centerAnchorLimit : Symbol(centerAnchorLimit, Decl(controlFlowCaching.ts, 3, 38)) + + labelOffset.y = leftBottom ? size : 0; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + } else if (rotation < (90 + centerAnchorLimit)) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>centerAnchorLimit : Symbol(centerAnchorLimit, Decl(controlFlowCaching.ts, 3, 38)) + + labelOffset.x = -size * 0.4; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + } else if (rotation < 180) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) + + labelOffset.y = leftBottom ? 0 : -size; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + } else if (rotation < (270 - centerAnchorLimit)) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>centerAnchorLimit : Symbol(centerAnchorLimit, Decl(controlFlowCaching.ts, 3, 38)) + + labelOffset.y = leftBottom ? 0 : -size; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + } else if (rotation < (270 + centerAnchorLimit)) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>centerAnchorLimit : Symbol(centerAnchorLimit, Decl(controlFlowCaching.ts, 3, 38)) + + labelOffset.y = leftBottom ? size * 0.4 : 0; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + } else { + labelOffset.y = leftBottom ? size : 0; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + } + } + + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; +>titleRotation : Symbol(titleRotation, Decl(controlFlowCaching.ts, 8, 30)) +>taTitleOrientation : Symbol(taTitleOrientation, Decl(controlFlowCaching.ts, 14, 92)) +>taTitleOrientation : Symbol(taTitleOrientation, Decl(controlFlowCaching.ts, 14, 92)) + + titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); +>titlePos : Symbol(titlePos, Decl(controlFlowCaching.ts, 8, 20)) +>offsets : Symbol(offsets, Decl(controlFlowCaching.ts, 3, 15)) +>titleOffset : Symbol(titleOffset, Decl(controlFlowCaching.ts, 8, 49)) +>titleRotation : Symbol(titleRotation, Decl(controlFlowCaching.ts, 8, 30)) +>tsize : Symbol(tsize, Decl(controlFlowCaching.ts, 23, 60)) + + switch (labelAlign) { +>labelAlign : Symbol(labelAlign, Decl(controlFlowCaching.ts, 8, 113)) + + case "start": + labelAlign = "end"; +>labelAlign : Symbol(labelAlign, Decl(controlFlowCaching.ts, 8, 113)) + + break; + case "end": + labelAlign = "start"; +>labelAlign : Symbol(labelAlign, Decl(controlFlowCaching.ts, 8, 113)) + + break; + case "middle": + labelOffset.y -= size; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + break; + } + + let _ = rotation; +>_ : Symbol(_, Decl(controlFlowCaching.ts, 65, 7)) +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +} + diff --git a/tests/baselines/reference/controlFlowCaching.types b/tests/baselines/reference/controlFlowCaching.types new file mode 100644 index 00000000000..cd603b274d1 --- /dev/null +++ b/tests/baselines/reference/controlFlowCaching.types @@ -0,0 +1,607 @@ +=== tests/cases/compiler/controlFlowCaching.ts === + +// Repro for #8401 + +function f(dim, offsets, arr, acommon, centerAnchorLimit, g, has, lin) { +>f : (dim: any, offsets: any, arr: any, acommon: any, centerAnchorLimit: any, g: any, has: any, lin: any) => void +>dim : any +>offsets : any +>arr : any +>acommon : any +>centerAnchorLimit : any +>g : any +>has : any +>lin : any + + var isRtl = this._isRtl(); // chart mirroring +>isRtl : any +>this._isRtl() : any +>this._isRtl : any +>this : any +>_isRtl : any + + // prepare variable + var o = this.opt, ta = this.chart.theme.axis, position = o.position, +>o : any +>this.opt : any +>this : any +>opt : any +>ta : any +>this.chart.theme.axis : any +>this.chart.theme : any +>this.chart : any +>this : any +>chart : any +>theme : any +>axis : any +>position : any +>o.position : any +>o : any +>position : any + + leftBottom = position !== "rightOrTop", rotation = o.rotation % 360, +>leftBottom : boolean +>position !== "rightOrTop" : boolean +>position : any +>"rightOrTop" : string +>rotation : number +>o.rotation % 360 : number +>o.rotation : any +>o : any +>rotation : any +>360 : number + + start, stop, titlePos, titleRotation = 0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, +>start : any +>stop : any +>titlePos : any +>titleRotation : number +>0 : number +>titleOffset : any +>axisVector : any +>tickVector : any +>anchorOffset : any +>labelOffset : any +>labelAlign : any + + labelGap = this.chart.theme.axis.tick.labelGap, +>labelGap : any +>this.chart.theme.axis.tick.labelGap : any +>this.chart.theme.axis.tick : any +>this.chart.theme.axis : any +>this.chart.theme : any +>this.chart : any +>this : any +>chart : any +>theme : any +>axis : any +>tick : any +>labelGap : any + + taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), +>taFont : any +>o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font) : any +>o.font || (ta.majorTick && ta.majorTick.font) : any +>o.font : any +>o : any +>font : any +>(ta.majorTick && ta.majorTick.font) : any +>ta.majorTick && ta.majorTick.font : any +>ta.majorTick : any +>ta : any +>majorTick : any +>ta.majorTick.font : any +>ta.majorTick : any +>ta : any +>majorTick : any +>font : any +>(ta.tick && ta.tick.font) : any +>ta.tick && ta.tick.font : any +>ta.tick : any +>ta : any +>tick : any +>ta.tick.font : any +>ta.tick : any +>ta : any +>tick : any +>font : any + + taTitleFont = o.titleFont || (ta.title && ta.title.font), +>taTitleFont : any +>o.titleFont || (ta.title && ta.title.font) : any +>o.titleFont : any +>o : any +>titleFont : any +>(ta.title && ta.title.font) : any +>ta.title && ta.title.font : any +>ta.title : any +>ta : any +>title : any +>ta.title.font : any +>ta.title : any +>ta : any +>title : any +>font : any + + taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", +>taFontColor : any +>o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black" : any +>o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) : any +>o.fontColor || (ta.majorTick && ta.majorTick.fontColor) : any +>o.fontColor : any +>o : any +>fontColor : any +>(ta.majorTick && ta.majorTick.fontColor) : any +>ta.majorTick && ta.majorTick.fontColor : any +>ta.majorTick : any +>ta : any +>majorTick : any +>ta.majorTick.fontColor : any +>ta.majorTick : any +>ta : any +>majorTick : any +>fontColor : any +>(ta.tick && ta.tick.fontColor) : any +>ta.tick && ta.tick.fontColor : any +>ta.tick : any +>ta : any +>tick : any +>ta.tick.fontColor : any +>ta.tick : any +>ta : any +>tick : any +>fontColor : any +>"black" : string + + taTitleFontColor = o.titleFontColor || (ta.title && ta.title.fontColor) || "black", +>taTitleFontColor : any +>o.titleFontColor || (ta.title && ta.title.fontColor) || "black" : any +>o.titleFontColor || (ta.title && ta.title.fontColor) : any +>o.titleFontColor : any +>o : any +>titleFontColor : any +>(ta.title && ta.title.fontColor) : any +>ta.title && ta.title.fontColor : any +>ta.title : any +>ta : any +>title : any +>ta.title.fontColor : any +>ta.title : any +>ta : any +>title : any +>fontColor : any +>"black" : string + + taTitleGap = (o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15, +>taTitleGap : any +>(o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15 : any +>(o.titleGap == 0) : boolean +>o.titleGap == 0 : boolean +>o.titleGap : any +>o : any +>titleGap : any +>0 : number +>0 : number +>o.titleGap || (ta.title && ta.title.gap) || 15 : any +>o.titleGap || (ta.title && ta.title.gap) : any +>o.titleGap : any +>o : any +>titleGap : any +>(ta.title && ta.title.gap) : any +>ta.title && ta.title.gap : any +>ta.title : any +>ta : any +>title : any +>ta.title.gap : any +>ta.title : any +>ta : any +>title : any +>gap : any +>15 : number + + taTitleOrientation = o.titleOrientation || (ta.title && ta.title.orientation) || "axis", +>taTitleOrientation : any +>o.titleOrientation || (ta.title && ta.title.orientation) || "axis" : any +>o.titleOrientation || (ta.title && ta.title.orientation) : any +>o.titleOrientation : any +>o : any +>titleOrientation : any +>(ta.title && ta.title.orientation) : any +>ta.title && ta.title.orientation : any +>ta.title : any +>ta : any +>title : any +>ta.title.orientation : any +>ta.title : any +>ta : any +>title : any +>orientation : any +>"axis" : string + + taMajorTick = this.chart.theme.getTick("major", o), +>taMajorTick : any +>this.chart.theme.getTick("major", o) : any +>this.chart.theme.getTick : any +>this.chart.theme : any +>this.chart : any +>this : any +>chart : any +>theme : any +>getTick : any +>"major" : string +>o : any + + taMinorTick = this.chart.theme.getTick("minor", o), +>taMinorTick : any +>this.chart.theme.getTick("minor", o) : any +>this.chart.theme.getTick : any +>this.chart.theme : any +>this.chart : any +>this : any +>chart : any +>theme : any +>getTick : any +>"minor" : string +>o : any + + taMicroTick = this.chart.theme.getTick("micro", o), +>taMicroTick : any +>this.chart.theme.getTick("micro", o) : any +>this.chart.theme.getTick : any +>this.chart.theme : any +>this.chart : any +>this : any +>chart : any +>theme : any +>getTick : any +>"micro" : string +>o : any + + taStroke = "stroke" in o ? o.stroke : ta.stroke, +>taStroke : any +>"stroke" in o ? o.stroke : ta.stroke : any +>"stroke" in o : boolean +>"stroke" : string +>o : any +>o.stroke : any +>o : any +>stroke : any +>ta.stroke : any +>ta : any +>stroke : any + + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, +>size : any +>taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0 : any +>taFont : any +>g.normalizedLength(g.splitFontString(taFont).size) : any +>g.normalizedLength : any +>g : any +>normalizedLength : any +>g.splitFontString(taFont).size : any +>g.splitFontString(taFont) : any +>g.splitFontString : any +>g : any +>splitFontString : any +>taFont : any +>size : any +>0 : number + + cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), +>cosr : number +>Math.abs(Math.cos(rotation * Math.PI / 180)) : number +>Math.abs : (x: number) => number +>Math : Math +>abs : (x: number) => number +>Math.cos(rotation * Math.PI / 180) : number +>Math.cos : (x: number) => number +>Math : Math +>cos : (x: number) => number +>rotation * Math.PI / 180 : number +>rotation * Math.PI : number +>rotation : number +>Math.PI : number +>Math : Math +>PI : number +>180 : number + + sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), +>sinr : number +>Math.abs(Math.sin(rotation * Math.PI / 180)) : number +>Math.abs : (x: number) => number +>Math : Math +>abs : (x: number) => number +>Math.sin(rotation * Math.PI / 180) : number +>Math.sin : (x: number) => number +>Math : Math +>sin : (x: number) => number +>rotation * Math.PI / 180 : number +>rotation * Math.PI : number +>rotation : number +>Math.PI : number +>Math : Math +>PI : number +>180 : number + + tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; +>tsize : any +>taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0 : any +>taTitleFont : any +>g.normalizedLength(g.splitFontString(taTitleFont).size) : any +>g.normalizedLength : any +>g : any +>normalizedLength : any +>g.splitFontString(taTitleFont).size : any +>g.splitFontString(taTitleFont) : any +>g.splitFontString : any +>g : any +>splitFontString : any +>taTitleFont : any +>size : any +>0 : number + + if (rotation < 0) { +>rotation < 0 : boolean +>rotation : number +>0 : number + + rotation += 360; +>rotation += 360 : number +>rotation : number +>360 : number + } + var cachedLabelW = this._getMaxLabelSize(); +>cachedLabelW : any +>this._getMaxLabelSize() : any +>this._getMaxLabelSize : any +>this : any +>_getMaxLabelSize : any + + cachedLabelW = cachedLabelW && cachedLabelW.majLabelW; +>cachedLabelW = cachedLabelW && cachedLabelW.majLabelW : any +>cachedLabelW : any +>cachedLabelW && cachedLabelW.majLabelW : any +>cachedLabelW : any +>cachedLabelW.majLabelW : any +>cachedLabelW : any +>majLabelW : any + + titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, +>titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) + tsize + taTitleGap : any +>titleOffset : any +>size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) + tsize + taTitleGap : any +>size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) + tsize : any +>size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) : any +>size * cosr + (cachedLabelW || 0) * sinr + labelGap : any +>size * cosr + (cachedLabelW || 0) * sinr : number +>size * cosr : number +>size : any +>cosr : number +>(cachedLabelW || 0) * sinr : number +>(cachedLabelW || 0) : any +>cachedLabelW || 0 : any +>cachedLabelW : any +>0 : number +>sinr : number +>labelGap : any +>Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) : number +>Math.max : (...values: number[]) => number +>Math : Math +>max : (...values: number[]) => number +>taMajorTick.length > 0 ? taMajorTick.length : 0 : any +>taMajorTick.length > 0 : boolean +>taMajorTick.length : any +>taMajorTick : any +>length : any +>0 : number +>taMajorTick.length : any +>taMajorTick : any +>length : any +>0 : number + + taMinorTick.length > 0 ? taMinorTick.length : 0) + +>taMinorTick.length > 0 ? taMinorTick.length : 0 : any +>taMinorTick.length > 0 : boolean +>taMinorTick.length : any +>taMinorTick : any +>length : any +>0 : number +>taMinorTick.length : any +>taMinorTick : any +>length : any +>0 : number + + tsize + taTitleGap; +>tsize : any +>taTitleGap : any + + axisVector = { x: isRtl ? -1 : 1, y: 0 }; // chart mirroring +>axisVector = { x: isRtl ? -1 : 1, y: 0 } : { x: number; y: number; } +>axisVector : any +>{ x: isRtl ? -1 : 1, y: 0 } : { x: number; y: number; } +>x : number +>isRtl ? -1 : 1 : number +>isRtl : any +>-1 : number +>1 : number +>1 : number +>y : number +>0 : number + + switch (rotation) { +>rotation : number + + default: + if (rotation < (90 - centerAnchorLimit)) { +>rotation < (90 - centerAnchorLimit) : boolean +>rotation : number +>(90 - centerAnchorLimit) : number +>90 - centerAnchorLimit : number +>90 : number +>centerAnchorLimit : any + + labelOffset.y = leftBottom ? size : 0; +>labelOffset.y = leftBottom ? size : 0 : any +>labelOffset.y : any +>labelOffset : any +>y : any +>leftBottom ? size : 0 : any +>leftBottom : boolean +>size : any +>0 : number + + } else if (rotation < (90 + centerAnchorLimit)) { +>rotation < (90 + centerAnchorLimit) : boolean +>rotation : number +>(90 + centerAnchorLimit) : any +>90 + centerAnchorLimit : any +>90 : number +>centerAnchorLimit : any + + labelOffset.x = -size * 0.4; +>labelOffset.x = -size * 0.4 : number +>labelOffset.x : any +>labelOffset : any +>x : any +>-size * 0.4 : number +>-size : number +>size : any +>0.4 : number + + } else if (rotation < 180) { +>rotation < 180 : boolean +>rotation : number +>180 : number + + labelOffset.y = leftBottom ? 0 : -size; +>labelOffset.y = leftBottom ? 0 : -size : number +>labelOffset.y : any +>labelOffset : any +>y : any +>leftBottom ? 0 : -size : number +>leftBottom : boolean +>0 : number +>-size : number +>size : any + + } else if (rotation < (270 - centerAnchorLimit)) { +>rotation < (270 - centerAnchorLimit) : boolean +>rotation : number +>(270 - centerAnchorLimit) : number +>270 - centerAnchorLimit : number +>270 : number +>centerAnchorLimit : any + + labelOffset.y = leftBottom ? 0 : -size; +>labelOffset.y = leftBottom ? 0 : -size : number +>labelOffset.y : any +>labelOffset : any +>y : any +>leftBottom ? 0 : -size : number +>leftBottom : boolean +>0 : number +>-size : number +>size : any + + } else if (rotation < (270 + centerAnchorLimit)) { +>rotation < (270 + centerAnchorLimit) : boolean +>rotation : number +>(270 + centerAnchorLimit) : any +>270 + centerAnchorLimit : any +>270 : number +>centerAnchorLimit : any + + labelOffset.y = leftBottom ? size * 0.4 : 0; +>labelOffset.y = leftBottom ? size * 0.4 : 0 : number +>labelOffset.y : any +>labelOffset : any +>y : any +>leftBottom ? size * 0.4 : 0 : number +>leftBottom : boolean +>size * 0.4 : number +>size : any +>0.4 : number +>0 : number + + } else { + labelOffset.y = leftBottom ? size : 0; +>labelOffset.y = leftBottom ? size : 0 : any +>labelOffset.y : any +>labelOffset : any +>y : any +>leftBottom ? size : 0 : any +>leftBottom : boolean +>size : any +>0 : number + } + } + + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; +>titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0 : number +>titleRotation : number +>(taTitleOrientation && taTitleOrientation == "away") ? 180 : 0 : number +>(taTitleOrientation && taTitleOrientation == "away") : boolean +>taTitleOrientation && taTitleOrientation == "away" : boolean +>taTitleOrientation : any +>taTitleOrientation == "away" : boolean +>taTitleOrientation : any +>"away" : string +>180 : number +>0 : number + + titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); +>titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize) : any +>titlePos.y : any +>titlePos : any +>y : any +>offsets.t - titleOffset + (titleRotation ? 0 : tsize) : any +>offsets.t - titleOffset : number +>offsets.t : any +>offsets : any +>t : any +>titleOffset : any +>(titleRotation ? 0 : tsize) : any +>titleRotation ? 0 : tsize : any +>titleRotation : number +>0 : number +>tsize : any + + switch (labelAlign) { +>labelAlign : any + + case "start": +>"start" : string + + labelAlign = "end"; +>labelAlign = "end" : string +>labelAlign : any +>"end" : string + + break; + case "end": +>"end" : string + + labelAlign = "start"; +>labelAlign = "start" : string +>labelAlign : any +>"start" : string + + break; + case "middle": +>"middle" : string + + labelOffset.y -= size; +>labelOffset.y -= size : number +>labelOffset.y : any +>labelOffset : any +>y : any +>size : any + + break; + } + + let _ = rotation; +>_ : number +>rotation : number +} + diff --git a/tests/baselines/reference/typeGuardEnums.types b/tests/baselines/reference/typeGuardEnums.types index 2bef6915046..1d39a81d78a 100644 --- a/tests/baselines/reference/typeGuardEnums.types +++ b/tests/baselines/reference/typeGuardEnums.types @@ -27,7 +27,7 @@ else { if (typeof x !== "number") { >typeof x !== "number" : boolean >typeof x : string ->x : number | string +>x : number | string | E | V >"number" : string x; // string @@ -35,6 +35,6 @@ if (typeof x !== "number") { } else { x; // number|E|V ->x : number +>x : number | E | V } diff --git a/tests/baselines/reference/typeGuardNesting.types b/tests/baselines/reference/typeGuardNesting.types index 2b18e232412..afd060284d9 100644 --- a/tests/baselines/reference/typeGuardNesting.types +++ b/tests/baselines/reference/typeGuardNesting.types @@ -34,7 +34,7 @@ if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'stri >(typeof strOrBool === 'boolean') : boolean >typeof strOrBool === 'boolean' : boolean >typeof strOrBool : string ->strOrBool : string | boolean +>strOrBool : boolean | string >'boolean' : string >strOrBool : boolean >false : boolean @@ -56,7 +56,7 @@ if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'stri >(typeof strOrBool !== 'string') : boolean >typeof strOrBool !== 'string' : boolean >typeof strOrBool : string ->strOrBool : string | boolean +>strOrBool : boolean | string >'string' : string >strOrBool : boolean >false : boolean @@ -94,7 +94,7 @@ if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boole >(typeof strOrBool === 'boolean') : boolean >typeof strOrBool === 'boolean' : boolean >typeof strOrBool : string ->strOrBool : string | boolean +>strOrBool : boolean | string >'boolean' : string >strOrBool : boolean >false : boolean @@ -116,7 +116,7 @@ if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boole >(typeof strOrBool !== 'string') : boolean >typeof strOrBool !== 'string' : boolean >typeof strOrBool : string ->strOrBool : string | boolean +>strOrBool : boolean | string >'string' : string >strOrBool : boolean >false : boolean diff --git a/tests/baselines/reference/typeGuardTautologicalConsistiency.types b/tests/baselines/reference/typeGuardTautologicalConsistiency.types index cf9e82d9fd2..256eaec8293 100644 --- a/tests/baselines/reference/typeGuardTautologicalConsistiency.types +++ b/tests/baselines/reference/typeGuardTautologicalConsistiency.types @@ -23,7 +23,7 @@ if (typeof stringOrNumber === "number" && typeof stringOrNumber !== "number") { >typeof stringOrNumber === "number" && typeof stringOrNumber !== "number" : boolean >typeof stringOrNumber === "number" : boolean >typeof stringOrNumber : string ->stringOrNumber : number | string +>stringOrNumber : string | number >"number" : string >typeof stringOrNumber !== "number" : boolean >typeof stringOrNumber : string diff --git a/tests/cases/compiler/controlFlowCaching.ts b/tests/cases/compiler/controlFlowCaching.ts new file mode 100644 index 00000000000..6510a89b06e --- /dev/null +++ b/tests/cases/compiler/controlFlowCaching.ts @@ -0,0 +1,68 @@ +// @strictNullChecks: true + +// Repro for #8401 + +function f(dim, offsets, arr, acommon, centerAnchorLimit, g, has, lin) { + var isRtl = this._isRtl(); // chart mirroring + // prepare variable + var o = this.opt, ta = this.chart.theme.axis, position = o.position, + leftBottom = position !== "rightOrTop", rotation = o.rotation % 360, + start, stop, titlePos, titleRotation = 0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, + labelGap = this.chart.theme.axis.tick.labelGap, + taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), + taTitleFont = o.titleFont || (ta.title && ta.title.font), + taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", + taTitleFontColor = o.titleFontColor || (ta.title && ta.title.fontColor) || "black", + taTitleGap = (o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15, + taTitleOrientation = o.titleOrientation || (ta.title && ta.title.orientation) || "axis", + taMajorTick = this.chart.theme.getTick("major", o), + taMinorTick = this.chart.theme.getTick("minor", o), + taMicroTick = this.chart.theme.getTick("micro", o), + + taStroke = "stroke" in o ? o.stroke : ta.stroke, + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, + cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), + sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), + tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; + if (rotation < 0) { + rotation += 360; + } + var cachedLabelW = this._getMaxLabelSize(); + cachedLabelW = cachedLabelW && cachedLabelW.majLabelW; + titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, + taMinorTick.length > 0 ? taMinorTick.length : 0) + + tsize + taTitleGap; + axisVector = { x: isRtl ? -1 : 1, y: 0 }; // chart mirroring + switch (rotation) { + default: + if (rotation < (90 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? size : 0; + } else if (rotation < (90 + centerAnchorLimit)) { + labelOffset.x = -size * 0.4; + } else if (rotation < 180) { + labelOffset.y = leftBottom ? 0 : -size; + } else if (rotation < (270 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? 0 : -size; + } else if (rotation < (270 + centerAnchorLimit)) { + labelOffset.y = leftBottom ? size * 0.4 : 0; + } else { + labelOffset.y = leftBottom ? size : 0; + } + } + + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; + titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); + switch (labelAlign) { + case "start": + labelAlign = "end"; + break; + case "end": + labelAlign = "start"; + break; + case "middle": + labelOffset.y -= size; + break; + } + + let _ = rotation; +}