Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ namespace ts {
* If so, the node _must_ be in the current file (as that's the only way anything could have traversed to it to yield it as the error node)
* This version of `createDiagnosticForNode` uses the binder's context to account for this, and always yields correct diagnostics even in these situations.
*/
function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): Diagnostic {
function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
return createDiagnosticForNodeInSourceFile(getSourceFileOfNode(node) || file, node, message, arg0, arg1, arg2);
}

Expand Down
10 changes: 5 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,11 @@ namespace ts {

getSuggestionDiagnostics: file => {
return (suggestionDiagnostics.get(file.fileName) || emptyArray).concat(getUnusedDiagnostics());
function getUnusedDiagnostics(): ReadonlyArray<Diagnostic> {
function getUnusedDiagnostics(): ReadonlyArray<DiagnosticWithLocation> {
if (file.isDeclarationFile) return emptyArray;

checkSourceFile(file);
const diagnostics: Diagnostic[] = [];
const diagnostics: DiagnosticWithLocation[] = [];
Debug.assert(!!(getNodeLinks(file).flags & NodeCheckFlags.TypeChecked));
checkUnusedIdentifiers(allPotentiallyUnusedIdentifiers.get(file.fileName)!, (kind, diag) => {
if (!unusedIsError(kind)) {
Expand Down Expand Up @@ -479,7 +479,7 @@ namespace ts {

const diagnostics = createDiagnosticCollection();
// Suggestion diagnostics must have a file. Keyed by source file name.
const suggestionDiagnostics = createMultiMap<Diagnostic>();
const suggestionDiagnostics = createMultiMap<DiagnosticWithLocation>();

const enum TypeFacts {
None = 0,
Expand Down Expand Up @@ -642,7 +642,7 @@ namespace ts {
Local,
Parameter,
}
type AddUnusedDiagnostic = (type: UnusedKind, diagnostic: Diagnostic) => void;
type AddUnusedDiagnostic = (type: UnusedKind, diagnostic: DiagnosticWithLocation) => void;

const builtinGlobals = createSymbolTable();
builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol);
Expand Down Expand Up @@ -838,7 +838,7 @@ namespace ts {
diagnostics.add(diagnostic);
}

function addErrorOrSuggestion(isError: boolean, diagnostic: Diagnostic) {
function addErrorOrSuggestion(isError: boolean, diagnostic: DiagnosticWithLocation) {
if (isError) {
diagnostics.add(diagnostic);
}
Expand Down
8 changes: 4 additions & 4 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ namespace ts {
return pathIsRelative(moduleName) || isRootedDiskPath(moduleName);
}

export function sortAndDeduplicateDiagnostics(diagnostics: ReadonlyArray<Diagnostic>): Diagnostic[] {
return sortAndDeduplicate(diagnostics, compareDiagnostics);
export function sortAndDeduplicateDiagnostics<T extends Diagnostic>(diagnostics: ReadonlyArray<T>): T[] {
return sortAndDeduplicate<T>(diagnostics, compareDiagnostics);
}
}

Expand Down Expand Up @@ -1578,8 +1578,8 @@ namespace ts {
return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] || message.message;
}

export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: (string | number)[]): Diagnostic;
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): Diagnostic {
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: (string | number)[]): DiagnosticWithLocation;
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): DiagnosticWithLocation {
Debug.assertGreaterThanOrEqual(start, 0);
Debug.assertGreaterThanOrEqual(length, 0);

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ namespace ts {
// tslint:enable variable-name

let sourceFile: SourceFile;
let parseDiagnostics: Diagnostic[];
let parseDiagnostics: DiagnosticWithLocation[];
let syntaxCursor: IncrementalParser.SyntaxCursor;

let currentToken: SyntaxKind;
Expand Down
55 changes: 34 additions & 21 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ namespace ts {
}

interface DiagnosticCache {
perFile?: Map<Diagnostic[]>;
perFile?: Map<DiagnosticWithLocation[]>;
allDiagnostics?: Diagnostic[];
}

Expand Down Expand Up @@ -453,7 +453,7 @@ namespace ts {

export function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): ReadonlyArray<Diagnostic> {
return configFileParseResult.options.configFile ?
configFileParseResult.options.configFile.parseDiagnostics.concat(configFileParseResult.errors) :
[...configFileParseResult.options.configFile.parseDiagnostics, ...configFileParseResult.errors] :
configFileParseResult.errors;
}

Expand Down Expand Up @@ -1211,8 +1211,8 @@ namespace ts {

function getDiagnosticsHelper(
sourceFile: SourceFile,
getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken) => ReadonlyArray<Diagnostic>,
cancellationToken: CancellationToken): ReadonlyArray<Diagnostic> {
getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken) => ReadonlyArray<DiagnosticWithLocation>,
cancellationToken: CancellationToken): ReadonlyArray<DiagnosticWithLocation> {
if (sourceFile) {
return getDiagnostics(sourceFile, cancellationToken);
}
Expand All @@ -1224,15 +1224,15 @@ namespace ts {
}));
}

function getSyntacticDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): ReadonlyArray<Diagnostic> {
function getSyntacticDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): ReadonlyArray<DiagnosticWithLocation> {
return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken);
}

function getSemanticDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): ReadonlyArray<Diagnostic> {
function getSemanticDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): ReadonlyArray<DiagnosticWithLocation> {
return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken);
}

function getDeclarationDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): ReadonlyArray<Diagnostic> {
function getDeclarationDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): ReadonlyArray<DiagnosticWithLocation> {
const options = program.getCompilerOptions();
// collect diagnostics from the program only once if either no source file was specified or out/outFile is set (bundled emit)
if (!sourceFile || options.out || options.outFile) {
Expand All @@ -1243,7 +1243,7 @@ namespace ts {
}
}

function getSyntacticDiagnosticsForFile(sourceFile: SourceFile): ReadonlyArray<Diagnostic> {
function getSyntacticDiagnosticsForFile(sourceFile: SourceFile): ReadonlyArray<DiagnosticWithLocation> {
// For JavaScript files, we report semantic errors for using TypeScript-only
// constructs from within a JavaScript file as syntactic errors.
if (isSourceFileJavaScript(sourceFile)) {
Expand Down Expand Up @@ -1278,11 +1278,11 @@ namespace ts {
}
}

function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): ReadonlyArray<DiagnosticWithLocation> {
return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedSemanticDiagnosticsForFile, getSemanticDiagnosticsForFileNoCache);
}

function getSemanticDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
function getSemanticDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken): DiagnosticWithLocation[] {
return runWithCancellationToken(() => {
// If skipLibCheck is enabled, skip reporting errors if file is a declaration file.
// If skipDefaultLibCheck is enabled, skip reporting errors if file contains a
Expand All @@ -1300,14 +1300,15 @@ namespace ts {
const includeBindAndCheckDiagnostics = sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX ||
sourceFile.scriptKind === ScriptKind.External || isCheckJs;
const bindDiagnostics = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
// TODO: GH#18217
const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) as ReadonlyArray<DiagnosticWithLocation> : emptyArray;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we are asking for diagnostics for a particular file. But getDiagnostics in checker.ts seems to add global diagnostics in some cases. @rbuckton Do you know how this works?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you are missing some of the built in types like Array for instance, that is a global diagnostic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @mhegazy said. We add those only when they are first requested.

const fileProcessingDiagnosticsInFile = fileProcessingDiagnostics.getDiagnostics(sourceFile.fileName);
const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName);
let diagnostics = bindDiagnostics.concat(checkDiagnostics, fileProcessingDiagnosticsInFile, programDiagnosticsInFile);
if (isCheckJs) {
diagnostics = concatenate(diagnostics, sourceFile.jsDocDiagnostics);
}
return filter(diagnostics, shouldReportDiagnostic);
return filter<DiagnosticWithLocation>(diagnostics, shouldReportDiagnostic);
});
}

