Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: fix
packages:
- "@typespec/compiler"
---

Show NoTarget diagnostic message in vscode
2 changes: 1 addition & 1 deletion packages/compiler/src/server/client-config-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface LSPConfig {
emit?: string[];
}

interface Config {
export interface Config {
lsp?: LSPConfig;
entrypoint?: string[];
}
Expand Down
26 changes: 11 additions & 15 deletions packages/compiler/src/server/compile-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ import { parse } from "../core/parser.js";
import { getBaseFileName, getDirectoryPath } from "../core/path-utils.js";
import type { CompilerHost, TypeSpecScriptNode } from "../core/types.js";
import { distinctArray } from "../utils/misc.js";
import { getLocationInYamlScript } from "../yaml/diagnostics.js";
import { parseYaml } from "../yaml/parser.js";
import { ClientConfigProvider } from "./client-config-provider.js";
import { serverOptions } from "./constants.js";
import { getDiagnosticRangeInTspConfig } from "./diagnostics.js";
import { resolveEntrypointFile } from "./entrypoint-resolver.js";
import { FileService } from "./file-service.js";
import { FileSystemCache } from "./file-system-cache.js";
Expand Down Expand Up @@ -238,33 +237,30 @@ export function createCompileService({
}

let uri = document.uri;
let range = Range.create(0, 0, 0, 0);
let range: Range | undefined;
if (err.name === "ExternalError" && err.info.kind === "emitter" && configFilePath) {
const emitterName = err.info.metadata.name;
const [yamlScript] = parseYaml(await serverHost.compilerHost.readFile(configFilePath));
const target = getLocationInYamlScript(yamlScript, ["emit", emitterName], "key");
if (target.pos === 0) {
range = await getDiagnosticRangeInTspConfig(
configFilePath,
serverHost.compilerHost.readFile,
emitterName,
);

uri = fileService.getURL(configFilePath);
if (range === undefined) {
log({
level: "debug",
message: `Unexpected situation, can't find emitter '${emitterName}' in config file '${configFilePath}'`,
});
}
uri = fileService.getURL(configFilePath);
const lineAndChar = target.file.getLineAndCharacterOfPosition(target.pos);
range = Range.create(
lineAndChar.line,
lineAndChar.character,
lineAndChar.line,
lineAndChar.character + emitterName.length,
);
}

serverHost.sendDiagnostics({
uri,
diagnostics: [
{
severity: DiagnosticSeverity.Error,
range,
range: range ?? Range.create(0, 0, 0, 0),
message:
(err.name === "ExternalError"
? "External compiler error!\n"
Expand Down
108 changes: 103 additions & 5 deletions packages/compiler/src/server/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
Range,
Diagnostic as VSDiagnostic,
} from "vscode-languageserver";
import { Range as VSRange } from "vscode-languageserver";
import type { TextDocument } from "vscode-languageserver-textdocument";
import { DiagnosticSeverity } from "vscode-languageserver/node.js";
import {
Expand All @@ -11,24 +12,49 @@ import {
} from "../core/diagnostics.js";
import { getTypeName } from "../core/helpers/type-name-utils.js";
import type { Program } from "../core/program.js";
import type { Node, SourceLocation } from "../core/types.js";
import { Diagnostic } from "../core/types.js";
import type { Node, SourceFile, SourceLocation } from "../core/types.js";
import { Diagnostic, NoTarget } from "../core/types.js";
import { isDefined } from "../utils/misc.js";
import { getLocationInYamlScript } from "../yaml/diagnostics.js";
import { parseYaml } from "../yaml/parser.js";
import { Config } from "./client-config-provider.js";
import type { FileService } from "./file-service.js";
import type { ServerSourceFile } from "./types.js";

/** Convert TypeSpec Diagnostic to Lsp diagnostic. Each TypeSpec diagnostic could produce multiple lsp ones when it involve multiple locations. */
export function convertDiagnosticToLsp(
export async function convertDiagnosticToLsp(
fileService: FileService,
program: Program,
document: TextDocument,
diagnostic: Diagnostic,
): [VSDiagnostic, TextDocument][] {
const root = getVSLocation(
clientConfig?: Config | undefined,
readFile: ((path: string) => Promise<SourceFile>) | undefined = undefined,
): Promise<[VSDiagnostic, TextDocument][]> {
let root = getVSLocation(
fileService,
getSourceLocation(diagnostic.target, { locateId: true }),
document,
);

if (root === undefined) {
const emitterName = program.compilerOptions.emit?.find((emitName) =>
diagnostic.message.includes(emitName),
);
const result = await getDiagnosticInTspConfig(
fileService,
program.compilerOptions.config,
readFile,
emitterName,
);
if (result === NoTarget) {
return getDiagnosticInVsCodeSettings(diagnostic, document, clientConfig, emitterName);
} else if (result !== undefined) {
root = result;
} else {
return [];
}
}

if (root === undefined || !fileService.upToDate(root.document)) return [];

const instantiationNodes = getDiagnosticTemplateInstantitationTrace(diagnostic.target);
Expand Down Expand Up @@ -175,3 +201,75 @@ function convertSeverity(severity: "warning" | "error"): DiagnosticSeverity {
return DiagnosticSeverity.Error;
}
}

async function getDiagnosticInTspConfig(
fileService: FileService,
configFilePath: string | undefined,
readFile: ((path: string) => Promise<SourceFile>) | undefined,
emitterName: string | undefined,
): Promise<VSLocation | typeof NoTarget | undefined> {
if (configFilePath && readFile && emitterName && emitterName.length > 0) {
const docTspConfig = fileService.getOpenDocument(configFilePath);
if (!docTspConfig) {
return NoTarget;
}

const range = await getDiagnosticRangeInTspConfig(configFilePath, readFile, emitterName);
if (range === undefined) {
return NoTarget;
}
return {
range,
document: docTspConfig,
};
}
return undefined;
}

export async function getDiagnosticRangeInTspConfig(
configFilePath: string,
readFile: (path: string) => Promise<SourceFile>,
emitterName: string,
): Promise<Range | undefined> {
const [yamlScript] = parseYaml(await readFile(configFilePath));
const target = getLocationInYamlScript(yamlScript, ["emit", emitterName], "key");
if (target.pos === 0) {
return undefined;
}

const lineAndChar = target.file.getLineAndCharacterOfPosition(target.pos);
return VSRange.create(
lineAndChar.line,
lineAndChar.character,
lineAndChar.line,
lineAndChar.character + emitterName.length,
);
}

function getDiagnosticInVsCodeSettings(
diagnostic: Diagnostic,
document: TextDocument,
clientConfig?: Config | undefined,
emitterName?: string | undefined,
): [VSDiagnostic, TextDocument][] {
let customMsg = "";
if (clientConfig?.lsp?.emit && clientConfig.lsp.emit.includes(emitterName || "")) {
customMsg = " [It's configured in vscode settings]";
} else {
customMsg = " [No target source location reported for this Error/Warning]";
}

const relatedInformation: DiagnosticRelatedInformation[] = [];
return [
[
createLspDiagnostic({
range: VSRange.create(0, 0, 0, 0),
message: diagnostic.message + customMsg,
severity: convertSeverity(diagnostic.severity),
code: diagnostic.code,
relatedInformation,
}),
document,
],
];
}
15 changes: 14 additions & 1 deletion packages/compiler/src/server/serverlib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -739,8 +739,21 @@ export function createServer(
}
}

// Add a diagnostic slot to tspconfig.yaml
const configDocument = fileService.getOpenDocument(program.compilerOptions.config ?? "");
if (configDocument && !diagnosticMap.has(configDocument)) {
diagnosticMap.set(configDocument, []);
}

for (const each of program.diagnostics) {
const results = convertDiagnosticToLsp(fileService, program, document, each);
const results = await convertDiagnosticToLsp(
fileService,
program,
document,
each,
clientConfigsProvider?.config,
compilerHost.readFile,
);
for (const result of results) {
const [diagnostic, diagDocument] = result;
if (each.url) {
Expand Down
Loading