From d5042e8d624e87838c22fb2bdf54935399e7dc16 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Wed, 14 Jan 2026 15:41:07 +0100 Subject: [PATCH] LSP-407 ContractInferenceIndexKt: extract contract inference gist to a service class GitOrigin-RevId: e064a340a5fc3576d7fa482d9c68ff545bfae68a --- .../inference/ContractInferenceGist.kt | 38 +++++++++++++++++++ .../inference/ContractInferenceIndex.kt | 10 ++--- 2 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inference/ContractInferenceGist.kt diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inference/ContractInferenceGist.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inference/ContractInferenceGist.kt new file mode 100644 index 000000000000..51b71d4ac911 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inference/ContractInferenceGist.kt @@ -0,0 +1,38 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.codeInspection.dataFlow.inference + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.Service +import com.intellij.util.gist.GistManager +import com.intellij.util.gist.PsiFileGist + +private const val VERSION = 14 + +/** + * Holds the [PsiFileGist] for caching contract inference results. + *

+ * This is a separate service to avoid accessing [GistManager] during static class initialization of [InferenceVisitor], + * which violates platform rules (class initialization must not depend on services). + * See LSP-407 to learn more. + */ +@Service +internal class ContractInferenceGist { + val gist: PsiFileGist> get() = _gist + private val _gist: PsiFileGist> + + constructor() { + _gist = GistManager.getInstance().newPsiFileGist( + /* id = */ "contractInference", + /* version = */ VERSION, + /* externalizer = */ MethodDataExternalizer, + /* calcData = */ { psiFile -> indexFile(psiFile.node.lighterAST) }, + ) + } + + companion object { + @JvmStatic + fun getInstance(): ContractInferenceGist { + return ApplicationManager.getApplication().getService(ContractInferenceGist::class.java) + } + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inference/ContractInferenceIndex.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inference/ContractInferenceIndex.kt index 38acc991fb4e..ccb1fc8c62df 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inference/ContractInferenceIndex.kt +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inference/ContractInferenceIndex.kt @@ -23,17 +23,13 @@ import com.intellij.psi.util.CachedValuesManager import com.intellij.util.gist.GistManager import java.util.* -private val gist = GistManager.getInstance().newPsiFileGist("contractInference", 14, MethodDataExternalizer) { file -> - indexFile(file.node.lighterAST) -} - -private fun indexFile(tree: LighterAST): Map { +internal fun indexFile(tree: LighterAST): Map { val visitor = InferenceVisitor(tree) visitor.visitNode(tree.root) return visitor.result } -internal data class ClassData( +private data class ClassData( val hasSuper: Boolean, val hasPureInitializer: Boolean, val isFinal: Boolean, @@ -206,6 +202,7 @@ public fun handleInconsistency(method: PsiMethodImpl, cachedData: MethodData, e: if (e is ProcessCanceledException) return e val file = method.containingFile + val gist = ContractInferenceGist.getInstance().gist val gistMap = gist.getFileData(file) GistManager.getInstance().invalidateData(file.viewProvider.virtualFile) @@ -233,6 +230,7 @@ public fun handleInconsistency(method: PsiMethodImpl, cachedData: MethodData, e: public fun getIndexedData(method: PsiMethodImpl): MethodData? { val file = method.containingFile val map = CachedValuesManager.getCachedValue(file) { + val gist = ContractInferenceGist.getInstance().gist CachedValueProvider.Result.create(bindMethods(gist.getFileData(file), file), file) } return map[method]