diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8c383368df2..d3d4ae2088e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -164,6 +164,13 @@ module ts { var diagnostics: Diagnostic[] = []; var diagnosticsModified: boolean = false; + // Grammar checking + var sourceText: string = undefined; + var scanner: Scanner = undefined; + var hasParserError: boolean; + var grammarDiagnostics: Diagnostic[]; + var sourceFile: SourceFile; + function addDiagnostic(diagnostic: Diagnostic) { diagnostics.push(diagnostic); diagnosticsModified = true; @@ -7017,6 +7024,11 @@ module ts { // DECLARATION AND STATEMENT TYPE CHECKING function checkTypeParameter(node: TypeParameterDeclaration) { + // Grammar Checking + if (!hasParserError && node.expression) { + grammarErrorOnFirstToken(node.expression, Diagnostics.Type_expected); + } + checkSourceElement(node.constraint); if (fullTypeCheck) { checkTypeParameterHasIllegalReferencesInConstraint(node); @@ -8655,6 +8667,36 @@ module ts { return node; } + // Grammar checking helper functions + function scanToken(pos: number) { + var start = skipTrivia(sourceText, pos); + scanner.setTextPos(start); + scanner.scan(); + return start; + } + + function grammarErrorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void { + var start = scanToken(node.pos); + diagnostics.push(createFileDiagnostic(sourceFile, start, scanner.getTextPos() - start, message, arg0, arg1, arg2)); + } + + function grammarErrorAfterFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void { + scanToken(node.pos); + diagnostics.push(createFileDiagnostic(sourceFile, scanner.getTextPos(), 0, message, arg0, arg1, arg2)); + } + + function grammarErrorOnNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void { + var span = getErrorSpanForNode(node); + var start = span.end > span.pos ? skipTrivia(sourceFile.text, span.pos) : span.pos; + var length = span.end - start; + + diagnostics.push(createFileDiagnostic(sourceFile, start, length, message, arg0, arg1, arg2)); + } + + function grammarErrorAtPos(start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void { + diagnostics.push(createFileDiagnostic(sourceFile, start, length, message, arg0, arg1, arg2)); + } + function checkImportDeclaration(node: ImportDeclaration) { checkCollisionWithCapturedThisVariable(node, node.name); checkCollisionWithRequireExportsInGeneratedCode(node, node.name); @@ -8909,6 +8951,12 @@ module ts { // Fully type check a source file and collect the relevant diagnostics. function checkSourceFile(node: SourceFile) { + sourceText = node.text; + scanner = createScanner(compilerOptions.target, /*skipTrivia*/ true, sourceText); + hasParserError = node.parseDiagnostics.length > 0 ? true : false; + sourceFile = node; + //grammarDiagnostics = []; + var links = getNodeLinks(node); if (!(links.flags & NodeCheckFlags.TypeChecked)) { emitExtends = false; @@ -9469,7 +9517,7 @@ module ts { return program.getDiagnostics(sourceFile).length !== 0 || hasEarlyErrors(sourceFile) || (compilerOptions.noEmitOnError && getDiagnostics(sourceFile).length !== 0); - } + } function hasEarlyErrors(sourceFile?: SourceFile): boolean { return forEach(getDiagnostics(sourceFile), d => d.isEarly); diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 17dbae83bf2..5f54befb89b 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -73,7 +73,7 @@ module ts { Jump_target_cannot_cross_function_boundary: { code: 1107, category: DiagnosticCategory.Error, key: "Jump target cannot cross function boundary." }, A_return_statement_can_only_be_used_within_a_function_body: { code: 1108, category: DiagnosticCategory.Error, key: "A 'return' statement can only be used within a function body." }, Expression_expected: { code: 1109, category: DiagnosticCategory.Error, key: "Expression expected." }, - Type_expected: { code: 1110, category: DiagnosticCategory.Error, key: "Type expected." }, + Type_expected: { code: 1110, category: DiagnosticCategory.Error, key: "Type expected.", isEarly: true }, A_constructor_implementation_cannot_be_declared_in_an_ambient_context: { code: 1111, category: DiagnosticCategory.Error, key: "A constructor implementation cannot be declared in an ambient context." }, A_class_member_cannot_be_declared_optional: { code: 1112, category: DiagnosticCategory.Error, key: "A class member cannot be declared optional." }, A_default_clause_cannot_appear_more_than_once_in_a_switch_statement: { code: 1113, category: DiagnosticCategory.Error, key: "A 'default' clause cannot appear more than once in a 'switch' statement." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a06b71d460e..f694ccfc32f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -285,7 +285,8 @@ }, "Type expected.": { "category": "Error", - "code": 1110 + "code": 1110, + "isEarly": true }, "A constructor implementation cannot be declared in an ambient context.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d406b938d4e..58582964550 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4679,7 +4679,6 @@ module ts { case SyntaxKind.TaggedTemplateExpression: return checkTaggedTemplateExpression(node); case SyntaxKind.ThrowStatement: return checkThrowStatement(node); case SyntaxKind.TupleType: return checkTupleType(node); - case SyntaxKind.TypeParameter: return checkTypeParameter(node); case SyntaxKind.TypeReference: return checkTypeReference(node); case SyntaxKind.VariableDeclaration: return checkVariableDeclaration(node); case SyntaxKind.VariableStatement: return checkVariableStatement(node); @@ -5727,12 +5726,6 @@ module ts { } } - function checkTypeParameter(node: TypeParameterDeclaration) { - if (node.expression) { - return grammarErrorOnFirstToken(node.expression, Diagnostics.Type_expected); - } - } - function checkTypeReference(node: TypeReferenceNode) { return checkTypeArguments(node.typeArguments); } diff --git a/tests/baselines/reference/typeParameterConstraints1.errors.txt b/tests/baselines/reference/typeParameterConstraints1.errors.txt index d2e1f505c83..f30fd4f6700 100644 --- a/tests/baselines/reference/typeParameterConstraints1.errors.txt +++ b/tests/baselines/reference/typeParameterConstraints1.errors.txt @@ -1,8 +1,8 @@ +tests/cases/compiler/typeParameterConstraints1.ts(6,25): error TS2304: Cannot find name 'hm'. tests/cases/compiler/typeParameterConstraints1.ts(8,25): error TS1110: Type expected. tests/cases/compiler/typeParameterConstraints1.ts(9,25): error TS1110: Type expected. tests/cases/compiler/typeParameterConstraints1.ts(10,26): error TS1110: Type expected. tests/cases/compiler/typeParameterConstraints1.ts(11,26): error TS1110: Type expected. -tests/cases/compiler/typeParameterConstraints1.ts(6,25): error TS2304: Cannot find name 'hm'. tests/cases/compiler/typeParameterConstraints1.ts(12,26): error TS2304: Cannot find name 'undefined'.