From 14fb822ed682915e00c1e4f97bcfefca6a36ec9b Mon Sep 17 00:00:00 2001 From: slop Date: Mon, 13 Apr 2026 09:22:57 -0700 Subject: [PATCH] Avoid PSI resolution in Java file parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit annotation.qualifiedName triggers PsiJavaCodeReferenceElement.resolve() which goes through JavaPsiFacade → Kotlin JavaElementFinder → CliKotlinAsJavaSupport → CliTraceHolder.module — a lateinit property that is only set during compilation, not in srcx's parsing-only environment. This crashes every project containing Java source files. Use nameReferenceElement.referenceName instead, which reads the annotation name text without triggering type resolution. Also wrap parseJavaFile in runCatching so any remaining resolution-triggering operation logs a warning and skips the file instead of failing the entire project analysis. --- .../srcx/analysis/SourceFileMetadata.kt | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/zone/clanker/gradle/srcx/analysis/SourceFileMetadata.kt b/src/main/kotlin/zone/clanker/gradle/srcx/analysis/SourceFileMetadata.kt index 29acab6..3a6ff16 100644 --- a/src/main/kotlin/zone/clanker/gradle/srcx/analysis/SourceFileMetadata.kt +++ b/src/main/kotlin/zone/clanker/gradle/srcx/analysis/SourceFileMetadata.kt @@ -228,7 +228,7 @@ private fun resolveJavaTypeInfo(javaFile: PsiJavaFile, fallbackName: String): Ja val text = javaFile.text return JavaTypeInfo( className = cls.name ?: fallbackName, - annotations = cls.annotations.mapNotNull { it.qualifiedName?.substringAfterLast('.') }, + annotations = cls.annotations.mapNotNull { it.nameReferenceElement?.referenceName }, supertypes = superList, isInterface = cls.isInterface, isAbstract = cls.hasModifierProperty(PsiModifier.ABSTRACT), @@ -237,38 +237,46 @@ private fun resolveJavaTypeInfo(javaFile: PsiJavaFile, fallbackName: String): Ja ) } +private val javaParseLogger = + org.gradle.api.logging.Logging + .getLogger("zone.clanker.gradle.srcx.analysis.SourceFileMetadata") + private fun parseJavaFile(file: File, psiManager: PsiManager): SourceFileMetadata? { val vf = LightVirtualFile(file.name, JavaFileType.INSTANCE, file.readText()) val javaFile = psiManager.findFile(vf) as? PsiJavaFile ?: return null - val packageName = javaFile.packageName - val imports = - javaFile.importList - ?.importStatements - ?.mapNotNull { it.qualifiedName } - ?.filter { fqn -> PLATFORM_PREFIXES.none { fqn.startsWith(it) } } - ?: emptyList() + return runCatching { + val packageName = javaFile.packageName + val imports = + javaFile.importList + ?.importStatements + ?.mapNotNull { it.qualifiedName } + ?.filter { fqn -> PLATFORM_PREFIXES.none { fqn.startsWith(it) } } + ?: emptyList() - val info = resolveJavaTypeInfo(javaFile, file.nameWithoutExtension) - val qualifiedName = if (packageName.isNotEmpty()) "$packageName.${info.className}" else info.className + val info = resolveJavaTypeInfo(javaFile, file.nameWithoutExtension) + val qualifiedName = if (packageName.isNotEmpty()) "$packageName.${info.className}" else info.className - return SourceFileMetadata( - file = file, - packageName = packageName, - qualifiedName = qualifiedName, - simpleName = info.className, - imports = imports, - annotations = info.annotations, - supertypes = info.supertypes, - isInterface = info.isInterface, - isAbstract = info.isAbstract, - isObject = false, - isDataClass = false, - language = SourceFileMetadata.Language.JAVA, - lineCount = countCodeLines(file.readLines()), - methods = info.methods, - declarationLine = info.declarationLine, - ) + SourceFileMetadata( + file = file, + packageName = packageName, + qualifiedName = qualifiedName, + simpleName = info.className, + imports = imports, + annotations = info.annotations, + supertypes = info.supertypes, + isInterface = info.isInterface, + isAbstract = info.isAbstract, + isObject = false, + isDataClass = false, + language = SourceFileMetadata.Language.JAVA, + lineCount = countCodeLines(file.readLines()), + methods = info.methods, + declarationLine = info.declarationLine, + ) + }.onFailure { e -> + javaParseLogger.warn("srcx: Failed to parse Java file '${file.name}': ${e.message}", e) + }.getOrNull() } private fun countCodeLines(lines: List): Int {