RFR: 8348611: Eliminate DeferredLintHandler and emit warnings after attribution [v11]

Stephan Schroevers duke at openjdk.org
Sun Sep 21 15:05:35 UTC 2025


On Thu, 14 Aug 2025 04:00:12 GMT, Archie Cobbs <acobbs at openjdk.org> wrote:

>> This is a cleanup/refactoring of how lint warnings are logged and `@SuppressWarnings` annotations applied.
>> 
>> A central challenge with lint warnings is that warnings can be generated during any compiler phase, but whether a particular lint warning is suppressed via `@SuppressWarnings` can't be known until after attribution. For example, the parser doesn't have enough information to interpret and apply `@SuppressWarnings("text-blocks")` to text blocks, or `@SuppressWarnings("preview")` to preview lexical features; instead, the `DeferredLintHandler` is used to workaround this limitation.
>> 
>> In addition, several other factors complicate things:
>> * Knowing the current applicable `Lint` instance requires manually tracking it with each declaration visited and applying/removing the `@SuppressWarnings` annotation there, if any
>> * Some warnings are "suppressibly mandatory" (i.e., they are _emitted if not suppressed_ instead of _emitted if enabled_)
>> * Some warnings are "unsuppressibly mandatory" (e.g., the "internal proprietary API" warning)
>> * Some mandatory warnings are _aggregated_ into notes that are emitted at the end of compilation when not enabled
>> * Some warnings are _lint_ warnings, with a corresponding lint category, while others are just "plain" warnings
>> * Some lint warnings are suppressible via `@SuppressWarnings`, while others are only suppressible via `-Xlint:-foo` flags
>> * Speculative compilation requires holding log messages in purgatory until the speculation resolves, after which they are then either discarded or emitted. But this creates a tricky interaction with `DeferredLintHandler` because even after speculation is complete, we may still not yet know whether a warning should be suppressed.
>> 
>> Previously the logic to get all of this right was non-obviously woven around the code base. In particular, you needed to know somehow whether or not to use `DeferredLintHandler`, and in what "mode".
>> 
>> The overall goal of this PR is to simplify usage so that **no matter where you are in the compiler, you can just invoke `log.warning()` to log a warning** and (mostly) forget about all of the details listed above.
>
> Archie Cobbs has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Remove unused field from "LintRange" record.

Alright, run the following script in an empty directory to create a `pom.xml` and `src/main/java/com/example/Reproducer.java`:

#!/usr/bin/env bash

set -euo pipefail

# Creates files in the current directory to reproduce the issue.
# This script writes `pom.xml` and `src/main/java/com/example/Reproducer.java`.

mkdir -p src/main/java/com/example

cat > pom.xml <<'POM_EOF'
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>reproducer</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.google.errorprone</groupId>
                <artifactId>error_prone_core</artifactId>
                <version>2.42.0</version>
            </dependency>
            <dependency>
                <groupId>javax.annotation</groupId>
                <artifactId>javax.annotation-api</artifactId>
                <version>1.3.2</version>
            </dependency>
            <dependency>
                <groupId>org.assertj</groupId>
                <artifactId>assertj-bom</artifactId>
                <version>3.27.4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.openrewrite.recipe</groupId>
                <artifactId>rewrite-recipe-bom</artifactId>
                <version>3.14.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>com.google.errorprone</groupId>
            <artifactId>error_prone_core</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.openrewrite</groupId>
            <artifactId>rewrite-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.openrewrite</groupId>
            <artifactId>rewrite-templating</artifactId>
        </dependency>
    </dependencies>

    <build>
        <pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.14.0</version>
					<configuration>
						<compilerArgs>
							<arg>-Xlint:all</arg>
						</compilerArgs>
						<parameters>true</parameters>
						<release>17</release>
						<showWarnings>true</showWarnings>
						<failOnWarning>true</failOnWarning>
					</configuration>
				</plugin>
			</plugins>
        </pluginManagement>
    </build>

    <profiles>
        <profile>
            <id>break-it</id>
    		<build>
				<pluginManagement>
					<plugins>
						<plugin>
							<groupId>org.apache.maven.plugins</groupId>
							<artifactId>maven-compiler-plugin</artifactId>
							<configuration>
								<annotationProcessorPaths>
									<path>
										<groupId>org.openrewrite</groupId>
										<artifactId>rewrite-templating</artifactId>
									</path>
								</annotationProcessorPaths>
							</configuration>
						</plugin>
					</plugins>
				</pluginManagement>
    		</build>
        </profile>
    </profiles>
</project>
POM_EOF

cat > src/main/java/com/example/Reproducer.java <<'JAVA_EOF'
package com.example;

import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import org.assertj.core.api.AbstractThrowableAssert;

final class Reproducer {
  // XXX: This rule changes the `Throwable` against which subsequent assertions are made.
  static final class AbstractThrowableAssertCauseIsSameAs {
    @BeforeTemplate
    @SuppressWarnings("deprecation" /* This deprecated API will be rewritten. */)
    AbstractThrowableAssert<?, ? extends Throwable> before(
        AbstractThrowableAssert<?, ? extends Throwable> throwableAssert, Throwable expected) {
      return throwableAssert.hasCauseReference(expected);
    }

    @AfterTemplate
    AbstractThrowableAssert<?, ? extends Throwable> after(
        AbstractThrowableAssert<?, ? extends Throwable> throwableAssert, Throwable expected) {
      return throwableAssert.cause().isSameAs(expected);
    }
  }
}
JAVA_EOF


With JDK 25, the build passes when running `mvn clean compile`, but fails when running `mvn clean compile -Pbreak-it`:

...
[INFO] --- compiler:3.14.0:compile (default-compile) @ reproducer ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 1 source file with javac [debug parameters release 17] to target/classes
[INFO] -------------------------------------------------------------
[WARNING] COMPILATION WARNING : 
[INFO] -------------------------------------------------------------
[WARNING] /tmp/_/src/main/java/com/example/Reproducer.java:[14,29] hasCauseReference(java.lang.Throwable) in org.assertj.core.api.AbstractThrowableAssert has been deprecated
[INFO] 1 warning
[INFO] -------------------------------------------------------------
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR : 
[INFO] -------------------------------------------------------------
[ERROR] warnings found and -Werror specified
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
...


With JDK 24 and below (and the latest JDK 26 EA build) the build correctly passes with both commands.

-------------

PR Comment: https://git.openjdk.org/jdk/pull/24584#issuecomment-3316051542


More information about the compiler-dev mailing list