Expand Down Expand Up @@ -1336,9 +1337,9 @@ namespace ts {
return true;
}

function getJavaScriptSyntacticDiagnosticsForFile(sourceFile: SourceFile): Diagnostic[] {
function getJavaScriptSyntacticDiagnosticsForFile(sourceFile: SourceFile): DiagnosticWithLocation[] {
return runWithCancellationToken(() => {
const diagnostics: Diagnostic[] = [];
const diagnostics: DiagnosticWithLocation[] = [];
let parent: Node = sourceFile;
walk(sourceFile);

Expand Down Expand Up @@ -1506,20 +1507,20 @@ namespace ts {
}
}

function createDiagnosticForNodeArray(nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): Diagnostic {
function createDiagnosticForNodeArray(nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
const start = nodes.pos;
return createFileDiagnostic(sourceFile, start, nodes.end - start, message, arg0, arg1, arg2);
}

// Since these are syntactic diagnostics, parent might not have been set
// this means the sourceFile cannot be infered from the node
function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): Diagnostic {
function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2);
}
});
}

function getDeclarationDiagnosticsWorker(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken): Diagnostic[] {
function getDeclarationDiagnosticsWorker(sourceFile: SourceFile, cancellationToken: CancellationToken): ReadonlyArray<DiagnosticWithLocation> {
return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache);
}

Expand All @@ -1530,12 +1531,24 @@ namespace ts {
return ts.getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile);
});
}

