Clean up error messages for using TypeScript syntax in JavaScr… (#35254)

* Fix up quotation marks in error messages in JavaScript files.

* Accepted baselines.

* Typescript -> TypeScript

* Accepted baselines.

* Migrate syntactic diagnostics tests to baselining tests.

* Accepted baselines.

* Update diagnosticMessages.json

* Removed markers.

* Add ability to baseline both semantic and syntactic diagnostics.

* Fix up broken diagnostics when using a server LS.

* Accepted baselines.

* Lints.

* Fake up sourcefile objects in the tsserver session client instead.

* Fewer allocations.
This commit is contained in:
Daniel Rosenwasser
2019-11-22 14:51:22 -08:00
committed by GitHub
parent 3e329469c1
commit 3da85df511
79 changed files with 547 additions and 387 deletions

View File

@@ -1539,7 +1539,7 @@ namespace ts {
}
if (element.questionToken) {
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.questionToken, Diagnostics._0_can_only_be_used_in_a_ts_file, "?"));
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.questionToken, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, "?"));
}
if (!isDoubleQuotedString(element.name)) {
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.name, Diagnostics.String_literal_with_double_quotes_expected));

View File

@@ -4543,59 +4543,51 @@
"category": "Error",
"code": 8001
},
"'import ... =' can only be used in a .ts file.": {
"'import ... =' can only be used in TypeScript files.": {
"category": "Error",
"code": 8002
},
"'export=' can only be used in a .ts file.": {
"'export =' can only be used in TypeScript files.": {
"category": "Error",
"code": 8003
},
"'type parameter declarations' can only be used in a .ts file.": {
"Type parameter declarations can only be used in TypeScript files.": {
"category": "Error",
"code": 8004
},
"'implements clauses' can only be used in a .ts file.": {
"'implements' clauses can only be used in TypeScript files.": {
"category": "Error",
"code": 8005
},
"'interface declarations' can only be used in a .ts file.": {
"'{0}' declarations can only be used in TypeScript files.": {
"category": "Error",
"code": 8006
},
"'module declarations' can only be used in a .ts file.": {
"category": "Error",
"code": 8007
},
"'type aliases' can only be used in a .ts file.": {
"Type aliases can only be used in TypeScript files.": {
"category": "Error",
"code": 8008
},
"'{0}' can only be used in a .ts file.": {
"The '{0}' modifier can only be used in TypeScript files.": {
"category": "Error",
"code": 8009
},
"'types' can only be used in a .ts file.": {
"Type annotations can only be used in TypeScript files.": {
"category": "Error",
"code": 8010
},
"'type arguments' can only be used in a .ts file.": {
"Type arguments can only be used in TypeScript files.": {
"category": "Error",
"code": 8011
},
"'parameter modifiers' can only be used in a .ts file.": {
"Parameter modifiers can only be used in TypeScript files.": {
"category": "Error",
"code": 8012
},
"'non-null assertions' can only be used in a .ts file.": {
"Non-null assertions can only be used in TypeScript files.": {
"category": "Error",
"code": 8013
},
"'enum declarations' can only be used in a .ts file.": {
"category": "Error",
"code": 8015
},
"'type assertion expressions' can only be used in a .ts file.": {
"Type assertion expressions can only be used in TypeScript files.": {
"category": "Error",
"code": 8016
},

View File

@@ -1797,7 +1797,7 @@ namespace ts {
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.MethodDeclaration:
if ((<ParameterDeclaration | PropertyDeclaration | MethodDeclaration>parent).questionToken === node) {
diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_can_only_be_used_in_a_ts_file, "?"));
diagnostics.push(createDiagnosticForNode(node, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, "?"));
return;
}
// falls through
@@ -1811,45 +1811,48 @@ namespace ts {
case SyntaxKind.VariableDeclaration:
// type annotation
if ((<FunctionLikeDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration>parent).type === node) {
diagnostics.push(createDiagnosticForNode(node, Diagnostics.types_can_only_be_used_in_a_ts_file));
diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_annotations_can_only_be_used_in_TypeScript_files));
return;
}
}
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.import_can_only_be_used_in_a_ts_file));
diagnostics.push(createDiagnosticForNode(node, Diagnostics.import_can_only_be_used_in_TypeScript_files));
return;
case SyntaxKind.ExportAssignment:
if ((<ExportAssignment>node).isExportEquals) {
diagnostics.push(createDiagnosticForNode(node, Diagnostics.export_can_only_be_used_in_a_ts_file));
diagnostics.push(createDiagnosticForNode(node, Diagnostics.export_can_only_be_used_in_TypeScript_files));
return;
}
break;
case SyntaxKind.HeritageClause:
const heritageClause = <HeritageClause>node;
if (heritageClause.token === SyntaxKind.ImplementsKeyword) {
diagnostics.push(createDiagnosticForNode(node, Diagnostics.implements_clauses_can_only_be_used_in_a_ts_file));
diagnostics.push(createDiagnosticForNode(node, Diagnostics.implements_clauses_can_only_be_used_in_TypeScript_files));
return;
}
break;
case SyntaxKind.InterfaceDeclaration:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.interface_declarations_can_only_be_used_in_a_ts_file));
diagnostics.push(createDiagnosticForNode(node, Diagnostics.Interface_declaration_cannot_have_implements_clause));
return;
case SyntaxKind.ModuleDeclaration:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.module_declarations_can_only_be_used_in_a_ts_file));
const moduleKeyword = node.flags & NodeFlags.Namespace ? tokenToString(SyntaxKind.NamespaceKeyword) : tokenToString(SyntaxKind.ModuleKeyword);
Debug.assertDefined(moduleKeyword);
diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, moduleKeyword));
return;
case SyntaxKind.TypeAliasDeclaration:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.type_aliases_can_only_be_used_in_a_ts_file));
diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_aliases_can_only_be_used_in_TypeScript_files));
return;
case SyntaxKind.EnumDeclaration:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.enum_declarations_can_only_be_used_in_a_ts_file));
const enumKeyword = Debug.assertDefined(tokenToString(SyntaxKind.EnumKeyword));
diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, enumKeyword));
return;
case SyntaxKind.NonNullExpression:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.non_null_assertions_can_only_be_used_in_a_ts_file));
diagnostics.push(createDiagnosticForNode(node, Diagnostics.Non_null_assertions_can_only_be_used_in_TypeScript_files));
return;
case SyntaxKind.AsExpression:
diagnostics.push(createDiagnosticForNode((node as AsExpression).type, Diagnostics.type_assertion_expressions_can_only_be_used_in_a_ts_file));
diagnostics.push(createDiagnosticForNode((node as AsExpression).type, Diagnostics.Type_assertion_expressions_can_only_be_used_in_TypeScript_files));
return;
case SyntaxKind.TypeAssertionExpression:
Debug.fail(); // Won't parse these in a JS file anyway, as they are interpreted as JSX.
@@ -1878,7 +1881,7 @@ namespace ts {
case SyntaxKind.ArrowFunction:
// Check type parameters
if (nodes === (<DeclarationWithTypeParameterChildren>parent).typeParameters) {
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.type_parameter_declarations_can_only_be_used_in_a_ts_file));
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_parameter_declarations_can_only_be_used_in_TypeScript_files));
return;
}
// falls through
@@ -1894,7 +1897,7 @@ namespace ts {
if (nodes === (<PropertyDeclaration>parent).modifiers) {
for (const modifier of <NodeArray<Modifier>>nodes) {
if (modifier.kind !== SyntaxKind.StaticKeyword) {
diagnostics.push(createDiagnosticForNode(modifier, Diagnostics._0_can_only_be_used_in_a_ts_file, tokenToString(modifier.kind)));
diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind)));
}
}
return;
@@ -1903,7 +1906,7 @@ namespace ts {
case SyntaxKind.Parameter:
// Check modifiers of parameter declaration
if (nodes === (<ParameterDeclaration>parent).modifiers) {
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.parameter_modifiers_can_only_be_used_in_a_ts_file));
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Parameter_modifiers_can_only_be_used_in_TypeScript_files));
return;
}
break;
@@ -1915,7 +1918,7 @@ namespace ts {
case SyntaxKind.TaggedTemplateExpression:
// Check type arguments
if (nodes === (<NodeWithTypeArguments>parent).typeArguments) {
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.type_arguments_can_only_be_used_in_a_ts_file));
diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_arguments_can_only_be_used_in_TypeScript_files));
return;
}
break;
@@ -1941,7 +1944,7 @@ namespace ts {
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.AbstractKeyword:
diagnostics.push(createDiagnosticForNode(modifier, Diagnostics._0_can_only_be_used_in_a_ts_file, tokenToString(modifier.kind)));
diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind)));
break;
// These are all legal modifiers.

