Fix `--showConfig` to show transitively implied options that vary from the default config (#60240)
This commit is contained in:
Родитель
bd1641f769
Коммит
b8e4ed8aeb
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
addToSeen,
|
||||
AlternateModeDiagnostics,
|
||||
append,
|
||||
arrayFrom,
|
||||
|
@ -2629,7 +2630,7 @@ export function convertToTSConfig(configParseResult: ParsedCommandLine, configFi
|
|||
const providedKeys = new Set(optionMap.keys());
|
||||
const impliedCompilerOptions: Record<string, CompilerOptionsValue> = {};
|
||||
for (const option in computedOptions) {
|
||||
if (!providedKeys.has(option) && some(computedOptions[option].dependencies, dep => providedKeys.has(dep))) {
|
||||
if (!providedKeys.has(option) && optionDependsOn(option, providedKeys)) {
|
||||
const implied = computedOptions[option].computeValue(configParseResult.options);
|
||||
const defaultValue = computedOptions[option].computeValue({});
|
||||
if (implied !== defaultValue) {
|
||||
|
@ -2641,6 +2642,18 @@ export function convertToTSConfig(configParseResult: ParsedCommandLine, configFi
|
|||
return config;
|
||||
}
|
||||
|
||||
function optionDependsOn(option: string, dependsOn: Set<string>): boolean {
|
||||
const seen = new Set<string>();
|
||||
return optionDependsOnRecursive(option);
|
||||
|
||||
function optionDependsOnRecursive(option: string): boolean {
|
||||
if (addToSeen(seen, option)) {
|
||||
return some(computedOptions[option]?.dependencies, dep => dependsOn.has(dep) || optionDependsOnRecursive(dep));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function optionMapToObject(optionMap: Map<string, CompilerOptionsValue>): object {
|
||||
return Object.fromEntries(optionMap);
|
||||
|
|
|
@ -8193,15 +8193,11 @@ export function getLastChild(node: Node): Node | undefined {
|
|||
*
|
||||
* @internal
|
||||
*/
|
||||
export function addToSeen<K>(seen: Map<K, true>, key: K): boolean;
|
||||
/** @internal */
|
||||
export function addToSeen<K, T>(seen: Map<K, T>, key: K, value: T): boolean;
|
||||
/** @internal */
|
||||
export function addToSeen<K, T>(seen: Map<K, T>, key: K, value: T = true as any): boolean {
|
||||
export function addToSeen<K>(seen: Set<K>, key: K): boolean {
|
||||
if (seen.has(key)) {
|
||||
return false;
|
||||
}
|
||||
seen.set(key, value);
|
||||
seen.add(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1297,7 +1297,7 @@ export class ProjectService {
|
|||
*/
|
||||
private readonly filenameToScriptInfoVersion = new Map<Path, number>();
|
||||
// Set of all '.js' files ever opened.
|
||||
private readonly allJsFilesForOpenFileTelemetry = new Map<string, true>();
|
||||
private readonly allJsFilesForOpenFileTelemetry = new Set<string>();
|
||||
|
||||
/**
|
||||
* Map to the real path of the infos
|
||||
|
|
|
@ -36,7 +36,7 @@ registerCodeFix({
|
|||
},
|
||||
getAllCodeActions: context => {
|
||||
const { program } = context;
|
||||
const seen = new Map<number, true>();
|
||||
const seen = new Set<number>();
|
||||
|
||||
return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => {
|
||||
eachDiagnostic(context, errorCodes, diag => {
|
||||
|
|
|
@ -36,7 +36,7 @@ registerCodeFix({
|
|||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: function getAllCodeActionsToConvertToTypeOnlyExport(context) {
|
||||
const fixedExportDeclarations = new Map<number, true>();
|
||||
const fixedExportDeclarations = new Set<number>();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const exportSpecifier = getExportSpecifierForDiagnosticSpan(diag, context.sourceFile);
|
||||
if (exportSpecifier && addToSeen(fixedExportDeclarations, getNodeId(exportSpecifier.parent.parent))) {
|
||||
|
|
|
@ -63,7 +63,7 @@ registerCodeFix({
|
|||
fixIds: [fixId],
|
||||
getAllCodeActions: context => {
|
||||
const { program, preferences, host } = context;
|
||||
const seen = new Map<number, true>();
|
||||
const seen = new Set<number>();
|
||||
|
||||
return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => {
|
||||
eachDiagnostic(context, errorCodes, diag => {
|
||||
|
|
|
@ -182,7 +182,7 @@ registerCodeFix({
|
|||
getAllCodeActions: context => {
|
||||
const { program, fixId } = context;
|
||||
const checker = program.getTypeChecker();
|
||||
const seen = new Map<string, true>();
|
||||
const seen = new Set<string>();
|
||||
const typeDeclToMembers = new Map<ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode, TypeLikeDeclarationInfo[]>();
|
||||
|
||||
return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => {
|
||||
|
|
|
@ -44,7 +44,7 @@ registerCodeFix({
|
|||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: function getAllCodeActionsToFixAwaitInSyncFunction(context) {
|
||||
const seen = new Map<number, true>();
|
||||
const seen = new Set<number>();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const nodes = getNodes(diag.file, diag.start);
|
||||
if (!nodes || !addToSeen(seen, getNodeId(nodes.insertBefore))) return;
|
||||
|
|
|
@ -44,7 +44,7 @@ registerCodeFix({
|
|||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: function getAllCodeActionsToFixClassDoesntImplementInheritedAbstractMember(context) {
|
||||
const seenClassDeclarations = new Map<number, true>();
|
||||
const seenClassDeclarations = new Set<number>();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const classDeclaration = getClass(diag.file, diag.start);
|
||||
if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) {
|
||||
|
|
|
@ -55,7 +55,7 @@ registerCodeFix({
|
|||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions(context) {
|
||||
const seenClassDeclarations = new Map<number, true>();
|
||||
const seenClassDeclarations = new Set<number>();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const classDeclaration = getClass(diag.file, diag.start);
|
||||
if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) {
|
||||
|
|
|
@ -38,7 +38,7 @@ registerCodeFix({
|
|||
fixIds: [fixId],
|
||||
getAllCodeActions(context) {
|
||||
const { sourceFile } = context;
|
||||
const seenClasses = new Map<number, true>(); // Ensure we only do this once per class.
|
||||
const seenClasses = new Set<number>(); // Ensure we only do this once per class.
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const nodes = getNodes(diag.file, diag.start);
|
||||
if (!nodes) return;
|
||||
|
|
|
@ -3585,7 +3585,7 @@ function getCompletionData(
|
|||
let importSpecifierResolver: codefix.ImportSpecifierResolver | undefined;
|
||||
const symbolToOriginInfoMap: SymbolOriginInfoMap = [];
|
||||
const symbolToSortTextMap: SymbolSortTextMap = [];
|
||||
const seenPropertySymbols = new Map<SymbolId, true>();
|
||||
const seenPropertySymbols = new Set<SymbolId>();
|
||||
const isTypeOnlyLocation = isTypeOnlyCompletion();
|
||||
const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => {
|
||||
return createModuleSpecifierResolutionHost(isFromPackageJson ? host.getPackageJsonAutoImportProvider!()! : program, host);
|
||||
|
@ -6051,14 +6051,14 @@ function isArrowFunctionBody(node: Node) {
|
|||
}
|
||||
|
||||
/** True if symbol is a type or a module containing at least one type. */
|
||||
function symbolCanBeReferencedAtTypeLocation(symbol: Symbol, checker: TypeChecker, seenModules = new Map<SymbolId, true>()): boolean {
|
||||
function symbolCanBeReferencedAtTypeLocation(symbol: Symbol, checker: TypeChecker, seenModules = new Set<Symbol>()): boolean {
|
||||
// Since an alias can be merged with a local declaration, we need to test both the alias and its target.
|
||||
// This code used to just test the result of `skipAlias`, but that would ignore any locally introduced meanings.
|
||||
return nonAliasCanBeReferencedAtTypeLocation(symbol) || nonAliasCanBeReferencedAtTypeLocation(skipAlias(symbol.exportSymbol || symbol, checker));
|
||||
|
||||
function nonAliasCanBeReferencedAtTypeLocation(symbol: Symbol): boolean {
|
||||
return !!(symbol.flags & SymbolFlags.Type) || checker.isUnknownSymbol(symbol) ||
|
||||
!!(symbol.flags & SymbolFlags.Module) && addToSeen(seenModules, getSymbolId(symbol)) &&
|
||||
!!(symbol.flags & SymbolFlags.Module) && addToSeen(seenModules, symbol) &&
|
||||
checker.getExportsOfModule(symbol).some(e => symbolCanBeReferencedAtTypeLocation(e, checker, seenModules));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -555,7 +555,7 @@ export function getExportInfoMap(importingFile: SourceFile | FutureSourceFile, h
|
|||
try {
|
||||
forEachExternalModuleToImportFrom(program, host, preferences, /*useAutoImportProvider*/ true, (moduleSymbol, moduleFile, program, isFromPackageJson) => {
|
||||
if (++moduleCount % 100 === 0) cancellationToken?.throwIfCancellationRequested();
|
||||
const seenExports = new Map<__String, true>();
|
||||
const seenExports = new Set<__String>();
|
||||
const checker = program.getTypeChecker();
|
||||
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker);
|
||||
// Note: I think we shouldn't actually see resolved module symbols here, but weird merges
|
||||
|
@ -634,7 +634,7 @@ function getNamesForExportedSymbol(defaultExport: Symbol, checker: TypeChecker,
|
|||
export function forEachNameOfDefaultExport<T>(defaultExport: Symbol, checker: TypeChecker, scriptTarget: ScriptTarget | undefined, cb: (name: string, capitalizedName?: string) => T | undefined): T | undefined {
|
||||
let chain: Symbol[] | undefined;
|
||||
let current: Symbol | undefined = defaultExport;
|
||||
const seen = new Map<Symbol, true>();
|
||||
const seen = new Set<Symbol>();
|
||||
|
||||
while (current) {
|
||||
// The predecessor to this function also looked for a name on the `localSymbol`
|
||||
|
|
|
@ -251,7 +251,6 @@ import {
|
|||
SymbolDisplayPart,
|
||||
SymbolDisplayPartKind,
|
||||
SymbolFlags,
|
||||
SymbolId,
|
||||
symbolName,
|
||||
SyntaxKind,
|
||||
textPart,
|
||||
|
@ -544,7 +543,7 @@ export function getImplementationsAtPosition(program: Program, cancellationToken
|
|||
}
|
||||
else if (entries) {
|
||||
const queue = createQueue(entries);
|
||||
const seenNodes = new Map<number, true>();
|
||||
const seenNodes = new Set<number>();
|
||||
while (!queue.isEmpty()) {
|
||||
const entry = queue.dequeue() as NodeEntry;
|
||||
if (!addToSeen(seenNodes, getNodeId(entry.node))) {
|
||||
|
@ -2666,7 +2665,7 @@ export namespace Core {
|
|||
* The value of previousIterationSymbol is undefined when the function is first called.
|
||||
*/
|
||||
function getPropertySymbolsFromBaseTypes<T>(symbol: Symbol, propertyName: string, checker: TypeChecker, cb: (symbol: Symbol) => T | undefined): T | undefined {
|
||||
const seen = new Map<SymbolId, true>();
|
||||
const seen = new Set<Symbol>();
|
||||
return recur(symbol);
|
||||
|
||||
function recur(symbol: Symbol): T | undefined {
|
||||
|
@ -2674,7 +2673,7 @@ export namespace Core {
|
|||
// interface C extends C {
|
||||
// /*findRef*/propName: string;
|
||||
// }
|
||||
if (!(symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) || !addToSeen(seen, getSymbolId(symbol))) return;
|
||||
if (!(symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) || !addToSeen(seen, symbol)) return;
|
||||
|
||||
return firstDefined(symbol.declarations, declaration =>
|
||||
firstDefined(getAllSuperTypeNodes(declaration), typeReference => {
|
||||
|
|
|
@ -247,7 +247,7 @@ function flattenTypeLiteralNodeReference(checker: TypeChecker, selection: TypeNo
|
|||
}
|
||||
if (isIntersectionTypeNode(selection)) {
|
||||
const result: TypeElement[] = [];
|
||||
const seen = new Map<string, true>();
|
||||
const seen = new Set<string>();
|
||||
for (const type of selection.types) {
|
||||
const flattenedTypeMembers = flattenTypeLiteralNodeReference(checker, type);
|
||||
if (!flattenedTypeMembers || !flattenedTypeMembers.every(type => type.name && addToSeen(seen, getNameFromPropertyName(type.name) as string))) {
|
||||
|
|
|
@ -555,7 +555,7 @@ function getAlreadyUsedTypesInStringLiteralUnion(union: UnionTypeNode, current:
|
|||
|
||||
function getStringLiteralCompletionsFromSignature(call: CallLikeExpression, arg: StringLiteralLike, argumentInfo: SignatureHelp.ArgumentInfoForCompletions, checker: TypeChecker): StringLiteralCompletionsFromTypes | undefined {
|
||||
let isNewIdentifier = false;
|
||||
const uniques = new Map<string, true>();
|
||||
const uniques = new Set<string>();
|
||||
const editingArgument = isJsxOpeningLikeElement(call) ? Debug.checkDefined(findAncestor(arg.parent, isJsxAttribute)) : arg;
|
||||
const candidates = checker.getCandidateSignaturesForStringLiteralCompletions(call, editingArgument);
|
||||
const types = flatMap(candidates, candidate => {
|
||||
|
@ -600,7 +600,7 @@ function stringLiteralCompletionsForObjectLiteral(checker: TypeChecker, objectLi
|
|||
};
|
||||
}
|
||||
|
||||
function getStringLiteralTypes(type: Type | undefined, uniques = new Map<string, true>()): readonly StringLiteralType[] {
|
||||
function getStringLiteralTypes(type: Type | undefined, uniques = new Set<string>()): readonly StringLiteralType[] {
|
||||
if (!type) return emptyArray;
|
||||
type = skipConstraint(type);
|
||||
return type.isUnion() ? flatMap(type.types, t => getStringLiteralTypes(t, uniques)) :
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {
|
||||
addToSeen,
|
||||
ArrowFunction,
|
||||
BindingElement,
|
||||
CharacterCodes,
|
||||
|
@ -493,7 +492,7 @@ export function isThisTypeAnnotatable(containingFunction: SignatureDeclaration):
|
|||
export class ChangeTracker {
|
||||
private readonly changes: Change[] = [];
|
||||
private newFileChanges?: MultiMap<string, NewFileInsertion>;
|
||||
private readonly classesWithNodesInsertedAtStart = new Map<number, { readonly node: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression; readonly sourceFile: SourceFile; }>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
|
||||
private readonly classesWithNodesInsertedAtStart = new Map<number, { readonly node: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression | TypeLiteralNode | EnumDeclaration; readonly sourceFile: SourceFile; }>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
|
||||
private readonly deletedNodes: { readonly sourceFile: SourceFile; readonly node: Node | NodeArray<TypeParameterDeclaration>; }[] = [];
|
||||
|
||||
public static fromContext(context: TextChangesContext): ChangeTracker {
|
||||
|
@ -903,7 +902,10 @@ export class ChangeTracker {
|
|||
|
||||
const members = getMembersOrProperties(node);
|
||||
const isEmpty = members.length === 0;
|
||||
const isFirstInsertion = addToSeen(this.classesWithNodesInsertedAtStart, getNodeId(node), { node, sourceFile });
|
||||
const isFirstInsertion = !this.classesWithNodesInsertedAtStart.has(getNodeId(node));
|
||||
if (isFirstInsertion) {
|
||||
this.classesWithNodesInsertedAtStart.set(getNodeId(node), { node, sourceFile });
|
||||
}
|
||||
const insertTrailingComma = isObjectLiteralExpression(node) && (!isJsonSourceFile(sourceFile) || !isEmpty);
|
||||
const insertLeadingComma = isObjectLiteralExpression(node) && isJsonSourceFile(sourceFile) && isEmpty && !isFirstInsertion;
|
||||
return {
|
||||
|
@ -1246,7 +1248,7 @@ function endPositionToDeleteNodeInList(sourceFile: SourceFile, node: Node, prevN
|
|||
return end;
|
||||
}
|
||||
|
||||
function getClassOrObjectBraceEnds(cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression, sourceFile: SourceFile): [number | undefined, number | undefined] {
|
||||
function getClassOrObjectBraceEnds(cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression | TypeLiteralNode | EnumDeclaration, sourceFile: SourceFile): [number | undefined, number | undefined] {
|
||||
const open = findChildOfKind(cls, SyntaxKind.OpenBraceToken, sourceFile);
|
||||
const close = findChildOfKind(cls, SyntaxKind.CloseBraceToken, sourceFile);
|
||||
return [open?.end, close?.end];
|
||||
|
|
|
@ -57,6 +57,8 @@ describe("unittests:: config:: showConfig", () => {
|
|||
|
||||
showTSConfigCorrectly("Show TSConfig with advanced options", ["--showConfig", "--declaration", "--declarationDir", "lib", "--skipLibCheck", "--noErrorTruncation"]);
|
||||
|
||||
showTSConfigCorrectly("Show TSConfig with transitively implied options", ["--showConfig", "--module", "nodenext"]);
|
||||
|
||||
showTSConfigCorrectly("Show TSConfig with compileOnSave and more", ["-p", "tsconfig.json"], {
|
||||
compilerOptions: {
|
||||
esModuleInterop: true,
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "nodenext",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "nodenext",
|
||||
"moduleDetection": "force",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolvePackageJsonExports": true,
|
||||
"resolvePackageJsonImports": true,
|
||||
"useDefineForClassFields": true
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче