Disallow floating promises. (#14605)

This commit is contained in:
Christian Gunderman
2025-12-05 16:12:49 -08:00
committed by GitHub
parent 3cf44acc08
commit 025e450ac2
57 changed files with 128 additions and 4 deletions
@@ -346,6 +346,7 @@ export class ExtensionManager extends ExtensionLoader {
'success',
),
);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.enableExtension(newExtensionConfig.name, SettingScope.User);
}
} finally {
+1
View File
@@ -799,6 +799,7 @@ export function migrateDeprecatedSettings(
`Migrating deprecated extensions.disabled settings from ${scope} settings...`,
);
for (const extension of settings.extensions.disabled ?? []) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
extensionManager.disableExtension(extension, scope);
}
+8
View File
@@ -294,6 +294,7 @@ export const AppContainer = (props: AppContainerProps) => {
const staticExtraHeight = 3;
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
(async () => {
// Note: the program will not work if this fails so let errors be
// handled by the global catch.
@@ -384,6 +385,7 @@ export const AppContainer = (props: AppContainerProps) => {
// Initialize input history from logger (past sessions)
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
initializeFromLogger(logger);
}, [logger, initializeFromLogger]);
@@ -984,6 +986,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
const currentIde = ideClient.getCurrentIde();
setCurrentIDE(currentIde || null);
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
getIde();
}, []);
const shouldShowIdePrompt = Boolean(
@@ -1109,6 +1112,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
recordExitFail(config);
}
if (ctrlCPressCount > 1) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
handleSlashCommand('/quit', undefined, undefined, false);
} else {
ctrlCTimerRef.current = setTimeout(() => {
@@ -1127,6 +1131,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
recordExitFail(config);
}
if (ctrlDPressCount > 1) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
handleSlashCommand('/quit', undefined, undefined, false);
} else {
ctrlDTimerRef.current = setTimeout(() => {
@@ -1143,6 +1148,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
const handleIdePromptComplete = useCallback(
(result: IdeIntegrationNudgeResult) => {
if (result.userSelection === 'yes') {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
handleSlashCommand('/ide install');
settings.setValue(
SettingScope.User,
@@ -1225,6 +1231,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
config.getIdeMode() &&
ideContextState
) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
handleSlashCommand('/ide status');
} else if (
keyMatchers[Command.SHOW_MORE_LINES](key) &&
@@ -1425,6 +1432,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
}
}
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
fetchBannerTexts();
return () => {
+2
View File
@@ -154,6 +154,7 @@ export function AuthDialog({
if (error) {
onAuthError(error);
} else {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
onSelect(authMethod, SettingScope.User);
}
};
@@ -173,6 +174,7 @@ export function AuthDialog({
);
return;
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
onSelect(undefined, SettingScope.User);
}
},
+2
View File
@@ -64,11 +64,13 @@ export const useAuthCommand = (settings: LoadedSettings, config: Config) => {
useEffect(() => {
if (authState === AuthState.AwaitingApiKeyInput) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
reloadApiKey();
}
}, [authState, reloadApiKey]);
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
(async () => {
if (authState !== AuthState.Unauthenticated) {
return;
@@ -72,6 +72,7 @@ describe('directoryCommand', () => {
describe('show', () => {
it('should display the list of directories', () => {
if (!showCommand?.action) throw new Error('No action');
// eslint-disable-next-line @typescript-eslint/no-floating-promises
showCommand.action(mockContext, '');
expect(mockWorkspaceContext.getDirectories).toHaveBeenCalled();
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
@@ -101,6 +102,7 @@ describe('directoryCommand', () => {
it('should show an error if no path is provided', () => {
if (!addCommand?.action) throw new Error('No action');
// eslint-disable-next-line @typescript-eslint/no-floating-promises
addCommand.action(mockContext, '');
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
@@ -92,6 +92,7 @@ function updateAction(context: CommandContext, args: string): Promise<void> {
extensions,
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
updateComplete.then((updateInfos) => {
if (updateInfos.length === 0) {
context.ui.addItem(
@@ -30,6 +30,7 @@ describe('statsCommand', () => {
it('should display general session stats when run with no subcommand', () => {
if (!statsCommand.action) throw new Error('Command has no action');
// eslint-disable-next-line @typescript-eslint/no-floating-promises
statsCommand.action(mockContext, '');
const expectedDuration = formatDuration(
@@ -50,6 +51,7 @@ describe('statsCommand', () => {
);
if (!modelSubCommand?.action) throw new Error('Subcommand has no action');
// eslint-disable-next-line @typescript-eslint/no-floating-promises
modelSubCommand.action(mockContext, '');
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
@@ -66,6 +68,7 @@ describe('statsCommand', () => {
);
if (!toolsSubCommand?.action) throw new Error('Subcommand has no action');
// eslint-disable-next-line @typescript-eslint/no-floating-promises
toolsSubCommand.action(mockContext, '');
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
@@ -41,6 +41,7 @@ export const FolderTrustDialog: React.FC<FolderTrustDialogProps> = ({
}, 250);
}
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
doRelaunch();
}, [isRestarting]);
@@ -18,6 +18,7 @@ export const IdeTrustChangeDialog = ({ reason }: IdeTrustChangeDialogProps) => {
useKeypress(
(key) => {
if (key.name === 'r' || key.name === 'R') {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
relaunchApp();
}
},
@@ -373,6 +373,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
useMouse(
(event: MouseEvent) => {
if (event.name === 'right-release') {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
handleClipboardPaste();
}
},
@@ -779,12 +780,14 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
// External editor
if (keyMatchers[Command.OPEN_EXTERNAL_EDITOR](key)) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
buffer.openInExternalEditor();
return;
}
// Ctrl+V for clipboard paste
if (keyMatchers[Command.PASTE_CLIPBOARD](key)) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
handleClipboardPaste();
return;
}
@@ -66,6 +66,7 @@ export const MultiFolderTrustDialog: React.FC<MultiFolderTrustDialogProps> = ({
useKeypress(
(key) => {
if (key.name === 'escape') {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
handleCancel();
}
},
@@ -49,6 +49,7 @@ export const Notifications = () => {
};
if (isScreenReaderEnabled) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
checkScreenReader();
}
}, [isScreenReaderEnabled]);
@@ -69,6 +70,7 @@ export const Notifications = () => {
}
}
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
writeScreenReaderNudgeFile();
}, [showScreenReaderNudge]);
@@ -70,6 +70,7 @@ export function PermissionsModifyTrustDialog({
if (needsRestart && key.name === 'r') {
const success = commitTrustLevelChange();
if (success) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
relaunchApp();
} else {
onExit();
@@ -665,6 +665,7 @@ const useLoadSessions = (config: Config, state: SessionBrowserState) => {
}
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
loadSessions();
}, [config, setSessions, setLoading, setError]);
@@ -693,6 +694,7 @@ const useLoadSessions = (config: Config, state: SessionBrowserState) => {
}
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
loadFullContent();
}, [
isSearchMode,
@@ -139,6 +139,7 @@ export function SettingsDialog({
setScrollOffset(0);
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
doSearch();
return () => {
@@ -57,6 +57,7 @@ export const ToolConfirmationMessage: React.FC<
setIsDiffingEnabled(client?.isDiffingEnabled() ?? false);
}
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
getIdeClient();
}
return () => {
@@ -75,6 +76,7 @@ export const ToolConfirmationMessage: React.FC<
);
}
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
onConfirm(outcome);
};
@@ -84,6 +86,7 @@ export const ToolConfirmationMessage: React.FC<
(key) => {
if (!isFocused) return;
if (key.name === 'escape' || (key.ctrl && key.name === 'c')) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
handleConfirm(ToolConfirmationOutcome.Cancel);
}
},
@@ -645,12 +645,14 @@ describe('KeypressContext', () => {
expect(keyHandler).not.toHaveBeenCalled();
// Advance time just before timeout
// eslint-disable-next-line @typescript-eslint/no-floating-promises
act(() => vi.advanceTimersByTime(ESC_TIMEOUT - 5));
// Still shouldn't broadcast
expect(keyHandler).not.toHaveBeenCalled();
// Advance past timeout
// eslint-disable-next-line @typescript-eslint/no-floating-promises
act(() => vi.advanceTimersByTime(10));
// Should now broadcast the incomplete sequence as regular input
@@ -789,12 +791,14 @@ describe('KeypressContext', () => {
act(() => stdin.write('\x1b[97;13'));
// Advance time partway
// eslint-disable-next-line @typescript-eslint/no-floating-promises
act(() => vi.advanceTimersByTime(30));
// Add more to sequence
act(() => stdin.write('5'));
// Advance time from the first timeout point
// eslint-disable-next-line @typescript-eslint/no-floating-promises
act(() => vi.advanceTimersByTime(25));
// Should not have timed out yet (timeout restarted)
@@ -878,6 +882,7 @@ describe('KeypressContext', () => {
act(() => stdin.write('\x1b[<'));
// Advance time past the normal kitty timeout (50ms)
// eslint-disable-next-line @typescript-eslint/no-floating-promises
act(() => vi.advanceTimersByTime(ESC_TIMEOUT + 10));
// Send the rest
@@ -930,6 +935,8 @@ describe('KeypressContext', () => {
for (const char of sequence) {
act(() => stdin.write(char));
// eslint-disable-next-line @typescript-eslint/no-floating-promises
act(() => vi.advanceTimersByTime(0));
}
@@ -40,6 +40,7 @@ function addShellCommandToGeminiHistory(
? resultText.substring(0, MAX_OUTPUT_LENGTH) + '\n... (truncated)'
: resultText;
// eslint-disable-next-line @typescript-eslint/no-floating-promises
geminiClient.addHistory({
role: 'user',
parts: [
@@ -350,6 +351,7 @@ export const useShellCommandProcessor = (
};
const execPromise = new Promise<void>((resolve) => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
executeCommand(resolve);
});
@@ -258,6 +258,7 @@ export const useSlashCommandProcessor = (
reloadCommands();
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
(async () => {
const ideClient = await IdeClient.getInstance();
ideClient.addStatusChangeListener(listener);
@@ -278,6 +279,7 @@ export const useSlashCommandProcessor = (
appEvents.on('extensionsStopping', extensionEventListener);
return () => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
(async () => {
const ideClient = await IdeClient.getInstance();
ideClient.removeStatusChangeListener(listener);
@@ -290,6 +292,7 @@ export const useSlashCommandProcessor = (
useEffect(() => {
const controller = new AbortController();
// eslint-disable-next-line @typescript-eslint/no-floating-promises
(async () => {
const commandService = await CommandService.create(
[
@@ -223,8 +223,10 @@ export function useAtCompletion(props: UseAtCompletionProps): void {
};
if (state.status === AtCompletionStatus.INITIALIZING) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
initialize();
} else if (state.status === AtCompletionStatus.SEARCHING) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
search();
}
@@ -98,6 +98,7 @@ export const useExtensionUpdates = (
return !currentState || currentState === ExtensionUpdateState.UNKNOWN;
});
if (extensionsToCheck.length === 0) return;
// eslint-disable-next-line @typescript-eslint/no-floating-promises
checkForAllExtensionUpdates(
extensionsToCheck,
extensionManager,
@@ -200,6 +201,7 @@ export const useExtensionUpdates = (
);
}
if (scheduledUpdate) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
Promise.all(updatePromises).then((results) => {
const nonNullResults = results.filter((result) => result != null);
scheduledUpdate.onCompleteCallbacks.forEach((callback) => {
@@ -977,6 +977,7 @@ describe('useGeminiStream', () => {
// Start a query
await act(async () => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
result.current.submitQuery('test query');
});
@@ -1036,6 +1037,7 @@ describe('useGeminiStream', () => {
// Start a query
await act(async () => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
result.current.submitQuery('test query');
});
@@ -1076,6 +1078,7 @@ describe('useGeminiStream', () => {
// Start a query
await act(async () => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
result.current.submitQuery('test query');
});
@@ -1117,6 +1120,7 @@ describe('useGeminiStream', () => {
const { result } = renderTestHook();
await act(async () => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
result.current.submitQuery('long running query');
});
@@ -993,6 +993,7 @@ export const useGeminiStream = (
);
if (lastQueryRef.current && lastPromptIdRef.current) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
submitQuery(
lastQueryRef.current,
{ isContinuation: true },
@@ -1176,6 +1177,7 @@ export const useGeminiStream = (
const combinedParts = geminiTools.flatMap(
(toolCall) => toolCall.response.responseParts,
);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
geminiClient.addHistory({
role: 'user',
parts: combinedParts,
@@ -1207,6 +1209,7 @@ export const useGeminiStream = (
return;
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
submitQuery(
responsesToSend,
{
@@ -1342,6 +1345,7 @@ export const useGeminiStream = (
}
}
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
saveRestorableToolCalls();
}, [
toolCalls,
@@ -37,6 +37,7 @@ export function useGitBranchName(cwd: string): string | undefined {
}, [cwd, setBranchName]);
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
fetchBranchName(); // Initial fetch
const gitLogsHeadPath = path.join(cwd, '.git', 'logs', 'HEAD');
@@ -52,6 +53,7 @@ export function useGitBranchName(cwd: string): string | undefined {
// Changes to .git/logs/HEAD (appends) indicate HEAD has likely changed
if (eventType === 'change' || eventType === 'rename') {
// Handle rename just in case
// eslint-disable-next-line @typescript-eslint/no-floating-promises
fetchBranchName();
}
});
@@ -62,6 +64,7 @@ export function useGitBranchName(cwd: string): string | undefined {
}
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
setupWatcher();
return () => {
@@ -49,6 +49,7 @@ export function useIdeTrustListener() {
onStoreChange();
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
(async () => {
const ideClient = await IdeClient.getInstance();
ideClient.addTrustChangeListener(handleTrustChange);
@@ -56,6 +57,7 @@ export function useIdeTrustListener() {
setConnectionStatus(ideClient.getConnectionStatus().status);
})();
return () => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
(async () => {
const ideClient = await IdeClient.getInstance();
ideClient.removeTrustChangeListener(handleTrustChange);
@@ -87,6 +87,7 @@ export function useIncludeDirsTrust(
}
if (added.length > 0 || errors.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
finishAddingDirectories(config, addItem, added, errors);
}
config.clearPendingIncludeDirectories();
@@ -151,6 +152,7 @@ export function useIncludeDirsTrust(
/>,
);
} else if (added.length > 0 || errors.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
finishAddingDirectories(config, addItem, added, errors);
config.clearPendingIncludeDirectories();
}
@@ -23,6 +23,7 @@ describe('useLoadingIndicator', () => {
afterEach(() => {
vi.useRealTimers(); // Restore real timers after each test
// eslint-disable-next-line @typescript-eslint/no-floating-promises
act(() => vi.runOnlyPendingTimers);
vi.restoreAllMocks();
});
@@ -58,6 +58,7 @@ export const usePrivacySettings = (config: Config) => {
});
}
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
fetchInitialState();
}, [config]);
@@ -181,6 +181,7 @@ export function usePromptCompletion({
lastSelectedTextRef.current = '';
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
generatePromptSuggestions();
}, [
buffer.text,
@@ -584,11 +584,13 @@ describe('useSelectionList', () => {
});
pressNumber('0');
// eslint-disable-next-line @typescript-eslint/no-floating-promises
act(() => vi.advanceTimersByTime(1000)); // Timeout the '0' input
pressNumber('1');
expect(mockOnSelect).not.toHaveBeenCalled(); // Should be waiting for second digit
// eslint-disable-next-line @typescript-eslint/no-floating-promises
act(() => vi.advanceTimersByTime(1000)); // Timeout '1'
expect(mockOnSelect).toHaveBeenCalledWith('Item 1');
});
@@ -64,6 +64,7 @@ export function useSessionResume({
refreshStaticRef.current(); // Force Static component to re-render with the updated history.
// Give the history to the Gemini client.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
config.getGeminiClient()?.resumeChat(clientHistory, resumedData);
},
[config, isGeminiClientInitialized, setQuittingMessages],
@@ -84,6 +84,7 @@ export function useShellHistory(
const loadedHistory = await readHistoryFile(filePath);
setHistory(loadedHistory.reverse()); // Newest first
}
// eslint-disable-next-line @typescript-eslint/no-floating-promises
loadHistory();
}, [projectRoot, storage]);
@@ -97,6 +98,7 @@ export function useShellHistory(
.filter(Boolean);
setHistory(newHistory);
// Write to file in reverse order (oldest first)
// eslint-disable-next-line @typescript-eslint/no-floating-promises
writeHistoryFile(historyFilePath, [...newHistory].reverse());
setHistoryIndex(-1);
},
@@ -232,6 +232,7 @@ function useCommandSuggestions(
}
}
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
fetchAndSetSuggestions();
return () => abortController.abort();
}
@@ -112,6 +112,7 @@ export const CloudFreePrivacyNotice = ({
items={items}
initialIndex={privacyState.dataCollectionOptIn ? 0 : 1}
onSelect={(value) => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
updateDataCollectionOptIn(value);
// Only exit if there was no error.
if (!privacyState.error) {
@@ -105,6 +105,7 @@ export class Connection {
this.#handler = handler;
this.#peerInput = peerInput;
this.#textEncoder = new TextEncoder();
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.#receive(peerOutput);
}
@@ -121,6 +122,7 @@ export class Connection {
if (trimmedLine) {
const message = JSON.parse(trimmedLine);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.#processMessage(message);
}
}
@@ -289,6 +289,7 @@ export class Session {
text: part.text,
};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.sendUpdate({
sessionUpdate: part.thought
? 'agent_thought_chunk'