View File

@@ -374,12 +374,14 @@ namespace ts.server {
private getDiagnostics(file: string, command: CommandNames): DiagnosticWithLocation[] {
const request = this.processRequest<protocol.SyntacticDiagnosticsSyncRequest | protocol.SemanticDiagnosticsSyncRequest | protocol.SuggestionDiagnosticsSyncRequest>(command, { file, includeLinePosition: true });
const response = this.processResponse<protocol.SyntacticDiagnosticsSyncResponse | protocol.SemanticDiagnosticsSyncResponse | protocol.SuggestionDiagnosticsSyncResponse>(request);
const sourceText = getSnapshotText(this.host.getScriptSnapshot(file)!);
const fakeSourceFile = { fileName: file, text: sourceText } as SourceFile; // Warning! This is a huge lie!
return (<protocol.DiagnosticWithLinePosition[]>response.body).map((entry): DiagnosticWithLocation => {
const category = firstDefined(Object.keys(DiagnosticCategory), id =>
isString(id) && entry.category === id.toLowerCase() ? (<any>DiagnosticCategory)[id] : undefined);
return {
file: undefined!, // TODO: GH#18217
file: fakeSourceFile,
start: entry.start,
length: entry.length,
messageText: entry.message,
@@ -518,14 +520,14 @@ namespace ts.server {
return notImplemented();
}
getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems {
getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems | undefined {
const args: protocol.SignatureHelpRequestArgs = this.createFileLocationRequestArgs(fileName, position);
const request = this.processRequest<protocol.SignatureHelpRequest>(CommandNames.SignatureHelp, args);
const response = this.processResponse<protocol.SignatureHelpResponse>(request);
if (!response.body) {
return undefined!; // TODO: GH#18217
return undefined;
}
const { items, applicableSpan: encodedApplicableSpan, selectedItemIndex, argumentIndex, argumentCount } = response.body;

View File

@@ -221,7 +221,7 @@ namespace FourSlash {
}
}
constructor(private basePath: string, private testType: FourSlashTestType, public testData: FourSlashData) {
constructor(private originalInputFileName: string, private basePath: string, private testType: FourSlashTestType, public testData: FourSlashData) {
// Create a new Services Adapter
this.cancellationToken = new TestCancellationToken();
let compilationOptions = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions);
@@ -1475,7 +1475,7 @@ namespace FourSlash {
}
public baselineCurrentFileBreakpointLocations() {
const baselineFile = this.getBaselineFileName().replace("breakpointValidation", "bpSpan");
const baselineFile = this.getBaselineFileNameForInternalFourslashFile().replace("breakpointValidation", "bpSpan");
Harness.Baseline.runBaseline(baselineFile, this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos)!));
}
@@ -1554,8 +1554,49 @@ namespace FourSlash {
return result;
}
public baselineSyntacticDiagnostics() {
const files = this.getCompilerTestFiles();
const result = this.getSyntacticDiagnosticBaselineText(files);
Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(), result);
}
private getCompilerTestFiles() {
return ts.map(this.testData.files, ({ content, fileName }) => ({
content, unitName: fileName
}));
}
public baselineSyntacticAndSemanticDiagnostics() {
const files = this.getCompilerTestFiles();
const result = this.getSyntacticDiagnosticBaselineText(files)
+ Harness.IO.newLine()
+ Harness.IO.newLine()
+ this.getSemanticDiagnosticBaselineText(files);
Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(), result);
}
private getSyntacticDiagnosticBaselineText(files: Harness.Compiler.TestFile[]) {
const diagnostics = ts.flatMap(files,
file => this.languageService.getSyntacticDiagnostics(file.unitName)
);
const result = `Syntactic Diagnostics for file '${this.originalInputFileName}':`
+ Harness.IO.newLine()
+ Harness.Compiler.getErrorBaseline(files, diagnostics, /*pretty*/ false);
return result;
}
private getSemanticDiagnosticBaselineText(files: Harness.Compiler.TestFile[]) {
const diagnostics = ts.flatMap(files,
file => this.languageService.getSemanticDiagnostics(file.unitName)
);
const result = `Semantic Diagnostics for file '${this.originalInputFileName}':`
+ Harness.IO.newLine()
+ Harness.Compiler.getErrorBaseline(files, diagnostics, /*pretty*/ false);
return result;
}
public baselineQuickInfo() {
const baselineFile = this.getBaselineFileName();
const baselineFile = this.getBaselineFileNameForInternalFourslashFile();
Harness.Baseline.runBaseline(
baselineFile,
stringify(
@@ -1567,7 +1608,7 @@ namespace FourSlash {
public baselineSmartSelection() {
const n = "\n";
const baselineFile = this.getBaselineFileName();
const baselineFile = this.getBaselineFileNameForInternalFourslashFile();
const markers = this.getMarkers();
const fileContent = this.activeFile.content;
const text = markers.map(marker => {
@@ -1652,11 +1693,15 @@ namespace FourSlash {
Harness.IO.log(stringify(help.items[help.selectedItemIndex]));
}
private getBaselineFileName() {
private getBaselineFileNameForInternalFourslashFile() {
return this.testData.globalOptions[MetadataOptionNames.baselineFile] ||
ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ".baseline");
}
private getBaselineFileNameForContainingTestFile() {
return ts.getBaseFileName(this.originalInputFileName).replace(ts.Extension.Ts, ".baseline");
}
private getSignatureHelp({ triggerReason }: FourSlashInterface.VerifySignatureHelpOptions): ts.SignatureHelpItems | undefined {
return this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition, {
triggerReason
@@ -3263,7 +3308,7 @@ namespace FourSlash {
// Parse out the files and their metadata
const testData = parseTestData(absoluteBasePath, content, absoluteFileName);
const state = new TestState(absoluteBasePath, testType, testData);
const state = new TestState(absoluteFileName, absoluteBasePath, testType, testData);
const output = ts.transpileModule(content, { reportDiagnostics: true, compilerOptions: { target: ts.ScriptTarget.ES2015 } });
if (output.diagnostics!.length > 0) {
throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics![0].messageText}`);
@@ -4122,6 +4167,14 @@ namespace FourSlashInterface {
this.state.baselineSmartSelection();
}
public baselineSyntacticDiagnostics() {
this.state.baselineSyntacticDiagnostics();
}
public baselineSyntacticAndSemanticDiagnostics() {
this.state.baselineSyntacticAndSemanticDiagnostics();
}
public nameOrDottedNameSpanTextIs(text: string) {
this.state.verifyCurrentNameOrDottedNameSpanText(text);
}