8344148: Add an explicit compiler phase for warning generation

Reviewed-by: vromero
This commit is contained in:
Archie Cobbs 2025-01-06 18:37:19 +00:00
parent 8d388ccd9e
commit 27646e5516
19 changed files with 219 additions and 30 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -58,11 +58,12 @@ public class CompileStates extends HashMap<Env<AttrContext>, CompileStates.Compi
PROCESS(3),
ATTR(4),
FLOW(5),
TRANSTYPES(6),
TRANSPATTERNS(7),
LOWER(8),
UNLAMBDA(9),
GENERATE(10);
WARN(6),
TRANSTYPES(7),
TRANSPATTERNS(8),
LOWER(9),
UNLAMBDA(10),
GENERATE(11);
CompileState(int value) {
this.value = value;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -229,7 +229,6 @@ public class Flow {
new AssignAnalyzer().analyzeTree(env, make);
new FlowAnalyzer().analyzeTree(env, make);
new CaptureAnalyzer().analyzeTree(env, make);
new ThisEscapeAnalyzer(names, syms, types, rs, log, lint).analyzeTree(env);
}
public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -58,6 +58,7 @@ import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List;
@ -140,8 +141,17 @@ import static com.sun.tools.javac.tree.JCTree.Tag.*;
* <li>We assume that native methods do not leak.
* <li>We don't try to follow {@code super()} invocations; that's for the superclass analysis to handle.
* </ul>
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
class ThisEscapeAnalyzer extends TreeScanner {
public class ThisEscapeAnalyzer extends TreeScanner {
protected static final Context.Key<ThisEscapeAnalyzer> contextKey = new Context.Key<>();
// Other singletons we utilize
private final Names names;
private final Symtab syms;
@ -211,15 +221,24 @@ class ThisEscapeAnalyzer extends TreeScanner {
*/
private RefSet<Ref> refs;
// Constructor
// Access
ThisEscapeAnalyzer(Names names, Symtab syms, Types types, Resolve rs, Log log, Lint lint) {
this.names = names;
this.syms = syms;
this.types = types;
this.rs = rs;
this.log = log;
this.lint = lint;
public static ThisEscapeAnalyzer instance(Context context) {
ThisEscapeAnalyzer instance = context.get(contextKey);
if (instance == null)
instance = new ThisEscapeAnalyzer(context);
return instance;
}
@SuppressWarnings("this-escape")
protected ThisEscapeAnalyzer(Context context) {
context.put(contextKey, this);
names = Names.instance(context);
log = Log.instance(context);
syms = Symtab.instance(context);
types = Types.instance(context);
rs = Resolve.instance(context);
lint = Lint.instance(context);
}
//
@ -227,6 +246,24 @@ class ThisEscapeAnalyzer extends TreeScanner {
//
public void analyzeTree(Env<AttrContext> env) {
try {
doAnalyzeTree(env);
} finally {
attrEnv = null;
methodMap.clear();
nonPublicOuters.clear();
targetClass = null;
warningList.clear();
methodClass = null;
callStack.clear();
invocations.clear();
pendingWarning = null;
depth = -1;
refs = null;
}
}
private void doAnalyzeTree(Env<AttrContext> env) {
// Sanity check
Assert.check(checkInvariants(false, false));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -1168,7 +1168,7 @@ public class TransTypes extends TreeTranslator {
private Env<AttrContext> env;
private static final String statePreviousToFlowAssertMsg =
"The current compile state [%s] of class %s is previous to FLOW";
"The current compile state [%s] of class %s is previous to WARN";
void translateClass(ClassSymbol c) {
Type st = types.supertype(c.type);
@ -1189,7 +1189,7 @@ public class TransTypes extends TreeTranslator {
* 1) has no compile state being it the most outer class.
* We accept this condition for inner classes.
*
* 2) has a compile state which is previous to Flow state.
* 2) has a compile state which is previous to WARN state.
*/
boolean envHasCompState = compileStates.get(myEnv) != null;
if (!envHasCompState && c.outermostClass() == c) {
@ -1197,7 +1197,7 @@ public class TransTypes extends TreeTranslator {
}
if (envHasCompState &&
CompileState.FLOW.isAfter(compileStates.get(myEnv))) {
CompileState.WARN.isAfter(compileStates.get(myEnv))) {
Assert.error(String.format(statePreviousToFlowAssertMsg,
compileStates.get(myEnv), myEnv.enclClass.sym));
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.javac.comp;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Log;
/** This pass checks for various things to warn about.
* It runs after attribution and flow analysis.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class WarningAnalyzer {
protected static final Context.Key<WarningAnalyzer> contextKey = new Context.Key<>();
private final Log log;
private final ThisEscapeAnalyzer thisEscapeAnalyzer;
public static WarningAnalyzer instance(Context context) {
WarningAnalyzer instance = context.get(contextKey);
if (instance == null)
instance = new WarningAnalyzer(context);
return instance;
}
@SuppressWarnings("this-escape")
protected WarningAnalyzer(Context context) {
context.put(contextKey, this);
log = Log.instance(context);
thisEscapeAnalyzer = ThisEscapeAnalyzer.instance(context);
}
public void analyzeTree(Env<AttrContext> env) {
thisEscapeAnalyzer.analyzeTree(env);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -306,6 +306,10 @@ public class JavaCompiler {
*/
protected Flow flow;
/** The warning analyzer.
*/
protected WarningAnalyzer warningAnalyzer;
/** The modules visitor
*/
protected Modules modules;
@ -419,6 +423,7 @@ public class JavaCompiler {
chk = Check.instance(context);
gen = Gen.instance(context);
flow = Flow.instance(context);
warningAnalyzer = WarningAnalyzer.instance(context);
transTypes = TransTypes.instance(context);
lower = Lower.instance(context);
annotate = Annotate.instance(context);
@ -962,20 +967,20 @@ public class JavaCompiler {
if (!CompileState.ATTR.isAfter(shouldStopPolicyIfNoError)) {
switch (compilePolicy) {
case SIMPLE:
generate(desugar(flow(attribute(todo))));
generate(desugar(warn(flow(attribute(todo)))));
break;
case BY_FILE: {
Queue<Queue<Env<AttrContext>>> q = todo.groupByFile();
while (!q.isEmpty() && !shouldStop(CompileState.ATTR)) {
generate(desugar(flow(attribute(q.remove()))));
generate(desugar(warn(flow(attribute(q.remove())))));
}
}
break;
case BY_TODO:
while (!todo.isEmpty())
generate(desugar(flow(attribute(todo.remove()))));
generate(desugar(warn(flow(attribute(todo.remove())))));
break;
default:
@ -1435,6 +1440,56 @@ public class JavaCompiler {
}
}
/**
* Check for various things to warn about.
*
* @return the list of attributed parse trees
*/
public Queue<Env<AttrContext>> warn(Queue<Env<AttrContext>> envs) {
ListBuffer<Env<AttrContext>> results = new ListBuffer<>();
for (Env<AttrContext> env: envs) {
warn(env, results);
}
return stopIfError(CompileState.WARN, results);
}
/**
* Check for various things to warn about in an attributed parse tree.
*/
public Queue<Env<AttrContext>> warn(Env<AttrContext> env) {
ListBuffer<Env<AttrContext>> results = new ListBuffer<>();
warn(env, results);
return stopIfError(CompileState.WARN, results);
}
/**
* Check for various things to warn about in an attributed parse tree.
*/
protected void warn(Env<AttrContext> env, Queue<Env<AttrContext>> results) {
if (compileStates.isDone(env, CompileState.WARN)) {
results.add(env);
return;
}
if (shouldStop(CompileState.WARN))
return;
if (verboseCompilePolicy)
printNote("[warn " + env.enclClass.sym + "]");
JavaFileObject prev = log.useSource(
env.enclClass.sym.sourcefile != null ?
env.enclClass.sym.sourcefile :
env.toplevel.sourcefile);
try {
warningAnalyzer.analyzeTree(env);
compileStates.put(env, CompileState.WARN);
results.add(env);
}
finally {
log.useSource(prev);
}
}
private TaskEvent newAnalyzeTaskEvent(Env<AttrContext> env) {
JCCompilationUnit toplevel = env.toplevel;
ClassSymbol sym;
@ -1493,6 +1548,10 @@ public class JavaCompiler {
return;
}
// Ensure the file has reached the WARN state
if (!compileStates.isDone(env, CompileState.WARN))
warn(env);
/**
* Ensure that superclasses of C are desugared before C itself. This is
* required for two reasons: (i) as erasure (TransTypes) destroys
@ -1576,8 +1635,8 @@ public class JavaCompiler {
ScanNested scanner = new ScanNested();
scanner.scan(env.tree);
for (Env<AttrContext> dep: scanner.dependencies) {
if (!compileStates.isDone(dep, CompileState.FLOW))
desugaredEnvs.put(dep, desugar(flow(attribute(dep))));
if (!compileStates.isDone(dep, CompileState.WARN))
desugaredEnvs.put(dep, desugar(warn(flow(attribute(dep)))));
}
//We need to check for error another time as more classes might

View File

@ -1,9 +1,12 @@
[attribute Y]
[flow Y]
[warn Y]
[attribute W]
[flow W]
[warn W]
[attribute Z]
[flow Z]
[warn Z]
[desugar Z]
[desugar W]
[desugar Y]

View File

@ -1,7 +1,9 @@
[attribute A]
[flow A]
[warn A]
[attribute B]
[flow B]
[warn B]
[desugar B]
[desugar A]
[generate code A]

View File

@ -1,7 +1,9 @@
[attribute Y]
[flow Y]
[warn Y]
[attribute W]
[flow W]
[warn W]
[attribute Z]
[flow Z]
T6734819c.java:15:11: compiler.err.unreachable.stmt

View File

@ -1,4 +1,5 @@
[attribute Explicit]
[flow Explicit]
[warn Explicit]
[desugar Explicit]
[generate code Explicit]

View File

@ -4,6 +4,9 @@
[flow A]
[flow A1]
[flow A2]
[warn A]
[warn A1]
[warn A2]
[desugar A]
[desugar A1]
[desugar A2]

View File

@ -4,6 +4,9 @@
[flow A]
[flow A1]
[flow A2]
[warn A]
[warn A1]
[warn A2]
[desugar A]
[desugar A1]
[desugar A2]

View File

@ -1,17 +1,21 @@
[attribute A]
[flow A]
[warn A]
[desugar A]
[generate code A]
[attribute A1]
[flow A1]
[warn A1]
[desugar A1]
[generate code A1]
[attribute A2]
[flow A2]
[warn A2]
[desugar A2]
[generate code A2]
[attribute B]
[flow B]
[warn B]
[desugar B]
[generate code B]
[attribute B1]

View File

@ -1,17 +1,21 @@
[attribute A]
[flow A]
[warn A]
[desugar A]
[generate code A]
[attribute A1]
[flow A1]
[warn A1]
[desugar A1]
[generate code A1]
[attribute A2]
[flow A2]
[warn A2]
[desugar A2]
[generate code A2]
[attribute C]
[flow C]
[warn C]
[desugar C]
[generate code C]
[attribute C1]

View File

@ -1,7 +1,9 @@
[attribute A]
[flow A]
[warn A]
[attribute B]
[flow B]
[warn B]
[desugar B]
[desugar A]
[generate code A.A1]

View File

@ -1,10 +1,12 @@
[attribute B]
[flow B]
[warn B]
[desugar B]
[generate code B.Inner]
[generate code B]
[attribute A]
[flow A]
[warn A]
[desugar A]
[generate code A.A1]
[generate code A.A2]

View File

@ -1,7 +1,9 @@
[attribute A]
[flow A]
[warn A]
[attribute B]
[flow B]
[warn B]
[desugar B]
[desugar A]
[generate code A.A1]

View File

@ -1,10 +1,12 @@
[attribute B]
[flow B]
[warn B]
[desugar B]
[generate code B.Inner]
[generate code B]
[attribute A]
[flow A]
[warn A]
[desugar A]
[generate code A.A1]
[generate code A.A2]

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -115,7 +115,7 @@ public class AttrRecovery extends TestRunner {
Path curPath = Path.of(".");
List<String> actual = new JavacTask(tb)
.options("-XDrawDiagnostics", "-XDdev",
"-XDshould-stop.at=FLOW", "-Xlint:this-escape")
"-XDshould-stop.at=WARN", "-Xlint:this-escape")
.sources(code)
.outdir(curPath)
.run(Expect.FAIL)