From 3bb7be96fa1b048e7c2ef4409611046c046c4a25 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 16 Jun 2015 16:03:45 -0700 Subject: [PATCH 01/11] Scan less during classification. --- src/harness/runner.ts | 1 - src/services/services.ts | 28 +++++++++++-------- .../syntacticClassificationsDocComment3.ts | 1 - 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/harness/runner.ts b/src/harness/runner.ts index 42c2d5667c4..1e9c6470421 100644 --- a/src/harness/runner.ts +++ b/src/harness/runner.ts @@ -49,7 +49,6 @@ if (testConfigFile !== '') { if (!option) { continue; } - ts.sys.write("Option: " + option + "\r\n"); switch (option) { case 'compiler': runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance)); diff --git a/src/services/services.ts b/src/services/services.ts index 3939e62cfd2..cebe79630d0 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -6132,13 +6132,7 @@ namespace ts { result.push(type); } - function classifyLeadingTrivia(token: Node): void { - let tokenStart = skipTrivia(sourceFile.text, token.pos, /*stopAfterLineBreak:*/ false); - if (tokenStart === token.pos) { - return; - } - - // token has trivia. Classify them appropriately. + function classifyLeadingTriviaAndGetTokenStart(token: Node): number { triviaScanner.setTextPos(token.pos); while (true) { let start = triviaScanner.getTextPos(); @@ -6148,13 +6142,23 @@ namespace ts { // The moment we get something that isn't trivia, then stop processing. if (!isTrivia(kind)) { - return; + return start; + } + + // Don't bother with newlines/whitespace. + if (kind === SyntaxKind.NewLineTrivia || kind === SyntaxKind.WhitespaceTrivia) { + continue; } // Only bother with the trivia if it at least intersects the span of interest. if (textSpanIntersectsWith(span, start, width)) { if (isComment(kind)) { classifyComment(token, kind, start, width); + + // Classifying a comment might cause us to reuse the trivia scanner + // (because of jsdoc comments). So after we classify the comment make + // sure we set the scanner position back to where it needs to be. + triviaScanner.setTextPos(end); continue; } @@ -6292,12 +6296,14 @@ namespace ts { } function classifyToken(token: Node): void { - classifyLeadingTrivia(token); + let tokenStart = classifyLeadingTriviaAndGetTokenStart(token); - if (token.getWidth() > 0) { + let tokenWidth = token.getEnd() - tokenStart; + Debug.assert(tokenWidth >= 0); + if (tokenWidth > 0) { let type = classifyTokenType(token.kind, token); if (type) { - pushClassification(token.getStart(), token.getWidth(), type); + pushClassification(tokenStart, tokenWidth, type); } } } diff --git a/tests/cases/fourslash/syntacticClassificationsDocComment3.ts b/tests/cases/fourslash/syntacticClassificationsDocComment3.ts index bdc7faa470e..6aca8cc415f 100644 --- a/tests/cases/fourslash/syntacticClassificationsDocComment3.ts +++ b/tests/cases/fourslash/syntacticClassificationsDocComment3.ts @@ -14,7 +14,6 @@ verify.syntacticClassificationsAre( c.punctuation("{"), c.keyword("number"), c.comment(" /* } */"), - c.comment("/* } */"), c.keyword("var"), c.text("v"), c.punctuation(";")); From d206f62adb02488ecf693097025d441c7112a673 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 16 Jun 2015 17:55:36 -0700 Subject: [PATCH 02/11] Squueze perf in syntactic classification. --- src/compiler/scanner.ts | 23 +++++++++++++++ src/compiler/utilities.ts | 6 ++++ src/services/services.ts | 60 +++++++++++++++++++++------------------ 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index fd8883045fe..fcba1da9dca 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -376,8 +376,31 @@ namespace ts { return ch >= CharacterCodes._0 && ch <= CharacterCodes._7; } + export function couldStartTrivia(text: string, pos: number): boolean { + // Keep in sync with skipTrivia + let ch = text.charCodeAt(pos); + switch (ch) { + case CharacterCodes.carriageReturn: + case CharacterCodes.lineFeed: + case CharacterCodes.tab: + case CharacterCodes.verticalTab: + case CharacterCodes.formFeed: + case CharacterCodes.space: + case CharacterCodes.slash: + // starts of normal trivia + case CharacterCodes.lessThan: + case CharacterCodes.equals: + case CharacterCodes.greaterThan: + // Starts of conflict marker trivia + return true; + default: + return ch > CharacterCodes.maxAsciiCharacter; + } + } + /* @internal */ export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean): number { + // Keep in sync with couldStartTrivia while (true) { let ch = text.charCodeAt(pos); switch (ch) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7251867f070..6d95e7eac83 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2083,6 +2083,12 @@ namespace ts { return start <= textSpanEnd(span) && end >= span.start; } + export function textSpanIntersectsWith2(start1: number, length1: number, start2: number, length2: number) { + let end1 = start1 + length1; + let end2 = start2 + length2; + return start2 <= end1 && end2 >= start1; + } + export function textSpanIntersectsWithPosition(span: TextSpan, position: number) { return position <= textSpanEnd(span) && position >= span.start; } diff --git a/src/services/services.ts b/src/services/services.ts index cebe79630d0..2f529ddab7b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -167,7 +167,7 @@ namespace ts { } public getFullWidth(): number { - return this.end - this.getFullStart(); + return this.end - this.pos; } public getLeadingTriviaWidth(sourceFile?: SourceFile): number { @@ -6116,6 +6116,8 @@ namespace ts { function getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications { // doesn't use compiler - no need to synchronize with host let sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); + let spanStart = span.start; + let spanLength = span.length; // Make a scanner we can get trivia from. let triviaScanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ false, sourceFile.text); @@ -6136,6 +6138,11 @@ namespace ts { triviaScanner.setTextPos(token.pos); while (true) { let start = triviaScanner.getTextPos(); + // only bother scanning if we have something that could be trivia. + if (!couldStartTrivia(sourceFile.text, start)) { + return start; + } + let kind = triviaScanner.scan(); let end = triviaScanner.getTextPos(); let width = end - start; @@ -6151,33 +6158,31 @@ namespace ts { } // Only bother with the trivia if it at least intersects the span of interest. - if (textSpanIntersectsWith(span, start, width)) { - if (isComment(kind)) { - classifyComment(token, kind, start, width); + if (isComment(kind)) { + classifyComment(token, kind, start, width); - // Classifying a comment might cause us to reuse the trivia scanner - // (because of jsdoc comments). So after we classify the comment make - // sure we set the scanner position back to where it needs to be. - triviaScanner.setTextPos(end); + // Classifying a comment might cause us to reuse the trivia scanner + // (because of jsdoc comments). So after we classify the comment make + // sure we set the scanner position back to where it needs to be. + triviaScanner.setTextPos(end); + continue; + } + + if (kind === SyntaxKind.ConflictMarkerTrivia) { + let text = sourceFile.text; + let ch = text.charCodeAt(start); + + // for the <<<<<<< and >>>>>>> markers, we just add them in as comments + // in the classification stream. + if (ch === CharacterCodes.lessThan || ch === CharacterCodes.greaterThan) { + pushClassification(start, width, ClassificationType.comment); continue; } - if (kind === SyntaxKind.ConflictMarkerTrivia) { - let text = sourceFile.text; - let ch = text.charCodeAt(start); - - // for the <<<<<<< and >>>>>>> markers, we just add them in as comments - // in the classification stream. - if (ch === CharacterCodes.lessThan || ch === CharacterCodes.greaterThan) { - pushClassification(start, width, ClassificationType.comment); - continue; - } - - // for the ======== add a comment for the first line, and then lex all - // subsequent lines up until the end of the conflict marker. - Debug.assert(ch === CharacterCodes.equals); - classifyDisabledMergeCode(text, start, end); - } + // for the ======== add a comment for the first line, and then lex all + // subsequent lines up until the end of the conflict marker. + Debug.assert(ch === CharacterCodes.equals); + classifyDisabledMergeCode(text, start, end); } } } @@ -6298,7 +6303,7 @@ namespace ts { function classifyToken(token: Node): void { let tokenStart = classifyLeadingTriviaAndGetTokenStart(token); - let tokenWidth = token.getEnd() - tokenStart; + let tokenWidth = token.end - tokenStart; Debug.assert(tokenWidth >= 0); if (tokenWidth > 0) { let type = classifyTokenType(token.kind, token); @@ -6408,9 +6413,10 @@ namespace ts { } // Ignore nodes that don't intersect the original span to classify. - if (textSpanIntersectsWith(span, element.getFullStart(), element.getFullWidth())) { + if (textSpanIntersectsWith2(spanStart, spanLength, element.pos, element.getFullWidth())) { let children = element.getChildren(sourceFile); - for (let child of children) { + for (let i = 0, n = children.length; i < n; i++) { + let child = children[i]; if (isToken(child)) { classifyToken(child); } From 752e0ba00384a8664d7f6cafec588e88c5f30226 Mon Sep 17 00:00:00 2001 From: Tingan Ho Date: Thu, 18 Jun 2015 23:04:05 +0800 Subject: [PATCH 03/11] Fixes type predicate crash bug --- src/compiler/checker.ts | 1 + .../expressions/typeGuards/typeGuardFunctionErrors.ts | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index efc3e32166e..0861cd96a01 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5761,6 +5761,7 @@ namespace ts { let signature = getResolvedSignature(expr); if (signature.typePredicate && + expr.arguments[signature.typePredicate.parameterIndex] && getSymbolAtLocation(expr.arguments[signature.typePredicate.parameterIndex]) === symbol) { if (!assumeTrue) { diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts index e157b2afe36..688099280c5 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts @@ -23,6 +23,10 @@ function hasMissingIsKeyword(): x { return true; } +function hasMissingParameter(): x is A { + return true; +} + function hasMissingTypeInTypeGuardType(x): x is { return true; } @@ -132,4 +136,10 @@ function b6([a, b, p1], p2, p3): p1 is A { function b7({a, b, c: {p1}}, p2, p3): p1 is A { return true; +} + +// Should not crash the compiler +var x: A; +if (hasMissingParameter()) { + x.propA; } \ No newline at end of file From 84bb38415f2e0f47c8197ac6491abace54c58742 Mon Sep 17 00:00:00 2001 From: Tingan Ho Date: Thu, 18 Jun 2015 23:23:47 +0800 Subject: [PATCH 04/11] Accepts baselines --- .../typeGuardFunctionErrors.errors.txt | 69 +++++++++++-------- .../reference/typeGuardFunctionErrors.js | 18 +++++ 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt index 1542bbe0fee..499a51035f2 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt +++ b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt @@ -1,42 +1,43 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(15,12): error TS2322: Type 'string' is not assignable to type 'boolean'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(22,33): error TS2304: Cannot find name 'x'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(26,10): error TS2391: Function implementation is missing or not immediately following the declaration. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(27,5): error TS1131: Property or signature expected. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(28,1): error TS1128: Declaration or statement expected. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(30,38): error TS1225: Cannot find parameter 'x'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(34,51): error TS2322: Type 'B' is not assignable to type 'A'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(26,33): error TS1225: Cannot find parameter 'x'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(30,10): error TS2391: Function implementation is missing or not immediately following the declaration. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(31,5): error TS1131: Property or signature expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(32,1): error TS1128: Declaration or statement expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(34,38): error TS1225: Cannot find parameter 'x'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(38,51): error TS2322: Type 'B' is not assignable to type 'A'. Property 'propA' is missing in type 'B'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(38,56): error TS2322: Type 'number' is not assignable to type 'string'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(42,56): error TS2322: Type 'T[]' is not assignable to type 'string'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(56,7): error TS2339: Property 'propB' does not exist on type 'A'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(61,7): error TS2339: Property 'propB' does not exist on type 'A'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(66,7): error TS2339: Property 'propB' does not exist on type 'A'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(71,46): error TS2345: Argument of type '(p1: any) => boolean' is not assignable to parameter of type '(p1: any) => boolean'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(42,56): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(46,56): error TS2322: Type 'T[]' is not assignable to type 'string'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(60,7): error TS2339: Property 'propB' does not exist on type 'A'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(65,7): error TS2339: Property 'propB' does not exist on type 'A'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(70,7): error TS2339: Property 'propB' does not exist on type 'A'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(75,46): error TS2345: Argument of type '(p1: any) => boolean' is not assignable to parameter of type '(p1: any) => boolean'. Type predicate 'p1 is C' is not assignable to 'p1 is B'. Type 'C' is not assignable to type 'B'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(75,1): error TS2322: Type '(p1: any, p2: any) => boolean' is not assignable to type '(p1: any, p2: any) => boolean'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(79,1): error TS2322: Type '(p1: any, p2: any) => boolean' is not assignable to type '(p1: any, p2: any) => boolean'. Signature '(p1: any, p2: any): boolean' must have a type predicate. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(81,1): error TS2322: Type '(p1: any, p2: any) => boolean' is not assignable to type '(p1: any, p2: any) => boolean'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(85,1): error TS2322: Type '(p1: any, p2: any) => boolean' is not assignable to type '(p1: any, p2: any) => boolean'. Type predicate 'p2 is A' is not assignable to 'p1 is A'. Parameter 'p2' is not in the same position as parameter 'p1'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(87,1): error TS2322: Type '(p1: any, p2: any, p3: any) => boolean' is not assignable to type '(p1: any, p2: any) => boolean'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(92,9): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(93,16): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(94,20): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(100,25): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(101,16): error TS2409: Return type of constructor signature must be assignable to the instance type of the class -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(103,20): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(106,20): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(107,16): error TS2408: Setters cannot return a value. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(112,18): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(116,22): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(120,20): error TS1229: A type predicate cannot reference a rest parameter. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(125,34): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(91,1): error TS2322: Type '(p1: any, p2: any, p3: any) => boolean' is not assignable to type '(p1: any, p2: any) => boolean'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(96,9): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(97,16): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(98,20): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(104,25): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(105,16): error TS2409: Return type of constructor signature must be assignable to the instance type of the class +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(107,20): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(110,20): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(111,16): error TS2408: Setters cannot return a value. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(116,18): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(120,22): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(124,20): error TS1229: A type predicate cannot reference a rest parameter. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(129,34): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(133,39): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(133,34): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. -==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (30 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (31 errors) ==== class A { propA: number; @@ -66,6 +67,12 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(133,39 return true; } + function hasMissingParameter(): x is A { + ~ +!!! error TS1225: Cannot find parameter 'x'. + return true; + } + function hasMissingTypeInTypeGuardType(x): x is { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2391: Function implementation is missing or not immediately following the declaration. @@ -237,4 +244,10 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(133,39 ~~ !!! error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. return true; + } + + // Should not crash the compiler + var x: A; + if (hasMissingParameter()) { + x.propA; } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardFunctionErrors.js b/tests/baselines/reference/typeGuardFunctionErrors.js index 5b0979ac576..4923f544345 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.js +++ b/tests/baselines/reference/typeGuardFunctionErrors.js @@ -24,6 +24,10 @@ function hasMissingIsKeyword(): x { return true; } +function hasMissingParameter(): x is A { + return true; +} + function hasMissingTypeInTypeGuardType(x): x is { return true; } @@ -133,6 +137,12 @@ function b6([a, b, p1], p2, p3): p1 is A { function b7({a, b, c: {p1}}, p2, p3): p1 is A { return true; +} + +// Should not crash the compiler +var x: A; +if (hasMissingParameter()) { + x.propA; } //// [typeGuardFunctionErrors.js] @@ -167,6 +177,9 @@ function hasTypeGuardTypeInsideTypeGuardType(x) { function hasMissingIsKeyword() { return true; } +function hasMissingParameter() { + return true; +} return true; function hasNonMatchingParameter(y) { return true; @@ -260,3 +273,8 @@ function b7(_a, p2, p3) { var a = _a.a, b = _a.b, p1 = _a.c.p1; return true; } +// Should not crash the compiler +var x; +if (hasMissingParameter()) { + x.propA; +} From 151306f42374369f1e9bc94d63de2af63b5f1381 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 18 Jun 2015 08:30:26 -0700 Subject: [PATCH 05/11] PR feedback. --- src/compiler/utilities.ts | 2 +- src/services/services.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 6d95e7eac83..85216444626 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2083,7 +2083,7 @@ namespace ts { return start <= textSpanEnd(span) && end >= span.start; } - export function textSpanIntersectsWith2(start1: number, length1: number, start2: number, length2: number) { + export function decodedTextSpanIntersectsWith(start1: number, length1: number, start2: number, length2: number) { let end1 = start1 + length1; let end2 = start2 + length2; return start2 <= end1 && end2 >= start1; diff --git a/src/services/services.ts b/src/services/services.ts index 2f529ddab7b..99b08254c1a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -6413,7 +6413,7 @@ namespace ts { } // Ignore nodes that don't intersect the original span to classify. - if (textSpanIntersectsWith2(spanStart, spanLength, element.pos, element.getFullWidth())) { + if (decodedTextSpanIntersectsWith(spanStart, spanLength, element.pos, element.getFullWidth())) { let children = element.getChildren(sourceFile); for (let i = 0, n = children.length; i < n; i++) { let child = children[i]; From ede80c1de22b40ca13f09cd8e6019f9bcb6cbc43 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 18 Jun 2015 09:06:47 -0700 Subject: [PATCH 06/11] Don't use spread operator when pushing arrays onto other arrays. Spreading emits as ".push.apply(reciver, values)". This pushes every elements in values onto the stack before calling the function. This can easily stack overflow if the amount of values is high (i hit this with ~10k values on my own system). --- src/services/services.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index bab5b7707ef..bed1f9b6bb9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1789,11 +1789,6 @@ namespace ts { sourceFile.moduleName = moduleName; } - // Store syntactic diagnostics - if (diagnostics && sourceFile.parseDiagnostics) { - diagnostics.push(...sourceFile.parseDiagnostics); - } - let newLine = getNewLineCharacter(options); // Output @@ -1815,9 +1810,8 @@ namespace ts { var program = createProgram([inputFileName], options, compilerHost); - if (diagnostics) { - diagnostics.push(...program.getCompilerOptionsDiagnostics()); - } + addRange(/*to*/ diagnostics, /*from*/ sourceFile.parseDiagnostics); + addRange(/*to*/ diagnostics, /*from*/ program.getCompilerOptionsDiagnostics()); // Emit program.emit(); @@ -4188,7 +4182,7 @@ namespace ts { var result: DefinitionInfo[] = []; forEach((type).types, t => { if (t.symbol) { - result.push(...getDefinitionFromSymbol(t.symbol, node)); + addRange(/*to*/ result, /*from*/ getDefinitionFromSymbol(t.symbol, node)); } }); return result; From ef7d1136b80970f0be11ad37f66fe15fb583d672 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 18 Jun 2015 09:32:52 -0700 Subject: [PATCH 07/11] Make it so all our diagnostics APIs return an independent set of diagnostics. In order to get all diagnostics, you must call all the APIs. And no APIs return diagnostics produced by other APIs. This is how things were before hte addition of the getCompletionOptionsDiagnostics API, and i'm returning things to that state. --- src/compiler/checker.ts | 16 ++++++++-------- src/compiler/program.ts | 12 ++++-------- src/compiler/types.ts | 4 ++-- src/services/services.ts | 7 +++++-- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index efc3e32166e..68a0916f743 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -159,6 +159,14 @@ namespace ts { } }; + let subtypeRelation: Map = {}; + let assignableRelation: Map = {}; + let identityRelation: Map = {}; + + initializeTypeChecker(); + + return checker; + function getEmitResolver(sourceFile?: SourceFile) { // Ensure we have all the type information in place for this file so that all the // emitter questions of this resolver will return the right information. @@ -4221,10 +4229,6 @@ namespace ts { // TYPE CHECKING - let subtypeRelation: Map = {}; - let assignableRelation: Map = {}; - let identityRelation: Map = {}; - function isTypeIdenticalTo(source: Type, target: Type): boolean { return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined); } @@ -13614,9 +13618,5 @@ namespace ts { return true; } } - - initializeTypeChecker(); - - return checker; } } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 800e2238a54..87b7ab26c48 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -105,7 +105,7 @@ namespace ts { } export function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile): Diagnostic[] { - let diagnostics = program.getSyntacticDiagnostics(sourceFile).concat(program.getGlobalDiagnostics()).concat(program.getSemanticDiagnostics(sourceFile)); + let diagnostics = program.getOptionsDiagnostics().concat(program.getSyntacticDiagnostics(sourceFile)).concat(program.getGlobalDiagnostics()).concat(program.getSemanticDiagnostics(sourceFile)); if (program.getCompilerOptions().declaration) { diagnostics.concat(program.getDeclarationDiagnostics(sourceFile)); @@ -177,10 +177,10 @@ namespace ts { getSourceFiles: () => files, getCompilerOptions: () => options, getSyntacticDiagnostics, + getOptionsDiagnostics, getGlobalDiagnostics, getSemanticDiagnostics, getDeclarationDiagnostics, - getCompilerOptionsDiagnostics, getTypeChecker, getClassifiableNames, getDiagnosticsProducingTypeChecker, @@ -311,19 +311,15 @@ namespace ts { } } - function getCompilerOptionsDiagnostics(): Diagnostic[] { + function getOptionsDiagnostics(): Diagnostic[] { let allDiagnostics: Diagnostic[] = []; addRange(allDiagnostics, diagnostics.getGlobalDiagnostics()); return sortAndDeduplicateDiagnostics(allDiagnostics); } function getGlobalDiagnostics(): Diagnostic[] { - let typeChecker = getDiagnosticsProducingTypeChecker(); - let allDiagnostics: Diagnostic[] = []; - addRange(allDiagnostics, typeChecker.getGlobalDiagnostics()); - addRange(allDiagnostics, diagnostics.getGlobalDiagnostics()); - + addRange(allDiagnostics, getDiagnosticsProducingTypeChecker().getGlobalDiagnostics()); return sortAndDeduplicateDiagnostics(allDiagnostics); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c39426a2e22..f9a2fa3b7b4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1212,11 +1212,11 @@ namespace ts { */ emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback): EmitResult; - getSyntacticDiagnostics(sourceFile?: SourceFile): Diagnostic[]; + getOptionsDiagnostics(): Diagnostic[]; getGlobalDiagnostics(): Diagnostic[]; + getSyntacticDiagnostics(sourceFile?: SourceFile): Diagnostic[]; getSemanticDiagnostics(sourceFile?: SourceFile): Diagnostic[]; getDeclarationDiagnostics(sourceFile?: SourceFile): Diagnostic[]; - /* @internal */ getCompilerOptionsDiagnostics(): Diagnostic[]; /** * Gets a type checker that can be used to semantically analyze source fils in the program. diff --git a/src/services/services.ts b/src/services/services.ts index bab5b7707ef..f3d7a80302d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -973,6 +973,9 @@ namespace ts { getSyntacticDiagnostics(fileName: string): Diagnostic[]; getSemanticDiagnostics(fileName: string): Diagnostic[]; + + // TODO: Rename this to getProgramDiagnostics to better indicate that these are any + // diagnostics present for the program level, and not just 'options' diagnostics. getCompilerOptionsDiagnostics(): Diagnostic[]; /** @@ -1816,7 +1819,7 @@ namespace ts { var program = createProgram([inputFileName], options, compilerHost); if (diagnostics) { - diagnostics.push(...program.getCompilerOptionsDiagnostics()); + diagnostics.push(...program.getOptionsDiagnostics()); } // Emit @@ -2796,7 +2799,7 @@ namespace ts { function getCompilerOptionsDiagnostics() { synchronizeHostData(); - return program.getGlobalDiagnostics(); + return program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics()); } /// Completion From 3f40e47fcce66ad427126feeb7c82eb1c944b476 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 18 Jun 2015 09:45:32 -0700 Subject: [PATCH 08/11] Don't access diagnostics directly. Use the supported Program API for them. --- src/services/services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index b97e2ebb9f8..7b94384a068 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1813,7 +1813,7 @@ namespace ts { var program = createProgram([inputFileName], options, compilerHost); - addRange(/*to*/ diagnostics, /*from*/ sourceFile.parseDiagnostics); + addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile)); addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics()); // Emit From 22f704c0a35bf3df1ba14bfb49b0e9e63ddd5bbb Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 18 Jun 2015 10:14:13 -0700 Subject: [PATCH 09/11] Fix capitalization --- tests/perftsc.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/perftsc.ts b/tests/perftsc.ts index e0189896a15..89bff1cc006 100644 --- a/tests/perftsc.ts +++ b/tests/perftsc.ts @@ -10,7 +10,7 @@ if (perftest.hasLogIOFlag()) { var content = perftest.readFile(s); return content !== undefined ? ts.createSourceFile(s, content, v) : undefined; }, - getDefaultLibFilename: () => ts.combinePaths(ts.getDirectoryPath(ts.normalizePath(perftest.getExecutingFilePath())), "lib.d.ts"), + getDefaultLibFileName: () => ts.combinePaths(ts.getDirectoryPath(ts.normalizePath(perftest.getExecutingFilePath())), "lib.d.ts"), writeFile: (f: string, content: string) => { throw new Error("Unexpected operation: writeFile"); }, getCurrentDirectory: () => perftest.getCurrentDirectory(), getCanonicalFileName: (f: string) => ts.sys.useCaseSensitiveFileNames ? f : f.toLowerCase(), @@ -19,8 +19,8 @@ if (perftest.hasLogIOFlag()) { }; var commandLine = ts.parseCommandLine(perftest.getArgsWithoutLogIOFlag()); - var program = ts.createProgram(commandLine.filenames, commandLine.options, compilerHost); - var fileNames = program.getSourceFiles().map(f => f.filename); + var program = ts.createProgram(commandLine.fileNames, commandLine.options, compilerHost); + var fileNames = program.getSourceFiles().map(f => f.fileName); perftest.writeIOLog(fileNames); } else { From c4f65f8bbf0b9d54311af782d989144c47cc2417 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 18 Jun 2015 11:00:50 -0700 Subject: [PATCH 10/11] PR feedback. --- src/compiler/program.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 87b7ab26c48..2af9ad9987d 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -105,7 +105,10 @@ namespace ts { } export function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile): Diagnostic[] { - let diagnostics = program.getOptionsDiagnostics().concat(program.getSyntacticDiagnostics(sourceFile)).concat(program.getGlobalDiagnostics()).concat(program.getSemanticDiagnostics(sourceFile)); + let diagnostics = program.getOptionsDiagnostics().concat( + program.getSyntacticDiagnostics(sourceFile), + program.getGlobalDiagnostics(), + program.getSemanticDiagnostics(sourceFile)); if (program.getCompilerOptions().declaration) { diagnostics.concat(program.getDeclarationDiagnostics(sourceFile)); From 38a54bc0b90f7145abd45e186e74e4dc78ad705c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 18 Jun 2015 14:16:00 -0700 Subject: [PATCH 11/11] Fix incremental parsing issue. We were moving a method-declaration called "constructor" into a class. This is incorrect as that same code should be parsed as a constructor-declaration now that it is in the class context. --- src/compiler/parser.ts | 10 +++++++++- tests/cases/unittests/incrementalParser.ts | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index a507125d4bb..a94341dd9c4 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1491,12 +1491,20 @@ namespace ts { switch (node.kind) { case SyntaxKind.Constructor: case SyntaxKind.IndexSignature: - case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.PropertyDeclaration: case SyntaxKind.SemicolonClassElement: return true; + case SyntaxKind.MethodDeclaration: + // Method declarations are not necessarily reusable. An object-literal + // may have a method calls "constructor(...)" and we must reparse that + // into an actual .ConstructorDeclaration. + let methodDeclaration = node; + let nameIsConstructor = methodDeclaration.name.kind === SyntaxKind.Identifier && + (methodDeclaration.name).originalKeywordKind === SyntaxKind.ConstructorKeyword; + + return !nameIsConstructor; } } diff --git a/tests/cases/unittests/incrementalParser.ts b/tests/cases/unittests/incrementalParser.ts index a0b361a87f4..1ef10eaa712 100644 --- a/tests/cases/unittests/incrementalParser.ts +++ b/tests/cases/unittests/incrementalParser.ts @@ -713,6 +713,24 @@ module m3 { }\ compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 4); }); + it('Do not move constructors from class to object-literal.', () => { + var source = "class C { public constructor() { } public constructor() { } public constructor() { } }" + + var oldText = ScriptSnapshot.fromString(source); + var newTextAndChange = withChange(oldText, 0, "class C".length, "var v ="); + + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0); + }); + + it('Do not move methods called "constructor" from object literal to class', () => { + var source = "var v = { public constructor() { } public constructor() { } public constructor() { } }" + + var oldText = ScriptSnapshot.fromString(source); + var newTextAndChange = withChange(oldText, 0, "var v =".length, "class C"); + + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 0); + }); + it('Moving index signatures from class to interface',() => { var source = "class C { public [a: number]: string; public [a: number]: string; public [a: number]: string }"