From 87ea28260b00ffe95a4226033aeee4f96484a03c Mon Sep 17 00:00:00 2001 From: Daniel Chao Date: Thu, 21 May 2026 14:34:11 -0700 Subject: [PATCH] Configure IntelliJ to respect `@LateInit` annotations (#1606) IntelliJ can understand that some annotations on fields mean that they are implicitly initialized, which means we don't get the "field XXX is not initialized" warning for `@LateInit` fields. This setting, unfortunately, is recorded into `.idea/misc.xml`, which contains a bunch of arbitrary stuff that we don't want to check into source control This adds some logic to touch up that file to mark `@LateInit` as implicitly initialized fields, so we don't get any editor warnings. Also, suppress some warnings. --- .../kotlin/ConfigureLateInitAnnotation.kt | 76 +++++++++++++++++++ build.gradle.kts | 4 + .../src/main/java/org/pkl/core/TypeAlias.java | 2 + 3 files changed, 82 insertions(+) create mode 100644 build-logic/src/main/kotlin/ConfigureLateInitAnnotation.kt diff --git a/build-logic/src/main/kotlin/ConfigureLateInitAnnotation.kt b/build-logic/src/main/kotlin/ConfigureLateInitAnnotation.kt new file mode 100644 index 00000000..cbd90c1f --- /dev/null +++ b/build-logic/src/main/kotlin/ConfigureLateInitAnnotation.kt @@ -0,0 +1,76 @@ +/* + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import groovy.util.Node +import groovy.xml.XmlParser +import groovy.xml.XmlUtil +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction + +abstract class ConfigureLateInitAnnotation : DefaultTask() { + private val miscXmlFile = project.rootProject.file(".idea/misc.xml") + + init { + inputs.file(miscXmlFile) + outputs.file(miscXmlFile) + } + + @TaskAction + fun run() { + val annotationName = "org.pkl.core.util.LateInit" + + if (!miscXmlFile.exists()) { + miscXmlFile.writeText( + """ + + + + """ + .trimIndent() + .trim() + ) + } + + val root = XmlParser().parse(miscXmlFile) + + fun Node.childNodes() = children().filterIsInstance() + + var entryPointsManager = + root.childNodes().find { + it.name() == "component" && it.attribute("name") == "EntryPointsManager" + } + if (entryPointsManager == null) { + entryPointsManager = root.appendNode("component", mapOf("name" to "EntryPointsManager")) + } + + var writeAnnotations = entryPointsManager.childNodes().find { it.name() == "writeAnnotations" } + if (writeAnnotations == null) { + writeAnnotations = entryPointsManager.appendNode("writeAnnotations") + } + + val alreadyExists = + writeAnnotations.childNodes().any { + it.name() == "writeAnnotation" && it.attribute("name") == annotationName + } + + if (!alreadyExists) { + writeAnnotations.appendNode("writeAnnotation", mapOf("name" to annotationName)) + miscXmlFile.writeText(XmlUtil.serialize(root)) + logger.lifecycle("Updated .idea/misc.xml") + } else { + logger.info("$annotationName is already configured in .idea/misc.xml") + } + } +} diff --git a/build.gradle.kts b/build.gradle.kts index 52395855..1270141a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,6 +17,7 @@ import org.jetbrains.gradle.ext.ActionDelegationConfig import org.jetbrains.gradle.ext.ActionDelegationConfig.TestRunner.PLATFORM import org.jetbrains.gradle.ext.ProjectSettings +import org.jetbrains.gradle.ext.taskTriggers plugins { id("pklAllProjects") @@ -36,6 +37,8 @@ nexusPublishing { } } +val configureLateInitAnnotation by tasks.registering(ConfigureLateInitAnnotation::class) + idea { project { this as ExtensionAware @@ -45,6 +48,7 @@ idea { delegateBuildRunToGradle = true testRunner = PLATFORM } + taskTriggers.afterSync(configureLateInitAnnotation) } } } diff --git a/pkl-core/src/main/java/org/pkl/core/TypeAlias.java b/pkl-core/src/main/java/org/pkl/core/TypeAlias.java index 07653cc7..77f7a404 100644 --- a/pkl-core/src/main/java/org/pkl/core/TypeAlias.java +++ b/pkl-core/src/main/java/org/pkl/core/TypeAlias.java @@ -47,6 +47,7 @@ public final class TypeAlias extends Member implements Value { } public void initAliasedType(PType type) { + //noinspection ConstantValue assert aliasedType == null; aliasedType = type; } @@ -80,6 +81,7 @@ public final class TypeAlias extends Member implements Value { /** Returns the type that this type alias stands for. */ public PType getAliasedType() { + //noinspection ConstantValue assert aliasedType != null; return aliasedType; }