function getAndCacheDiagnostics(
sourceFile: SourceFile,
cancellationToken: CancellationToken,
cache: DiagnosticCache,
getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken) => DiagnosticWithLocation[]
): ReadonlyArray<DiagnosticWithLocation>;
function getAndCacheDiagnostics(
sourceFile: SourceFile | undefined,
cancellationToken: CancellationToken,
cache: DiagnosticCache,
getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken) => DiagnosticWithLocation[],
): ReadonlyArray<Diagnostic>;
function getAndCacheDiagnostics(
sourceFile: SourceFile | undefined,
cancellationToken: CancellationToken,
cache: DiagnosticCache,
getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken) => Diagnostic[]) {
getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken) => DiagnosticWithLocation[],
): ReadonlyArray<Diagnostic> {

const cachedResult = sourceFile
? cache.perFile && cache.perFile.get(sourceFile.path)
Expand All @@ -1547,7 +1560,7 @@ namespace ts {
const result = getDiagnostics(sourceFile, cancellationToken) || emptyArray;
if (sourceFile) {
if (!cache.perFile) {
cache.perFile = createMap<Diagnostic[]>();
cache.perFile = createMap<DiagnosticWithLocation[]>();
}
cache.perFile.set(sourceFile.path, result);
}
Expand All @@ -1557,7 +1570,7 @@ namespace ts {
return result;
}

function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): Diagnostic[] {
function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): ReadonlyArray<DiagnosticWithLocation> {
return sourceFile.isDeclarationFile ? [] : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
}

Expand Down
4 changes: 2 additions & 2 deletions src/compiler/transformers/declarations.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/*@internal*/
namespace ts {
export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, file: SourceFile | undefined): Diagnostic[] {
export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, file: SourceFile | undefined): DiagnosticWithLocation[] {
if (file && isSourceFileJavaScript(file)) {
return []; // No declaration diagnostics for js for now
}
const compilerOptions = host.getCompilerOptions();
const result = transformNodes(resolver, host, compilerOptions, file ? [file] : filter(host.getSourceFiles(), isSourceFileNotJavaScript), [transformDeclarations], /*allowDtsFiles*/ false);
return result.diagnostics;
return result.diagnostics as DiagnosticWithLocation[]; // TODO: GH#18217
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these all should have a location.. can we push that into TransformationResult

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weswigham can you take a look as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They should all have a location.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, every addDiagnostic call uses createDiagnsoticForNode, which will always have a location - the type of addDiagnsotic on TransformationContext and diagnostics on TransformationResult can just be changed.

}

const declarationEmitNodeBuilderFlags = NodeBuilderFlags.MultilineObjectLiterals | TypeFormatFlags.WriteClassExpressionAsTypeLiteral | NodeBuilderFlags.UseTypeOfFunction | NodeBuilderFlags.UseStructuralFallback | NodeBuilderFlags.AllowEmptyTuple;
Expand Down
24 changes: 15 additions & 9 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2569,16 +2569,16 @@ namespace ts {

// File-level diagnostics reported by the parser (includes diagnostics about /// references
// as well as code diagnostics).
/* @internal */ parseDiagnostics: Diagnostic[];
/* @internal */ parseDiagnostics: DiagnosticWithLocation[];

// File-level diagnostics reported by the binder.
/* @internal */ bindDiagnostics: Diagnostic[];
/* @internal */ bindDiagnostics: DiagnosticWithLocation[];

// File-level JSDoc diagnostics reported by the JSDoc parser
/* @internal */ jsDocDiagnostics?: Diagnostic[];
/* @internal */ jsDocDiagnostics?: DiagnosticWithLocation[];

// Stores additional file-level diagnostics reported by the program
/* @internal */ additionalSyntacticDiagnostics?: ReadonlyArray<Diagnostic>;
/* @internal */ additionalSyntacticDiagnostics?: ReadonlyArray<DiagnosticWithLocation>;

// Stores a line map for the file.
// This field should never be used directly to obtain line map, use getLineMap function instead.
Expand Down Expand Up @@ -2685,9 +2685,9 @@ namespace ts {

getOptionsDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<Diagnostic>;
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<DiagnosticWithLocation>;
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<DiagnosticWithLocation>;
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray<DiagnosticWithLocation>;
getConfigFileParsingDiagnostics(): ReadonlyArray<Diagnostic>;

/**
Expand Down Expand Up @@ -2999,7 +2999,7 @@ namespace ts {
* Does *not* get *all* suggestion diagnostics, just the ones that were convenient to report in the checker.
* Others are added in computeSuggestionDiagnostics.
*/
/* @internal */ getSuggestionDiagnostics(file: SourceFile): ReadonlyArray<Diagnostic>;
/* @internal */ getSuggestionDiagnostics(file: SourceFile): ReadonlyArray<DiagnosticWithLocation>;

/**
* Depending on the operation performed, it may be appropriate to throw away the checker
Expand Down Expand Up @@ -4115,6 +4115,11 @@ namespace ts {
code: number;
source?: string;
}
export interface DiagnosticWithLocation extends Diagnostic {
file: SourceFile;
start: number;
length: number;
}

export enum DiagnosticCategory {
Warning,
Expand Down Expand Up @@ -5136,7 +5141,8 @@ namespace ts {

// If fileName is provided, gets all the diagnostics associated with that file name.
// Otherwise, returns all the diagnostics (global and file associated) in this collection.
getDiagnostics(fileName?: string): Diagnostic[];
getDiagnostics(fileName: string): DiagnosticWithLocation[];
getDiagnostics(): Diagnostic[];

reattachFileDiagnostics(newFile: SourceFile): void;
}
Expand Down
Loading