Fix `--showConfig` to show transitively implied options that vary from the default config (#60240)

This commit is contained in:
Andrew Branch 2024-10-16 11:14:44 -07:00 коммит произвёл GitHub
Родитель bd1641f769
Коммит b8e4ed8aeb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
19 изменённых файлов: 57 добавлений и 32 удалений

Просмотреть файл

@ -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
}
}