Add tests for javascript semantic dianostics.

This commit is contained in:
Cyrus Najmabadi
2015-03-17 16:27:56 -07:00
parent 4a346045ce
commit e650e8a6d4
7 changed files with 98 additions and 19 deletions

View File

@@ -14,6 +14,7 @@
//
/// <reference path='..\services\services.ts' />
/// <reference path='..\services\shims.ts' />
/// <reference path='harnessLanguageService.ts' />
/// <reference path='harness.ts' />
/// <reference path='fourslashRunner.ts' />
@@ -126,12 +127,13 @@ module FourSlash {
outDir: 'outDir',
sourceMap: 'sourceMap',
sourceRoot: 'sourceRoot',
allowNonTsExtensions: 'allowNonTsExtensions',
resolveReference: 'ResolveReference', // This flag is used to specify entry file for resolve file references. The flag is only allow once per test file
};
// List of allowed metadata names
var fileMetadataNames = [testOptMetadataNames.fileName, testOptMetadataNames.emitThisFile, testOptMetadataNames.resolveReference];
var globalMetadataNames = [testOptMetadataNames.baselineFile, testOptMetadataNames.declaration,
var globalMetadataNames = [testOptMetadataNames.allowNonTsExtensions, testOptMetadataNames.baselineFile, testOptMetadataNames.declaration,
testOptMetadataNames.mapRoot, testOptMetadataNames.module, testOptMetadataNames.out,
testOptMetadataNames.outDir, testOptMetadataNames.sourceMap, testOptMetadataNames.sourceRoot]
@@ -141,6 +143,9 @@ module FourSlash {
for (var prop in globalOptions) {
if (globalOptions.hasOwnProperty(prop)) {
switch (prop) {
case testOptMetadataNames.allowNonTsExtensions:
settings.allowNonTsExtensions = true;
break;
case testOptMetadataNames.declaration:
settings.declaration = true;
break;
@@ -788,6 +793,16 @@ module FourSlash {
return "\nActual " + name + ":\n\t" + actualValue + "\nExpected value:\n\t" + expectedValue;
}
public getSemanticDiagnostics(expected: string) {
var diagnostics = this.languageService.getSemanticDiagnostics(this.activeFile.fileName);
var realized = ts.realizeDiagnostics(diagnostics, "\r\n");
var actual = JSON.stringify(realized, null, " ");
if (actual !== expected) {
ts.sys.writeFile("c:\\temp\\out.txt", actual);
}
assert.equal(actual, expected);
}
public verifyQuickInfoString(negative: boolean, expectedText?: string, expectedDocumentation?: string) {
[expectedText, expectedDocumentation].forEach(str => {
if (str) {

View File

@@ -2420,12 +2420,10 @@ module ts {
case SyntaxKind.ExportAssignment:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.export_can_only_be_used_in_TypeScript));
return;
case SyntaxKind.TypeParameter:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.type_parameter_declarations_can_only_be_used_in_TypeScript));
return;
case SyntaxKind.ClassDeclaration:
let classDeclaration = <ClassDeclaration>node;
if (checkModifiers(classDeclaration.modifiers)) {
if (checkModifiers(classDeclaration.modifiers) ||
checkTypeParameters(classDeclaration.typeParameters)) {
return;
}
break;
@@ -2456,6 +2454,7 @@ module ts {
case SyntaxKind.FunctionDeclaration:
let functionDeclaration = <FunctionLikeDeclaration>node;
if (checkModifiers(functionDeclaration.modifiers) ||
checkTypeParameters(functionDeclaration.typeParameters) ||
checkTypeAnnotation(functionDeclaration.type)) {
return;
}
@@ -2476,7 +2475,8 @@ module ts {
case SyntaxKind.NewExpression:
let expression = <CallExpression>node;
if (expression.typeArguments && expression.typeArguments.length > 0) {
diagnostics.push(createFileDiagnostic(sourceFile, expression.typeArguments.pos, expression.typeArguments.end,
let start = expression.typeArguments.pos;
diagnostics.push(createFileDiagnostic(sourceFile, start, expression.typeArguments.end - start,
Diagnostics.type_arguments_can_only_be_used_in_TypeScript));
return;
}
@@ -2484,7 +2484,8 @@ module ts {
case SyntaxKind.Parameter:
let parameter = <ParameterDeclaration>node;
if (parameter.modifiers) {
diagnostics.push(createFileDiagnostic(sourceFile, parameter.modifiers.pos, parameter.modifiers.end,
let start = parameter.modifiers.pos;
diagnostics.push(createFileDiagnostic(sourceFile, start, parameter.modifiers.end - start,
Diagnostics.parameter_modifiers_can_only_be_used_in_TypeScript));
return;
}
@@ -2511,6 +2512,15 @@ module ts {
forEachChild(node, walk);
}
function checkTypeParameters(typeParameters: NodeArray<TypeParameterDeclaration>): boolean {
if (typeParameters) {
let start = typeParameters.pos;
diagnostics.push(createFileDiagnostic(sourceFile, start, typeParameters.end - start, Diagnostics.type_parameter_declarations_can_only_be_used_in_TypeScript));
return true;
}
return false;
}
function checkTypeAnnotation(type: TypeNode): boolean {
if (type) {
diagnostics.push(createDiagnosticForNode(type, Diagnostics.types_can_only_be_used_in_TypeScript));

View File

@@ -325,6 +325,22 @@ module ts {
}
}
/* @internal */
export function realizeDiagnostics(diagnostics: Diagnostic[], newLine: string): { message: string; start: number; length: number; category: string; } []{
return diagnostics.map(d => realizeDiagnostic(d, newLine));
}
function realizeDiagnostic(diagnostic: Diagnostic, newLine: string): { message: string; start: number; length: number; category: string; } {
return {
message: flattenDiagnosticMessageText(diagnostic.messageText, newLine),
start: diagnostic.start,
length: diagnostic.length,
/// TODO: no need for the tolowerCase call
category: DiagnosticCategory[diagnostic.category].toLowerCase(),
code: diagnostic.code
};
}
class LanguageServiceShimObject extends ShimBase implements LanguageServiceShim {
private logger: Logger;
@@ -385,18 +401,7 @@ module ts {
private realizeDiagnostics(diagnostics: Diagnostic[]): { message: string; start: number; length: number; category: string; }[]{
var newLine = this.getNewLine();
return diagnostics.map(d => this.realizeDiagnostic(d, newLine));
}
private realizeDiagnostic(diagnostic: Diagnostic, newLine: string): { message: string; start: number; length: number; category: string; } {
return {
message: flattenDiagnosticMessageText(diagnostic.messageText, newLine),
start: diagnostic.start,
length: diagnostic.length,
/// TODO: no need for the tolowerCase call
category: DiagnosticCategory[diagnostic.category].toLowerCase(),
code: diagnostic.code
};
return ts.realizeDiagnostics(diagnostics, newLine);
}
public getSyntacticClassifications(fileName: string, start: number, length: number): string {

View File

@@ -439,6 +439,10 @@ module FourSlashInterface {
displayParts: ts.SymbolDisplayPart[], documentation: ts.SymbolDisplayPart[]) {
FourSlash.currentTestState.verifyQuickInfoDisplayParts(kind, kindModifiers, textSpan, displayParts, documentation);
}
public getSemanticDiagnostics(expected: string) {
FourSlash.currentTestState.getSemanticDiagnostics(expected);
}
}
export class edit {

View File

@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
// @allowNonTsExtensions: true
// @Filename: a.tsjs
//// import a = b;
verify.getSemanticDiagnostics(`[
{
"message": "'import ... =' can only be used in TypeScript.",
"start": 0,
"length": 13,
"category": "error",
"code": 8002
}
]`);

View File

@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
// @allowNonTsExtensions: true
// @Filename: a.tsjs
//// export = b;
verify.getSemanticDiagnostics(`[
{
"message": "'export=' can only be used in TypeScript.",
"start": 0,
"length": 11,
"category": "error",
"code": 8003
}
]`);

View File

@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
// @allowNonTsExtensions: true
// @Filename: a.tsjs
//// class C<T>;
verify.getSemanticDiagnostics(`[
{
"message": "'type parameter declarations' can only be used in TypeScript.",
"start": 8,
"length": 1,
"category": "error",
"code": 8004
}
]`);