TypeScript/src/services/codeFixProvider.ts
Wenlu Wang 2f0c8b2bea
--noImplicitOverride (#39669)
* wip: add types

* wip

* Add cases

* Add some case

* Add more check

* accept baseline

* add abstract abd declare method

* add override in declaration

* accept baseline

* add property override

* Fix decalre modifier

* update baseline

* Add more cases

* make lint happy

* make lint happy

* Update description

* Add codefix

* simplify code

* accept baseline

* Update desc

* Accept baseline

* Add override completions

* filter out implements field in override context

* fix tests

* Add parameter property check

* Accept baseline

* acept baseline

* Add parameter property to declaration code action

* Add quickfix for override parameter property

* fix code style

* Add override with interface tests

* Add more cases about modifier position

* rename flag

* rename flags

* Added tests.

* Accepted baselines.

* Always issue errors for unnecessary 'override' modifiers.

* Accepted baselines.

* Override perf (#4)

* try cache check result

* pre check for override

* Do not issue error if implement abstract

* Add abstract-spec check

* Avoid override dead lock

* Add more case

* Add codefix for new error

* Fix error message

* Add jsdoc override tag (#6)

* Override jsdoc tag (#7)

* accept baseline

* Disallow codefix in js

* update baseline

* Omit override in d.ts

* Add more case in js

* Accept baseline

* fix override js test

* fix crlf

* Revert merge conflict changes

* Accept baseline

* Avoid space

* Fix CR issues

* Accept baseline

* Fix typo and add more check

* Fix error name

Co-authored-by: Daniel Rosenwasser <Daniel.Rosenwasser@microsoft.com>
2021-03-26 16:29:22 -07:00

104 lines
5.2 KiB
TypeScript

/* @internal */
namespace ts.codefix {
const errorCodeToFixes = createMultiMap<CodeFixRegistration>();
const fixIdToRegistration = new Map<string, CodeFixRegistration>();
export type DiagnosticAndArguments = DiagnosticMessage | [DiagnosticMessage, string] | [DiagnosticMessage, string, string];
function diagnosticToString(diag: DiagnosticAndArguments): string {
return isArray(diag)
? formatStringFromArgs(getLocaleSpecificMessage(diag[0]), diag.slice(1) as readonly string[])
: getLocaleSpecificMessage(diag);
}
export function createCodeFixActionWithoutFixAll(fixName: string, changes: FileTextChanges[], description: DiagnosticAndArguments) {
return createCodeFixActionWorker(fixName, diagnosticToString(description), changes, /*fixId*/ undefined, /*fixAllDescription*/ undefined);
}
export function createCodeFixAction(fixName: string, changes: FileTextChanges[], description: DiagnosticAndArguments, fixId: {}, fixAllDescription: DiagnosticAndArguments, command?: CodeActionCommand): CodeFixAction {
return createCodeFixActionWorker(fixName, diagnosticToString(description), changes, fixId, diagnosticToString(fixAllDescription), command);
}
export function createCodeFixActionMaybeFixAll(fixName: string, changes: FileTextChanges[], description: DiagnosticAndArguments, fixId?: {}, fixAllDescription?: DiagnosticAndArguments, command?: CodeActionCommand) {
return createCodeFixActionWorker(fixName, diagnosticToString(description), changes, fixId, fixAllDescription && diagnosticToString(fixAllDescription), command);
}
function createCodeFixActionWorker(fixName: string, description: string, changes: FileTextChanges[], fixId?: {}, fixAllDescription?: string, command?: CodeActionCommand): CodeFixAction {
return { fixName, description, changes, fixId, fixAllDescription, commands: command ? [command] : undefined };
}
export function registerCodeFix(reg: CodeFixRegistration) {
for (const error of reg.errorCodes) {
errorCodeToFixes.add(String(error), reg);
}
if (reg.fixIds) {
for (const fixId of reg.fixIds) {
Debug.assert(!fixIdToRegistration.has(fixId));
fixIdToRegistration.set(fixId, reg);
}
}
}
export function getSupportedErrorCodes(): string[] {
return arrayFrom(errorCodeToFixes.keys());
}
function removeFixIdIfFixAllUnavailable(registration: CodeFixRegistration, diagnostics: Diagnostic[]) {
const { errorCodes } = registration;
let maybeFixableDiagnostics = 0;
for (const diag of diagnostics) {
if (contains(errorCodes, diag.code)) maybeFixableDiagnostics++;
if (maybeFixableDiagnostics > 1) break;
}
const fixAllUnavailable = maybeFixableDiagnostics < 2;
return ({ fixId, fixAllDescription, ...action }: CodeFixAction): CodeFixAction => {
return fixAllUnavailable ? action : { ...action, fixId, fixAllDescription };
};
}
export function getFixes(context: CodeFixContext): readonly CodeFixAction[] {
const diagnostics = getDiagnostics(context);
const registrations = errorCodeToFixes.get(String(context.errorCode));
return flatMap(registrations, f => map(f.getCodeActions(context), removeFixIdIfFixAllUnavailable(f, diagnostics)));
}
export function getAllFixes(context: CodeFixAllContext): CombinedCodeActions {
// Currently fixId is always a string.
return fixIdToRegistration.get(cast(context.fixId, isString))!.getAllCodeActions!(context);
}
export function createCombinedCodeActions(changes: FileTextChanges[], commands?: CodeActionCommand[]): CombinedCodeActions {
return { changes, commands };
}
export function createFileTextChanges(fileName: string, textChanges: TextChange[]): FileTextChanges {
return { fileName, textChanges };
}
export function codeFixAll(
context: CodeFixAllContext,
errorCodes: number[],
use: (changes: textChanges.ChangeTracker, error: DiagnosticWithLocation, commands: Push<CodeActionCommand>) => void,
): CombinedCodeActions {
const commands: CodeActionCommand[] = [];
const changes = textChanges.ChangeTracker.with(context, t => eachDiagnostic(context, errorCodes, diag => use(t, diag, commands)));
return createCombinedCodeActions(changes, commands.length === 0 ? undefined : commands);
}
export function eachDiagnostic(context: CodeFixAllContext, errorCodes: readonly number[], cb: (diag: DiagnosticWithLocation) => void): void {
for (const diag of getDiagnostics(context)) {
if (contains(errorCodes, diag.code)) {
cb(diag as DiagnosticWithLocation);
}
}
}
function getDiagnostics({ program, sourceFile, cancellationToken }: CodeFixContextBase) {
return [
...program.getSemanticDiagnostics(sourceFile, cancellationToken),
...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
...computeSuggestionDiagnostics(sourceFile, program, cancellationToken)
];
}
}