Skip to content

Commit b8e4ed8

Browse files
authored
Fix --showConfig to show transitively implied options that vary from the default config (#60240)
1 parent bd1641f commit b8e4ed8

19 files changed

+57
-32
lines changed

src/compiler/commandLineParser.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
addToSeen,
23
AlternateModeDiagnostics,
34
append,
45
arrayFrom,
@@ -2629,7 +2630,7 @@ export function convertToTSConfig(configParseResult: ParsedCommandLine, configFi
26292630
const providedKeys = new Set(optionMap.keys());
26302631
const impliedCompilerOptions: Record<string, CompilerOptionsValue> = {};
26312632
for (const option in computedOptions) {
2632-
if (!providedKeys.has(option) && some(computedOptions[option].dependencies, dep => providedKeys.has(dep))) {
2633+
if (!providedKeys.has(option) && optionDependsOn(option, providedKeys)) {
26332634
const implied = computedOptions[option].computeValue(configParseResult.options);
26342635
const defaultValue = computedOptions[option].computeValue({});
26352636
if (implied !== defaultValue) {
@@ -2641,6 +2642,18 @@ export function convertToTSConfig(configParseResult: ParsedCommandLine, configFi
26412642
return config;
26422643
}
26432644

2645+
function optionDependsOn(option: string, dependsOn: Set<string>): boolean {
2646+
const seen = new Set<string>();
2647+
return optionDependsOnRecursive(option);
2648+
2649+
function optionDependsOnRecursive(option: string): boolean {
2650+
if (addToSeen(seen, option)) {
2651+
return some(computedOptions[option]?.dependencies, dep => dependsOn.has(dep) || optionDependsOnRecursive(dep));
2652+
}
2653+
return false;
2654+
}
2655+
}
2656+
26442657
/** @internal */
26452658
export function optionMapToObject(optionMap: Map<string, CompilerOptionsValue>): object {
26462659
return Object.fromEntries(optionMap);

src/compiler/utilities.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -8193,15 +8193,11 @@ export function getLastChild(node: Node): Node | undefined {
81938193
*
81948194
* @internal
81958195
*/
8196-
export function addToSeen<K>(seen: Map<K, true>, key: K): boolean;
8197-
/** @internal */
8198-
export function addToSeen<K, T>(seen: Map<K, T>, key: K, value: T): boolean;
8199-
/** @internal */
8200-
export function addToSeen<K, T>(seen: Map<K, T>, key: K, value: T = true as any): boolean {
8196+
export function addToSeen<K>(seen: Set<K>, key: K): boolean {
82018197
if (seen.has(key)) {
82028198
return false;
82038199
}
8204-
seen.set(key, value);
8200+
seen.add(key);
82058201
return true;
82068202
}
82078203

src/server/editorServices.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1297,7 +1297,7 @@ export class ProjectService {
12971297
*/
12981298
private readonly filenameToScriptInfoVersion = new Map<Path, number>();
12991299
// Set of all '.js' files ever opened.
1300-
private readonly allJsFilesForOpenFileTelemetry = new Map<string, true>();
1300+
private readonly allJsFilesForOpenFileTelemetry = new Set<string>();
13011301

13021302
/**
13031303
* Map to the real path of the infos

src/services/codefixes/convertConstToLet.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ registerCodeFix({
3636
},
3737
getAllCodeActions: context => {
3838
const { program } = context;
39-
const seen = new Map<number, true>();
39+
const seen = new Set<number>();
4040

4141
return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => {
4242
eachDiagnostic(context, errorCodes, diag => {

src/services/codefixes/convertToTypeOnlyExport.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ registerCodeFix({
3636
},
3737
fixIds: [fixId],
3838
getAllCodeActions: function getAllCodeActionsToConvertToTypeOnlyExport(context) {
39-
const fixedExportDeclarations = new Map<number, true>();
39+
const fixedExportDeclarations = new Set<number>();
4040
return codeFixAll(context, errorCodes, (changes, diag) => {
4141
const exportSpecifier = getExportSpecifierForDiagnosticSpan(diag, context.sourceFile);
4242
if (exportSpecifier && addToSeen(fixedExportDeclarations, getNodeId(exportSpecifier.parent.parent))) {

src/services/codefixes/fixAddMissingConstraint.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ registerCodeFix({
6363
fixIds: [fixId],
6464
getAllCodeActions: context => {
6565
const { program, preferences, host } = context;
66-
const seen = new Map<number, true>();
66+
const seen = new Set<number>();
6767

6868
return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => {
6969
eachDiagnostic(context, errorCodes, diag => {

src/services/codefixes/fixAddMissingMember.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ registerCodeFix({
182182
getAllCodeActions: context => {
183183
const { program, fixId } = context;
184184
const checker = program.getTypeChecker();
185-
const seen = new Map<string, true>();
185+
const seen = new Set<string>();
186186
const typeDeclToMembers = new Map<ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode, TypeLikeDeclarationInfo[]>();
187187

188188
return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => {

src/services/codefixes/fixAwaitInSyncFunction.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ registerCodeFix({
4444
},
4545
fixIds: [fixId],
4646
getAllCodeActions: function getAllCodeActionsToFixAwaitInSyncFunction(context) {
47-
const seen = new Map<number, true>();
47+
const seen = new Set<number>();
4848
return codeFixAll(context, errorCodes, (changes, diag) => {
4949
const nodes = getNodes(diag.file, diag.start);
5050
if (!nodes || !addToSeen(seen, getNodeId(nodes.insertBefore))) return;

src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ registerCodeFix({
4444
},
4545
fixIds: [fixId],
4646
getAllCodeActions: function getAllCodeActionsToFixClassDoesntImplementInheritedAbstractMember(context) {
47-
const seenClassDeclarations = new Map<number, true>();
47+
const seenClassDeclarations = new Set<number>();
4848
return codeFixAll(context, errorCodes, (changes, diag) => {
4949
const classDeclaration = getClass(diag.file, diag.start);
5050
if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) {

src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ registerCodeFix({
5555
},
5656
fixIds: [fixId],
5757
getAllCodeActions(context) {
58-
const seenClassDeclarations = new Map<number, true>();
58+
const seenClassDeclarations = new Set<number>();
5959
return codeFixAll(context, errorCodes, (changes, diag) => {
6060
const classDeclaration = getClass(diag.file, diag.start);
6161
if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) {

src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ registerCodeFix({
3838
fixIds: [fixId],
3939
getAllCodeActions(context) {
4040
const { sourceFile } = context;
41-
const seenClasses = new Map<number, true>(); // Ensure we only do this once per class.
41+
const seenClasses = new Set<number>(); // Ensure we only do this once per class.
4242
return codeFixAll(context, errorCodes, (changes, diag) => {
4343
const nodes = getNodes(diag.file, diag.start);
4444
if (!nodes) return;

src/services/completions.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3585,7 +3585,7 @@ function getCompletionData(
35853585
let importSpecifierResolver: codefix.ImportSpecifierResolver | undefined;
35863586
const symbolToOriginInfoMap: SymbolOriginInfoMap = [];
35873587
const symbolToSortTextMap: SymbolSortTextMap = [];
3588-
const seenPropertySymbols = new Map<SymbolId, true>();
3588+
const seenPropertySymbols = new Set<SymbolId>();
35893589
const isTypeOnlyLocation = isTypeOnlyCompletion();
35903590
const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => {
35913591
return createModuleSpecifierResolutionHost(isFromPackageJson ? host.getPackageJsonAutoImportProvider!()! : program, host);
@@ -6051,14 +6051,14 @@ function isArrowFunctionBody(node: Node) {
60516051
}
60526052

60536053
/** True if symbol is a type or a module containing at least one type. */
6054-
function symbolCanBeReferencedAtTypeLocation(symbol: Symbol, checker: TypeChecker, seenModules = new Map<SymbolId, true>()): boolean {
6054+
function symbolCanBeReferencedAtTypeLocation(symbol: Symbol, checker: TypeChecker, seenModules = new Set<Symbol>()): boolean {
60556055
// Since an alias can be merged with a local declaration, we need to test both the alias and its target.
60566056
// This code used to just test the result of `skipAlias`, but that would ignore any locally introduced meanings.
60576057
return nonAliasCanBeReferencedAtTypeLocation(symbol) || nonAliasCanBeReferencedAtTypeLocation(skipAlias(symbol.exportSymbol || symbol, checker));
60586058

60596059
function nonAliasCanBeReferencedAtTypeLocation(symbol: Symbol): boolean {
60606060
return !!(symbol.flags & SymbolFlags.Type) || checker.isUnknownSymbol(symbol) ||
6061-
!!(symbol.flags & SymbolFlags.Module) && addToSeen(seenModules, getSymbolId(symbol)) &&
6061+
!!(symbol.flags & SymbolFlags.Module) && addToSeen(seenModules, symbol) &&
60626062
checker.getExportsOfModule(symbol).some(e => symbolCanBeReferencedAtTypeLocation(e, checker, seenModules));
60636063
}
60646064
}

src/services/exportInfoMap.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ export function getExportInfoMap(importingFile: SourceFile | FutureSourceFile, h
555555
try {
556556
forEachExternalModuleToImportFrom(program, host, preferences, /*useAutoImportProvider*/ true, (moduleSymbol, moduleFile, program, isFromPackageJson) => {
557557
if (++moduleCount % 100 === 0) cancellationToken?.throwIfCancellationRequested();
558-
const seenExports = new Map<__String, true>();
558+
const seenExports = new Set<__String>();
559559
const checker = program.getTypeChecker();
560560
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker);
561561
// 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,
634634
export function forEachNameOfDefaultExport<T>(defaultExport: Symbol, checker: TypeChecker, scriptTarget: ScriptTarget | undefined, cb: (name: string, capitalizedName?: string) => T | undefined): T | undefined {
635635
let chain: Symbol[] | undefined;
636636
let current: Symbol | undefined = defaultExport;
637-
const seen = new Map<Symbol, true>();
637+
const seen = new Set<Symbol>();
638638

639639
while (current) {
640640
// The predecessor to this function also looked for a name on the `localSymbol`

src/services/findAllReferences.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ import {
251251
SymbolDisplayPart,
252252
SymbolDisplayPartKind,
253253
SymbolFlags,
254-
SymbolId,
255254
symbolName,
256255
SyntaxKind,
257256
textPart,
@@ -544,7 +543,7 @@ export function getImplementationsAtPosition(program: Program, cancellationToken
544543
}
545544
else if (entries) {
546545
const queue = createQueue(entries);
547-
const seenNodes = new Map<number, true>();
546+
const seenNodes = new Set<number>();
548547
while (!queue.isEmpty()) {
549548
const entry = queue.dequeue() as NodeEntry;
550549
if (!addToSeen(seenNodes, getNodeId(entry.node))) {
@@ -2666,15 +2665,15 @@ export namespace Core {
26662665
* The value of previousIterationSymbol is undefined when the function is first called.
26672666
*/
26682667
function getPropertySymbolsFromBaseTypes<T>(symbol: Symbol, propertyName: string, checker: TypeChecker, cb: (symbol: Symbol) => T | undefined): T | undefined {
2669-
const seen = new Map<SymbolId, true>();
2668+
const seen = new Set<Symbol>();
26702669
return recur(symbol);
26712670

26722671
function recur(symbol: Symbol): T | undefined {
26732672
// Use `addToSeen` to ensure we don't infinitely recurse in this situation:
26742673
// interface C extends C {
26752674
// /*findRef*/propName: string;
26762675
// }
2677-
if (!(symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) || !addToSeen(seen, getSymbolId(symbol))) return;
2676+
if (!(symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) || !addToSeen(seen, symbol)) return;
26782677

26792678
return firstDefined(symbol.declarations, declaration =>
26802679
firstDefined(getAllSuperTypeNodes(declaration), typeReference => {

src/services/refactors/extractType.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ function flattenTypeLiteralNodeReference(checker: TypeChecker, selection: TypeNo
247247
}
248248
if (isIntersectionTypeNode(selection)) {
249249
const result: TypeElement[] = [];
250-
const seen = new Map<string, true>();
250+
const seen = new Set<string>();
251251
for (const type of selection.types) {
252252
const flattenedTypeMembers = flattenTypeLiteralNodeReference(checker, type);
253253
if (!flattenedTypeMembers || !flattenedTypeMembers.every(type => type.name && addToSeen(seen, getNameFromPropertyName(type.name) as string))) {

src/services/stringCompletions.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ function getAlreadyUsedTypesInStringLiteralUnion(union: UnionTypeNode, current:
555555

556556
function getStringLiteralCompletionsFromSignature(call: CallLikeExpression, arg: StringLiteralLike, argumentInfo: SignatureHelp.ArgumentInfoForCompletions, checker: TypeChecker): StringLiteralCompletionsFromTypes | undefined {
557557
let isNewIdentifier = false;
558-
const uniques = new Map<string, true>();
558+
const uniques = new Set<string>();
559559
const editingArgument = isJsxOpeningLikeElement(call) ? Debug.checkDefined(findAncestor(arg.parent, isJsxAttribute)) : arg;
560560
const candidates = checker.getCandidateSignaturesForStringLiteralCompletions(call, editingArgument);
561561
const types = flatMap(candidates, candidate => {
@@ -600,7 +600,7 @@ function stringLiteralCompletionsForObjectLiteral(checker: TypeChecker, objectLi
600600
};
601601
}
602602

603-
function getStringLiteralTypes(type: Type | undefined, uniques = new Map<string, true>()): readonly StringLiteralType[] {
603+
function getStringLiteralTypes(type: Type | undefined, uniques = new Set<string>()): readonly StringLiteralType[] {
604604
if (!type) return emptyArray;
605605
type = skipConstraint(type);
606606
return type.isUnion() ? flatMap(type.types, t => getStringLiteralTypes(t, uniques)) :

src/services/textChanges.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
addToSeen,
32
ArrowFunction,
43
BindingElement,
54
CharacterCodes,
@@ -493,7 +492,7 @@ export function isThisTypeAnnotatable(containingFunction: SignatureDeclaration):
493492
export class ChangeTracker {
494493
private readonly changes: Change[] = [];
495494
private newFileChanges?: MultiMap<string, NewFileInsertion>;
496-
private readonly classesWithNodesInsertedAtStart = new Map<number, { readonly node: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression; readonly sourceFile: SourceFile; }>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
495+
private readonly classesWithNodesInsertedAtStart = new Map<number, { readonly node: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression | TypeLiteralNode | EnumDeclaration; readonly sourceFile: SourceFile; }>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
497496
private readonly deletedNodes: { readonly sourceFile: SourceFile; readonly node: Node | NodeArray<TypeParameterDeclaration>; }[] = [];
498497

499498
public static fromContext(context: TextChangesContext): ChangeTracker {
@@ -903,7 +902,10 @@ export class ChangeTracker {
903902

904903
const members = getMembersOrProperties(node);
905904
const isEmpty = members.length === 0;
906-
const isFirstInsertion = addToSeen(this.classesWithNodesInsertedAtStart, getNodeId(node), { node, sourceFile });
905+
const isFirstInsertion = !this.classesWithNodesInsertedAtStart.has(getNodeId(node));
906+
if (isFirstInsertion) {
907+
this.classesWithNodesInsertedAtStart.set(getNodeId(node), { node, sourceFile });
908+
}
907909
const insertTrailingComma = isObjectLiteralExpression(node) && (!isJsonSourceFile(sourceFile) || !isEmpty);
908910
const insertLeadingComma = isObjectLiteralExpression(node) && isJsonSourceFile(sourceFile) && isEmpty && !isFirstInsertion;
909911
return {
@@ -1246,7 +1248,7 @@ function endPositionToDeleteNodeInList(sourceFile: SourceFile, node: Node, prevN
12461248
return end;
12471249
}
12481250

1249-
function getClassOrObjectBraceEnds(cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression, sourceFile: SourceFile): [number | undefined, number | undefined] {
1251+
function getClassOrObjectBraceEnds(cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression | TypeLiteralNode | EnumDeclaration, sourceFile: SourceFile): [number | undefined, number | undefined] {
12501252
const open = findChildOfKind(cls, SyntaxKind.OpenBraceToken, sourceFile);
12511253
const close = findChildOfKind(cls, SyntaxKind.CloseBraceToken, sourceFile);
12521254
return [open?.end, close?.end];

src/testRunner/unittests/config/showConfig.ts

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ describe("unittests:: config:: showConfig", () => {
5757

5858
showTSConfigCorrectly("Show TSConfig with advanced options", ["--showConfig", "--declaration", "--declarationDir", "lib", "--skipLibCheck", "--noErrorTruncation"]);
5959

60+
showTSConfigCorrectly("Show TSConfig with transitively implied options", ["--showConfig", "--module", "nodenext"]);
61+
6062
showTSConfigCorrectly("Show TSConfig with compileOnSave and more", ["-p", "tsconfig.json"], {
6163
compilerOptions: {
6264
esModuleInterop: true,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"compilerOptions": {
3+
"module": "nodenext",
4+
"target": "esnext",
5+
"moduleResolution": "nodenext",
6+
"moduleDetection": "force",
7+
"esModuleInterop": true,
8+
"allowSyntheticDefaultImports": true,
9+
"resolvePackageJsonExports": true,
10+
"resolvePackageJsonImports": true,
11+
"useDefineForClassFields": true
12+
}
13+
}

0 commit comments

Comments
 (0)