Skip to content

Commit

Permalink
Fix --showConfig to show transitively implied options that vary fro…
Browse files Browse the repository at this point in the history
…m the default config (#60240)
  • Loading branch information
andrewbranch authored Oct 16, 2024
1 parent bd1641f commit b8e4ed8
Show file tree
Hide file tree
Showing 19 changed files with 57 additions and 32 deletions.
15 changes: 14 additions & 1 deletion src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
addToSeen,
AlternateModeDiagnostics,
append,
arrayFrom,
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
8 changes: 2 additions & 6 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/services/codefixes/convertConstToLet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
2 changes: 1 addition & 1 deletion src/services/codefixes/convertToTypeOnlyExport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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))) {
Expand Down
2 changes: 1 addition & 1 deletion src/services/codefixes/fixAddMissingConstraint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
2 changes: 1 addition & 1 deletion src/services/codefixes/fixAddMissingMember.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
2 changes: 1 addition & 1 deletion src/services/codefixes/fixAwaitInSyncFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions src/services/completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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));
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/services/exportInfoMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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`
Expand Down
7 changes: 3 additions & 4 deletions src/services/findAllReferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,6 @@ import {
SymbolDisplayPart,
SymbolDisplayPartKind,
SymbolFlags,
SymbolId,
symbolName,
SyntaxKind,
textPart,
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -2666,15 +2665,15 @@ 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 {
// Use `addToSeen` to ensure we don't infinitely recurse in this situation:
// 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 => {
Expand Down
2 changes: 1 addition & 1 deletion src/services/refactors/extractType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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))) {
Expand Down
4 changes: 2 additions & 2 deletions src/services/stringCompletions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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)) :
Expand Down
10 changes: 6 additions & 4 deletions src/services/textChanges.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
addToSeen,
ArrowFunction,
BindingElement,
CharacterCodes,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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];
Expand Down
2 changes: 2 additions & 0 deletions src/testRunner/unittests/config/showConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"module": "nodenext",
"target": "esnext",
"moduleResolution": "nodenext",
"moduleDetection": "force",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolvePackageJsonExports": true,
"resolvePackageJsonImports": true,
"useDefineForClassFields": true
}
}

0 comments on commit b8e4ed8

Please sign in to comment.