From 4fcc61269bf8071d532eb2a259ba44d3ecda826c Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Mon, 17 Feb 2025 20:38:43 -0600 Subject: [PATCH 01/44] Track source end positions of declarations that support @SuppressWarnings. --- .../sun/tools/javac/parser/JavacParser.java | 40 +++++- .../com/sun/tools/javac/tree/JCTree.java | 12 +- .../com/sun/tools/javac/tree/TreeInfo.java | 49 +++++-- .../javac/parser/DeclarationEndPositions.java | 123 ++++++++++++++++++ .../tools/javac/parser/JavacParserTest.java | 9 +- 5 files changed, 210 insertions(+), 23 deletions(-) create mode 100644 test/langtools/tools/javac/parser/DeclarationEndPositions.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index d0b9fccde4887..abb247922cc8d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -701,6 +701,27 @@ protected void setErrorEndPos(int errPos) { protected void storeEnd(JCTree tree, int endpos) { endPosTable.storeEnd(tree, endpos); + + // Module, package, class, method, and variable declarations remember their end positions + switch (tree.getTag()) { + case MODULEDEF: + ((JCModuleDecl)tree).endPos = endpos; + break; + case PACKAGEDEF: + ((JCPackageDecl)tree).endPos = endpos; + break; + case CLASSDEF: + ((JCClassDecl)tree).endPos = endpos; + break; + case METHODDEF: + ((JCMethodDecl)tree).endPos = endpos; + break; + case VARDEF: + ((JCVariableDecl)tree).endPos = endpos; + break; + default: + break; + } } protected T to(T t) { @@ -2761,6 +2782,7 @@ JCNewClass classCreatorRest(int newpos, List defs = classInterfaceOrRecordBody(names.empty, false, false); JCModifiers mods = F.at(Position.NOPOS).Modifiers(0); body = toP(F.at(pos).AnonymousClassDef(mods, defs)); + storeEnd(body, S.prevToken().endPos); } return toP(F.at(newpos).NewClass(encl, typeArgs, t, args, body)); } @@ -4018,6 +4040,7 @@ public JCTree.JCCompilationUnit parseCompilationUnit() { JCExpression pid = qualident(false); accept(SEMI); JCPackageDecl pd = toP(F.at(packagePos).PackageDecl(annotations, pid)); + storeEnd(pd, S.prevToken().endPos); attach(pd, firstToken.docComment()); consumedToplevelDoc = true; defs.append(pd); @@ -4127,7 +4150,7 @@ public JCTree.JCCompilationUnit parseCompilationUnit() { firstTypeDecl = false; } } - List topLevelDefs = isImplicitClass ? constructImplicitClass(defs.toList()) : defs.toList(); + List topLevelDefs = isImplicitClass ? constructImplicitClass(defs.toList(), S.prevToken().endPos) : defs.toList(); JCTree.JCCompilationUnit toplevel = F.at(firstToken.pos).TopLevel(topLevelDefs); if (!consumedToplevelDoc) attach(toplevel, firstToken.docComment()); @@ -4143,7 +4166,7 @@ public JCTree.JCCompilationUnit parseCompilationUnit() { } // Restructure top level to be an implicitly declared class. - private List constructImplicitClass(List origDefs) { + private List constructImplicitClass(List origDefs, int endPos) { ListBuffer topDefs = new ListBuffer<>(); ListBuffer defs = new ListBuffer<>(); @@ -4173,6 +4196,7 @@ private List constructImplicitClass(List origDefs) { JCClassDecl implicit = F.at(primaryPos).ClassDef( implicitMods, name, List.nil(), null, List.nil(), List.nil(), defs.toList()); + storeEnd(implicit, endPos); topDefs.append(implicit); return topDefs.toList(); } @@ -4188,9 +4212,11 @@ JCModuleDecl moduleDecl(JCModifiers mods, ModuleKind kind, Comment dc) { accept(LBRACE); directives = moduleDirectiveList(); accept(RBRACE); + int endPos = S.prevToken().endPos; accept(EOF); JCModuleDecl result = toP(F.at(pos).ModuleDef(mods, kind, name, directives)); + storeEnd(result, endPos); attach(result, dc); return result; } @@ -4393,6 +4419,7 @@ protected JCClassDecl classDeclaration(JCModifiers mods, Comment dc) { List defs = classInterfaceOrRecordBody(name, false, false); JCClassDecl result = toP(F.at(pos).ClassDef( mods, name, typarams, extending, implementing, permitting, defs)); + storeEnd(result, S.prevToken().endPos); attach(result, dc); return result; } @@ -4416,6 +4443,7 @@ protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) { saveDanglingDocComments(dc); List defs = classInterfaceOrRecordBody(name, false, true); + int endPos = S.prevToken().endPos; java.util.List fields = new ArrayList<>(); for (JCVariableDecl field : headerFields) { fields.add(field); @@ -4441,6 +4469,7 @@ protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) { defs = defs.prepend(field); } JCClassDecl result = toP(F.at(pos).ClassDef(mods, name, typarams, null, implementing, defs)); + storeEnd(result, endPos); attach(result, dc); return result; } @@ -4481,6 +4510,7 @@ protected JCClassDecl interfaceDeclaration(JCModifiers mods, Comment dc) { defs = classInterfaceOrRecordBody(name, true, false); JCClassDecl result = toP(F.at(pos).ClassDef( mods, name, typarams, null, extending, permitting, defs)); + storeEnd(result, S.prevToken().endPos); attach(result, dc); return result; } @@ -4529,6 +4559,7 @@ protected JCClassDecl enumDeclaration(JCModifiers mods, Comment dc) { JCClassDecl result = toP(F.at(pos). ClassDef(mods, name, List.nil(), null, implementing, defs)); + storeEnd(result, S.prevToken().endPos); attach(result, dc); return result; } @@ -4667,10 +4698,12 @@ JCTree enumeratorDeclaration(Name enumName) { createPos = identPos; JCIdent ident = F.at(identPos).Ident(enumName); JCNewClass create = F.at(createPos).NewClass(null, typeArgs, ident, args, body); + int endPos = S.prevToken().endPos; if (createPos != identPos) - storeEnd(create, S.prevToken().endPos); + storeEnd(create, endPos); ident = F.at(identPos).Ident(enumName); JCTree result = toP(F.at(pos).VarDef(mods, name, ident, create)); + storeEnd(result, endPos); attach(result, dc); return result; } @@ -5100,6 +5133,7 @@ protected JCTree methodDeclaratorRest(int pos, toP(F.at(pos).MethodDef(mods, name, type, typarams, receiverParam, params, thrown, body, defaultValue)); + storeEnd(result, S.prevToken().endPos); attach(result, dc); return result; } finally { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index de86b7e2c5747..5d39a914b2d77 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -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 @@ -641,6 +641,8 @@ public static class JCPackageDecl extends JCTree implements PackageTree { /** The tree representing the package clause. */ public JCExpression pid; public PackageSymbol packge; + /** Position of closing semicolon, optional. */ + public int endPos = Position.NOPOS; public JCPackageDecl(List annotations, JCExpression pid) { this.annotations = annotations; this.pid = pid; @@ -837,6 +839,8 @@ public static class JCClassDecl extends JCStatement implements ClassTree { public List defs; /** the symbol */ public ClassSymbol sym; + /** position of closing brace, optional. */ + public int endPos = Position.NOPOS; protected JCClassDecl(JCModifiers mods, Name name, List typarams, @@ -931,6 +935,8 @@ public static class JCMethodDecl extends JCTree implements MethodTree { public MethodSymbol sym; /** does this method completes normally */ public boolean completesNormally; + /** position of closing brace or semicolon, optional. */ + public int endPos = Position.NOPOS; protected JCMethodDecl(JCModifiers mods, Name name, @@ -1016,6 +1022,8 @@ public static class JCVariableDecl extends JCStatement implements VariableTree { public VarSymbol sym; /** explicit start pos */ public int startPos = Position.NOPOS; + /** position of closing semicolon, optional. */ + public int endPos = Position.NOPOS; /** declared using `var` */ private boolean declaredUsingVar; @@ -3126,6 +3134,8 @@ public static class JCModuleDecl extends JCTree implements ModuleTree { public JCExpression qualId; public List directives; public ModuleSymbol sym; + /** position of closing brace, optional. */ + public int endPos = Position.NOPOS; protected JCModuleDecl(JCModifiers mods, ModuleKind kind, JCExpression qualId, List directives) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index 5e3b043fb1187..196beced712af 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -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 @@ -500,26 +500,47 @@ public static int firstStatPos(JCTree tree) { return tree.pos; } - /** The end position of given tree, if it is a block with - * defined endpos. + /** The end position of the given tree, if defined. */ public static int endPos(JCTree tree) { - if (tree.hasTag(BLOCK) && ((JCBlock) tree).endpos != Position.NOPOS) - return ((JCBlock) tree).endpos; - else if (tree.hasTag(SYNCHRONIZED)) + int endPos; + switch (tree.getTag()) { + case BLOCK: + endPos = ((JCBlock) tree).endpos; + break; + case SYNCHRONIZED: return endPos(((JCSynchronized) tree).body); - else if (tree.hasTag(TRY)) { + case TRY: JCTry t = (JCTry) tree; return endPos((t.finalizer != null) ? t.finalizer : (t.catchers.nonEmpty() ? t.catchers.last().body : t.body)); - } else if (tree.hasTag(SWITCH) && - ((JCSwitch) tree).endpos != Position.NOPOS) { - return ((JCSwitch) tree).endpos; - } else if (tree.hasTag(SWITCH_EXPRESSION) && - ((JCSwitchExpression) tree).endpos != Position.NOPOS) { - return ((JCSwitchExpression) tree).endpos; - } else + case SWITCH: + endPos = ((JCSwitch) tree).endpos; + break; + case SWITCH_EXPRESSION: + endPos = ((JCSwitchExpression) tree).endpos; + break; + case MODULEDEF: + endPos = ((JCModuleDecl) tree).endPos; + break; + case PACKAGEDEF: + endPos = ((JCPackageDecl) tree).endPos; + break; + case CLASSDEF: + endPos = ((JCClassDecl) tree).endPos; + break; + case METHODDEF: + endPos = ((JCMethodDecl) tree).endPos; + break; + case VARDEF: + endPos = ((JCVariableDecl) tree).endPos; + break; + default: return tree.pos; + } + if (endPos != Position.NOPOS) + return endPos; + return tree.pos; } diff --git a/test/langtools/tools/javac/parser/DeclarationEndPositions.java b/test/langtools/tools/javac/parser/DeclarationEndPositions.java new file mode 100644 index 0000000000000..12f0eac2a2c05 --- /dev/null +++ b/test/langtools/tools/javac/parser/DeclarationEndPositions.java @@ -0,0 +1,123 @@ +/* + * 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. + * + * 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. + */ + +/* + * @test + * @bug 8350212 + * @summary Verify ending source positions are calculated for declarations supporting SuppressWarnings + * @modules jdk.compiler/com.sun.tools.javac.tree + * @run main DeclarationEndPositions + */ + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree; +import com.sun.source.util.JavacTask; +import com.sun.source.util.TreeScanner; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.tree.TreeInfo; + +import java.io.IOException; +import java.net.URI; +import java.util.List; + +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; + +public class DeclarationEndPositions { + + public static void checkEndPosition(Class nodeType, String input, String marker) throws IOException { + + // Create source + var source = new SimpleJavaFileObject(URI.create("file://T.java"), JavaFileObject.Kind.SOURCE) { + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return input; + } + }; + + // Parse source + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + JavaCompiler.CompilationTask task = compiler.getTask(null, null, null, List.of(), List.of(), List.of(source)); + Iterable units = ((JavacTask)task).parse(); + + // Find node and check end position + JCTree.JCCompilationUnit unit = (JCTree.JCCompilationUnit)units.iterator().next(); + unit.accept(new TreeScanner() { + @Override + public Void scan(Tree node, Void aVoid) { + if (nodeType.isInstance(node)) { + JCTree tree = (JCTree)node; + int actual = TreeInfo.endPos(tree); + int expected = marker.indexOf('^') + 1; + if (actual != expected) { + throw new AssertionError(String.format( + "wrong end pos %d != %d for \"%s\" @ %d", actual, expected, input, tree.pos)); + } + } + return super.scan(node, aVoid); + } + }, null); + } + + public static void main(String... args) throws Exception { + + // JCModuleDecl + checkEndPosition(JCModuleDecl.class, + // 0 1 2 3 4 5 + // 012345678901234567890123456789012345678901234567890123456789 + "/* comment */ module fred { /* comment */ } /* comment */", + " ^ "); + + // JCPackageDecl + checkEndPosition(JCPackageDecl.class, + // 0 1 2 3 4 5 + // 012345678901234567890123456789012345678901234567890123456789 + "/* comment */ package fred; /* comment */", + " ^ "); + + // JCClassDecl + checkEndPosition(JCClassDecl.class, + // 0 1 2 3 4 5 + // 012345678901234567890123456789012345678901234567890123456789 + "/* comment */ class Fred { /* comment */ } /* comment */", + " ^ "); + + // JCMethodDecl + checkEndPosition(JCMethodDecl.class, + // 0 1 2 3 4 5 + // 012345678901234567890123456789012345678901234567890123456789 + "/* comment */ class Fred { void m() { /* comment */ } } /* comment */", + " ^ "); + + // JCVariableDecl + checkEndPosition(JCVariableDecl.class, + // 0 1 2 3 4 5 + // 012345678901234567890123456789012345678901234567890123456789 + "/* comment */ class Fred { int x = 123; } /* comment */", + " ^ "); + } +} diff --git a/test/langtools/tools/javac/parser/JavacParserTest.java b/test/langtools/tools/javac/parser/JavacParserTest.java index 40ab577a5d135..ee80553ba74d4 100644 --- a/test/langtools/tools/javac/parser/JavacParserTest.java +++ b/test/langtools/tools/javac/parser/JavacParserTest.java @@ -2298,10 +2298,9 @@ public Void visitCase(CaseTree node, Void p) { @Test //JDK-8310326 void testUnnamedClassPositions() throws IOException { - String code = """ - void main() { - } - """; + // 0 1 2 + // 012345678901234567890 + String code = "void main() { }"; DiagnosticCollector coll = new DiagnosticCollector<>(); JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, List.of("--enable-preview", "--source", System.getProperty("java.specification.version")), @@ -2313,7 +2312,7 @@ void main() { @Override public Void visitClass(ClassTree node, Void p) { assertEquals("Wrong start position", 0, sp.getStartPosition(cut, node)); - assertEquals("Wrong end position", -1, sp.getEndPosition(cut, node)); + assertEquals("Wrong end position", 15, sp.getEndPosition(cut, node)); assertEquals("Wrong modifiers start position", -1, sp.getStartPosition(cut, node.getModifiers())); assertEquals("Wrong modifiers end position", -1, sp.getEndPosition(cut, node.getModifiers())); return super.visitClass(node, p); From e235228708326379cc5072161fc2dcf617ea46e2 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Mon, 17 Feb 2025 21:41:19 -0600 Subject: [PATCH 02/44] Add end position for variables coming from variableDeclaratorId(). --- .../com/sun/tools/javac/parser/JavacParser.java | 8 ++++++-- .../javac/parser/DeclarationEndPositions.java | 16 ++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index abb247922cc8d..c11538986ee4f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -3944,7 +3944,9 @@ JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type, boolean log.error(token.pos, Errors.WrongReceiver); } } - return toP(F.at(pos).ReceiverVarDef(mods, pn, type)); + JCVariableDecl result = toP(F.at(pos).ReceiverVarDef(mods, pn, type)); + storeEnd(result, S.prevToken().endPos); + return result; } } else { /** if it is a lambda parameter and the token kind is not an identifier, @@ -3969,8 +3971,10 @@ JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type, boolean name = names.empty; } - return toP(F.at(pos).VarDef(mods, name, type, null, + JCVariableDecl result = toP(F.at(pos).VarDef(mods, name, type, null, type != null && type.hasTag(IDENT) && ((JCIdent)type).name == names.var)); + storeEnd(result, S.prevToken().endPos); + return result; } /** Resources = Resource { ";" Resources } diff --git a/test/langtools/tools/javac/parser/DeclarationEndPositions.java b/test/langtools/tools/javac/parser/DeclarationEndPositions.java index 12f0eac2a2c05..b32970d147eba 100644 --- a/test/langtools/tools/javac/parser/DeclarationEndPositions.java +++ b/test/langtools/tools/javac/parser/DeclarationEndPositions.java @@ -87,37 +87,33 @@ public static void main(String... args) throws Exception { // JCModuleDecl checkEndPosition(JCModuleDecl.class, - // 0 1 2 3 4 5 - // 012345678901234567890123456789012345678901234567890123456789 "/* comment */ module fred { /* comment */ } /* comment */", " ^ "); // JCPackageDecl checkEndPosition(JCPackageDecl.class, - // 0 1 2 3 4 5 - // 012345678901234567890123456789012345678901234567890123456789 "/* comment */ package fred; /* comment */", " ^ "); // JCClassDecl checkEndPosition(JCClassDecl.class, - // 0 1 2 3 4 5 - // 012345678901234567890123456789012345678901234567890123456789 "/* comment */ class Fred { /* comment */ } /* comment */", " ^ "); // JCMethodDecl checkEndPosition(JCMethodDecl.class, - // 0 1 2 3 4 5 - // 012345678901234567890123456789012345678901234567890123456789 "/* comment */ class Fred { void m() { /* comment */ } } /* comment */", " ^ "); // JCVariableDecl checkEndPosition(JCVariableDecl.class, - // 0 1 2 3 4 5 - // 012345678901234567890123456789012345678901234567890123456789 + "/* comment */ class Fred { int x; } /* comment */", + " ^ "); + checkEndPosition(JCVariableDecl.class, "/* comment */ class Fred { int x = 123; } /* comment */", " ^ "); + checkEndPosition(JCVariableDecl.class, + "/* comment */ class A { try {} catch (Error err) {} } /* comment */", + " ^ "); } } From daacc44c90c9cdd8e7a852f1bc7c7c3ac8ac48bf Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Tue, 18 Feb 2025 17:06:54 -0600 Subject: [PATCH 03/44] Fix bug where some warnings didn't have a source file position. --- .../com/sun/tools/javac/comp/Analyzer.java | 2 +- .../com/sun/tools/javac/comp/Attr.java | 4 +-- .../tools/javac/tree/VarWarnPosition.java | 26 +++++++++++++++++++ .../tools/javac/tree/VarWarnPosition.out | 4 +++ 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 test/langtools/tools/javac/tree/VarWarnPosition.java create mode 100644 test/langtools/tools/javac/tree/VarWarnPosition.out diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java index 3caa17a4dd289..ddc0dffbd795b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java @@ -366,7 +366,7 @@ abstract class RedundantLocalVarTypeAnalyzerBase extends } boolean isImplicitlyTyped(JCVariableDecl decl) { - return decl.vartype.pos == Position.NOPOS; + return decl.declaredUsingVar(); } /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index fec6017657320..160e5b8d25317 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -5693,9 +5693,9 @@ private Type capture(Type type) { private void setSyntheticVariableType(JCVariableDecl tree, Type type) { if (type.isErroneous()) { - tree.vartype = make.at(Position.NOPOS).Erroneous(); + tree.vartype = make.at(tree.pos()).Erroneous(); } else { - tree.vartype = make.at(Position.NOPOS).Type(type); + tree.vartype = make.at(tree.pos()).Type(type); } } diff --git a/test/langtools/tools/javac/tree/VarWarnPosition.java b/test/langtools/tools/javac/tree/VarWarnPosition.java new file mode 100644 index 0000000000000..bcb377bd2b0a5 --- /dev/null +++ b/test/langtools/tools/javac/tree/VarWarnPosition.java @@ -0,0 +1,26 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8329951 + * @summary Check that "var" variable synthetic types have a source position + * @compile/process/ref=VarWarnPosition.out -Xlint:deprecation -XDrawDiagnostics VarWarnPosition.java + */ + +import java.util.*; +import java.util.function.*; + +public class VarWarnPosition { + + VarWarnPosition() { + + // Test 1 + @SuppressWarnings("deprecation") + List deprecatedList = null; + for (var deprValue : deprecatedList) { } + + // Test 2 + Consumer c = d -> { }; + } +} + +@Deprecated +class Depr {} diff --git a/test/langtools/tools/javac/tree/VarWarnPosition.out b/test/langtools/tools/javac/tree/VarWarnPosition.out new file mode 100644 index 0000000000000..c8fd6005b8d1d --- /dev/null +++ b/test/langtools/tools/javac/tree/VarWarnPosition.out @@ -0,0 +1,4 @@ +VarWarnPosition.java:18:14: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package +VarWarnPosition.java:21:18: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package +VarWarnPosition.java:21:28: compiler.warn.has.been.deprecated: Depr, compiler.misc.unnamed.package +3 warnings From f5be4cad39776e02fa67ff1fd25986d16d1b8242 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Mon, 24 Mar 2025 08:59:11 -0500 Subject: [PATCH 04/44] Remove no-longer-needed workaround. --- .../share/classes/com/sun/tools/javac/main/JavaCompiler.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 05efcf7c04112..0879f22e3eed8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -928,11 +928,6 @@ public void compile(Collection sourceFileObjects, checkReusable(); hasBeenUsed = true; - // forcibly set the equivalent of -Xlint:-options, so that no further - // warnings about command line options are generated from this point on - options.put(XLINT_CUSTOM.primaryName + "-" + LintCategory.OPTIONS.option, "true"); - options.remove(XLINT_CUSTOM.primaryName + LintCategory.OPTIONS.option); - start_msec = now(); try { From 99b3c35e73a3f0b05b226e0ca1e572fb06c798c7 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 28 Mar 2025 22:17:07 -0600 Subject: [PATCH 05/44] Remove unused code. --- .../com/sun/tools/javac/main/JavaCompiler.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 05efcf7c04112..4a0f3419f643e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -238,10 +238,6 @@ else if (option.equals("class")) */ public Log log; - /** Whether or not the options lint category was initially disabled - */ - boolean optionsCheckingInitiallyDisabled; - /** Factory for creating diagnostic objects */ JCDiagnostic.Factory diagFactory; @@ -439,12 +435,6 @@ public JavaCompiler(Context context) { moduleFinder.moduleNameFromSourceReader = this::readModuleName; options = Options.instance(context); - // See if lint options checking was explicitly disabled by the - // user; this is distinct from the options check being - // enabled/disabled. - optionsCheckingInitiallyDisabled = - options.isSet(Option.XLINT_CUSTOM, "-options") || - options.isSet(Option.XLINT_CUSTOM, "none"); verbose = options.isSet(VERBOSE); sourceOutput = options.isSet(PRINTSOURCE); // used to be -s @@ -928,11 +918,6 @@ public void compile(Collection sourceFileObjects, checkReusable(); hasBeenUsed = true; - // forcibly set the equivalent of -Xlint:-options, so that no further - // warnings about command line options are generated from this point on - options.put(XLINT_CUSTOM.primaryName + "-" + LintCategory.OPTIONS.option, "true"); - options.remove(XLINT_CUSTOM.primaryName + LintCategory.OPTIONS.option); - start_msec = now(); try { From 0f85a240cc0b4e4a0d78af54027eef9a9337a511 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 4 Apr 2025 14:23:02 -0500 Subject: [PATCH 06/44] Add missing variable decl end position. --- .../share/classes/com/sun/tools/javac/parser/JavacParser.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index c11538986ee4f..b2279d21255fb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -3856,6 +3856,7 @@ JCVariableDecl variableDeclaratorRest(int pos, JCModifiers mods, JCExpression ty JCVariableDecl result = toP(F.at(pos).VarDef(mods, name, type, init, declaredUsingVar)); attach(result, dc); result.startPos = startPos; + storeEnd(result, S.prevToken().endPos); return result; } From e9931f6df2ebc1a283c33ec2295cec4f272b8288 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 4 Apr 2025 16:55:20 -0500 Subject: [PATCH 07/44] Add new method Log.clear(). --- .../com/sun/tools/javac/api/JavacTaskPool.java | 12 +++++------- .../share/classes/com/sun/tools/javac/util/Log.java | 12 ++++++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java index 1ce4cf50ada8a..87bc64c4e1976 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -267,7 +267,7 @@ void clear() { if (ht.get(Log.logKey) instanceof ReusableLog) { //log already inited - not first round - ((ReusableLog)Log.instance(this)).clear(); + Log.instance(this).clear(); Enter.instance(this).newRound(); ((ReusableJavaCompiler)ReusableJavaCompiler.instance(this)).clear(); Types.instance(this).newRound(); @@ -395,11 +395,9 @@ static class ReusableLog extends Log { this.context = context; } - void clear() { - recorded.clear(); - sourceMap.clear(); - nerrors = 0; - nwarnings = 0; + @Override + public void clear() { + super.clear(); //Set a fake listener that will lazily lookup the context for the 'real' listener. Since //this field is never updated when a new task is created, we cannot simply reset the field //or keep old value. This is a hack to workaround the limitations in the current infrastructure. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index 56ee413ea0716..e9da2efea9c8a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -689,6 +689,18 @@ public void report(JCDiagnostic diagnostic) { diagnosticHandler.report(diagnostic); } + /** + * Reset the state of this instance. + */ + public void clear() { + recorded.clear(); + sourceMap.clear(); + nerrors = 0; + nwarnings = 0; + nsuppressederrors = 0; + nsuppressedwarns = 0; + } + /** * Common diagnostic handling. * The diagnostic is counted, and depending on the options and how many diagnostics have been From a1108ac7266ad2288c3b649a3be7b69450fe5679 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 4 Apr 2025 17:17:00 -0500 Subject: [PATCH 08/44] Invoke Log.useSource() before recursing into attribution. --- .../com/sun/tools/javac/api/JavacTrees.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java index ce719230455d3..3981f91dce17a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -361,9 +361,14 @@ public TypeMirror getType(DocTreePath path) { new Log.DeferredDiagnosticHandler(log); try { Env env = getAttrContext(path.getTreePath()); - Type t = attr.attribType(dcReference.qualifierExpression, env); - if (t != null && !t.isErroneous()) { - return t; + JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); + try { + Type t = attr.attribType(dcReference.qualifierExpression, env); + if (t != null && !t.isErroneous()) { + return t; + } + } finally { + log.useSource(prevSource); } } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; @@ -390,6 +395,7 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { } Log.DeferredDiagnosticHandler deferredDiagnosticHandler = new Log.DeferredDiagnosticHandler(log); + JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); try { final TypeSymbol tsym; final Name memberName; @@ -511,6 +517,7 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; } finally { + log.useSource(prevSource); log.popDiagnosticHandler(deferredDiagnosticHandler); } } From 23447164dfb9c4e470891d4d40d87de44448140e Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Mon, 7 Apr 2025 09:20:16 -0500 Subject: [PATCH 09/44] Revert accidentally included unrelated changes. --- .../com/sun/tools/javac/api/JavacTrees.java | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java index 3981f91dce17a..ce719230455d3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, 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 @@ -361,14 +361,9 @@ public TypeMirror getType(DocTreePath path) { new Log.DeferredDiagnosticHandler(log); try { Env env = getAttrContext(path.getTreePath()); - JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); - try { - Type t = attr.attribType(dcReference.qualifierExpression, env); - if (t != null && !t.isErroneous()) { - return t; - } - } finally { - log.useSource(prevSource); + Type t = attr.attribType(dcReference.qualifierExpression, env); + if (t != null && !t.isErroneous()) { + return t; } } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; @@ -395,7 +390,6 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { } Log.DeferredDiagnosticHandler deferredDiagnosticHandler = new Log.DeferredDiagnosticHandler(log); - JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); try { final TypeSymbol tsym; final Name memberName; @@ -517,7 +511,6 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; } finally { - log.useSource(prevSource); log.popDiagnosticHandler(deferredDiagnosticHandler); } } From a9d584508d776496d2a851abfdd8f12ef1e80f65 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Mon, 7 Apr 2025 09:22:30 -0500 Subject: [PATCH 10/44] Reset the DiagnosticHandler stack in clear(). --- .../share/classes/com/sun/tools/javac/util/Log.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index e9da2efea9c8a..2167f47110477 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -699,6 +699,8 @@ public void clear() { nwarnings = 0; nsuppressederrors = 0; nsuppressedwarns = 0; + while (diagnosticHandler.prev != null) + popDiagnosticHandler(diagnosticHandler); } /** From 012702803f979a0c88a7b804435fc458e5696a72 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Tue, 8 Apr 2025 14:44:30 -0500 Subject: [PATCH 11/44] Refactoring/cleanup for handling of ending positions. --- .../com/sun/tools/javac/comp/Flow.java | 28 ++- .../com/sun/tools/javac/comp/Lower.java | 12 +- .../sun/tools/javac/comp/TransPatterns.java | 6 +- .../classes/com/sun/tools/javac/jvm/Gen.java | 28 +-- .../sun/tools/javac/parser/JavacParser.java | 228 ++++++------------ .../com/sun/tools/javac/tree/EndPosTable.java | 30 ++- .../com/sun/tools/javac/tree/JCTree.java | 16 +- .../com/sun/tools/javac/tree/TreeInfo.java | 65 ++--- .../share/classes/jdk/jshell/ReplParser.java | 1 - .../MissingLNTEntryForFinalizerTest.java | 2 +- .../javac/parser/DeclarationEndPositions.java | 2 +- .../javac/parser/extend/TrialParser.java | 1 - 12 files changed, 173 insertions(+), 246 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 6e16700b49cb4..37274347045a5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -379,6 +379,10 @@ private JumpKind(Tag treeTag) { abstract JCTree getTarget(JCTree tree); } + /** Table of ending positions. + */ + EndPosTable endPositions; + /** The currently pending exits that go from current inner blocks * to an enclosing block, in source order. */ @@ -614,7 +618,7 @@ public void visitMethodDef(JCMethodDecl tree) { tree.completesNormally = alive != Liveness.DEAD; if (alive == Liveness.ALIVE && !tree.sym.type.getReturnType().hasTag(VOID)) - log.error(TreeInfo.diagEndPos(tree.body), Errors.MissingRetStmt); + log.error(TreeInfo.diagEndPos(endPositions, tree.body), Errors.MissingRetStmt); clearPendingExits(true); } finally { @@ -756,10 +760,10 @@ public void visitSwitchExpression(JCSwitchExpression tree) { scanStats(c.stats); if (alive == Liveness.ALIVE) { if (c.caseKind == JCCase.RULE) { - log.error(TreeInfo.diagEndPos(c.body), + log.error(TreeInfo.diagEndPos(endPositions, c.body), Errors.RuleCompletesNormally); } else if (l.tail.isEmpty()) { - log.error(TreeInfo.diagEndPos(tree), + log.error(TreeInfo.diagEndPos(endPositions, tree), Errors.SwitchExpressionCompletesNormally); } } @@ -1232,7 +1236,7 @@ public void visitTry(JCTry tree) { scanStat(tree.finalizer); tree.finallyCanCompleteNormally = alive != Liveness.DEAD; if (alive == Liveness.DEAD) { - lint.logIfEnabled(TreeInfo.diagEndPos(tree.finalizer), + lint.logIfEnabled(TreeInfo.diagEndPos(endPositions, tree.finalizer), LintWarnings.FinallyCannotComplete); } else { while (exits.nonEmpty()) { @@ -1338,9 +1342,11 @@ public void analyzeTree(Env env, JCTree tree, TreeMaker make) { attrEnv = env; Flow.this.make = make; pendingExits = new ListBuffer<>(); + endPositions = env.toplevel.endPositions; alive = Liveness.ALIVE; scan(tree); } finally { + endPositions = null; pendingExits = null; Flow.this.make = null; } @@ -1922,11 +1928,13 @@ public void analyzeTree(Env env, JCTree tree, TreeMaker make) { attrEnv = env; Flow.this.make = make; pendingExits = new ListBuffer<>(); + endPositions = env.toplevel.endPositions; preciseRethrowTypes = new HashMap<>(); this.thrown = this.caught = null; this.classDef = null; scan(tree); } finally { + endPositions = null; pendingExits = null; Flow.this.make = null; this.thrown = this.caught = null; @@ -2536,13 +2544,13 @@ public void visitMethodDef(JCMethodDecl tree) { */ var.flags_field |= UNINITIALIZED_FIELD; } else { - checkInit(TreeInfo.diagEndPos(tree.body), var); + checkInit(TreeInfo.diagEndPos(endPositions, tree.body), var); } } else { checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); } } else { - checkInit(TreeInfo.diagEndPos(tree.body), var); + checkInit(TreeInfo.diagEndPos(endPositions, tree.body), var); } } } @@ -3223,11 +3231,11 @@ public void visitModuleDef(JCModuleDecl tree) { /** Perform definite assignment/unassignment analysis on a tree. */ - public void analyzeTree(Env env, TreeMaker make) { + public void analyzeTree(Env env, TreeMaker make) { analyzeTree(env, env.tree, make); } - public void analyzeTree(Env env, JCTree tree, TreeMaker make) { + public void analyzeTree(Env env, JCTree tree, TreeMaker make) { try { startPos = tree.pos().getStartPosition(); @@ -3240,6 +3248,7 @@ public void analyzeTree(Env env, JCTree tree, TreeMaker make) { nextadr = 0; Flow.this.make = make; pendingExits = new ListBuffer<>(); + endPositions = env.toplevel.endPositions; this.classDef = null; unrefdResources = WriteableScope.create(env.enclClass.sym); scan(tree); @@ -3255,6 +3264,7 @@ public void analyzeTree(Env env, JCTree tree, TreeMaker make) { firstadr = 0; nextadr = 0; Flow.this.make = null; + endPositions = null; pendingExits = null; this.classDef = null; unrefdResources = null; @@ -3467,8 +3477,10 @@ public void analyzeTree(Env env, JCTree tree, TreeMaker make) { attrEnv = env; Flow.this.make = make; pendingExits = new ListBuffer<>(); + endPositions = env.toplevel.endPositions; scan(tree); } finally { + endPositions = null; pendingExits = null; Flow.this.make = null; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index d2219bb08b861..7c325f5afdfdb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -1605,7 +1605,7 @@ private JCBlock makeTwrBlock(List resources, JCBlock block, int depth) { //create (semi-) finally block that will be copied into the main try body: int oldPos = make.pos; - make.at(TreeInfo.endPos(block)); + make.at(TreeInfo.endPos(endPosTable, block)); // if (#resource != null) { #resource.close(); } JCStatement bodyCloseStatement = makeResourceCloseInvocation(resourceUse); @@ -2048,7 +2048,7 @@ public T translate(T tree) { } else { make_at(tree.pos()); T result = super.translate(tree); - if (endPosTable != null && result != tree) { + if (endPosTable != null && result != null && result != tree) { endPosTable.replaceTree(tree, result); } return result; @@ -3678,7 +3678,7 @@ private void visitIterableForeachLoop(JCEnhancedForLoop tree) { vardefinit).setType(tree.var.type); indexDef.sym = tree.var.sym; JCBlock body = make.Block(0, List.of(indexDef, tree.body)); - body.endpos = TreeInfo.endPos(tree.body); + body.bracePos = TreeInfo.endPos(endPosTable, tree.body); result = translate(make. ForLoop(List.of(init), cond, @@ -3836,7 +3836,7 @@ private void handleSwitch(JCTree tree, JCExpression selector, List cases for (JCCase c : convertedCases) { if (c.caseKind == JCCase.RULE && c.completesNormally) { - JCBreak b = make.at(TreeInfo.endPos(c.stats.last())).Break(null); + JCBreak b = make.at(TreeInfo.endPos(endPosTable, c.stats.last())).Break(null); b.target = tree; c.stats = c.stats.append(b); } @@ -4139,7 +4139,7 @@ else if (oneCase == nullCase) { stmtList.append(switch2); JCBlock res = make.Block(0L, stmtList.toList()); - res.endpos = TreeInfo.endPos(tree); + res.bracePos = TreeInfo.endPos(endPosTable, tree); return res; } else { JCSwitchExpression switch2 = make.SwitchExpression(make.Ident(dollar_tmp), lb.toList()); @@ -4383,12 +4383,14 @@ public List translateTopLevelClass(Env env, JCTree cdef, Tr public JCMethodDecl translateMethod(Env env, JCMethodDecl methodDecl, TreeMaker make) { try { this.attrEnv = env; + this.endPosTable = env.toplevel.endPositions; this.make = make; this.currentClass = methodDecl.sym.enclClass(); proxies = new HashMap<>(); return translate(methodDecl); } finally { this.attrEnv = null; + this.endPosTable = null; this.make = null; this.currentClass = null; // the two fields below are set when visiting the method diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java index 93c4eaa03ee01..462b60214ac67 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java @@ -82,6 +82,7 @@ import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant; import com.sun.tools.javac.jvm.Target; +import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCBlock.PatternMatchingCatch; @@ -135,6 +136,7 @@ public static TransPatterns instance(Context context) { private final Preview preview; private TreeMaker make; private Env env; + private EndPosTable endPositions; BindingContext bindingContext = new BindingContext() { @Override @@ -751,7 +753,7 @@ void appendBreakIfNeeded(JCTree switchTree, List cases, JCCase c) { if (c.caseKind == CaseTree.CaseKind.RULE || (cases.last() == c && c.completesNormally)) { JCTree pos = c.stats.nonEmpty() ? c.stats.last() : c; - JCBreak brk = make.at(TreeInfo.endPos(pos)).Break(null); + JCBreak brk = make.at(TreeInfo.endPos(endPositions, pos)).Break(null); brk.target = switchTree; c.stats = c.stats.append(brk); } @@ -1391,11 +1393,13 @@ public JCTree translateTopLevelClass(Env env, JCTree cdef, TreeMake try { this.make = make; this.env = env; + this.endPositions = env.toplevel.endPositions; translate(cdef); } finally { // note that recursive invocations of this method fail hard this.make = null; this.env = null; + this.endPositions = null; } return cdef; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index b21a7f190c9f4..03d1c4e21fd4e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -500,7 +500,7 @@ else if ((block.flags & SYNTHETIC) == 0) c.members().enter(clinit); List clinitStats = clinitCode.toList(); JCBlock block = make.at(clinitStats.head.pos()).Block(0, clinitStats); - block.endpos = TreeInfo.endPos(clinitStats.last()); + block.bracePos = TreeInfo.endPos(endPosTable, clinitStats.last()); methodDefs.append(make.MethodDef(clinit, block)); if (!clinitTAs.isEmpty()) @@ -553,8 +553,8 @@ void normalizeMethod(JCMethodDecl md, List initCode, List make.Block(0, initCode.prepend(supercall))); - if (md.body.endpos == Position.NOPOS) - md.body.endpos = TreeInfo.endPos(md.body.stats.last()); + if (md.body.bracePos == Position.NOPOS) + md.body.bracePos = TreeInfo.endPos(endPosTable, md.body.stats.last()); md.sym.appendUniqueTypeAttributes(initTAs); } @@ -961,7 +961,7 @@ else if (tree.body != null) { // If last statement could complete normally, insert a // return at the end. if (code.isAlive()) { - code.statBegin(TreeInfo.endPos(tree.body)); + code.statBegin(TreeInfo.endPos(endPosTable, tree.body)); if (env.enclMethod == null || env.enclMethod.sym.type.getReturnType().hasTag(VOID)) { code.emitop0(return_); @@ -1015,7 +1015,7 @@ private int initCode(JCMethodDecl tree, Env env, boolean fatcode) { varDebugInfo, stackMap, debugCode, - genCrt ? new CRTable(tree, env.toplevel.endPositions) + genCrt ? new CRTable(tree, endPosTable) : null, syms, types, @@ -1121,7 +1121,7 @@ private void internalVisitBlock(JCBlock tree) { genStats(tree.stats, localEnv); // End the scope of all block-local variables in variable info. if (!env.tree.hasTag(METHODDEF)) { - code.statBegin(tree.endpos); + code.statBegin(tree.bracePos); code.endScopes(limit); code.pendingStatPos = Position.NOPOS; } @@ -1450,7 +1450,7 @@ private void handleSwitch(JCTree swtch, JCExpression selector, List case if (swtch instanceof JCSwitchExpression) { // Emit line position for the end of a switch expression - code.statBegin(TreeInfo.endPos(swtch)); + code.statBegin(TreeInfo.endPos(endPosTable, swtch)); } } code.endScopes(limit); @@ -1559,9 +1559,9 @@ void genTry(JCTree body, List catchers, Env env) { genStat(body, env, CRT_BLOCK); int endpc = code.curCP(); List gaps = env.info.gaps.toList(); - code.statBegin(TreeInfo.endPos(body)); + code.statBegin(TreeInfo.endPos(endPosTable, body)); genFinalizer(env); - code.statBegin(TreeInfo.endPos(env.tree)); + code.statBegin(TreeInfo.endPos(endPosTable, env.tree)); Chain exitChain; boolean actualTry = env.tree.hasTag(TRY); if (startpc == endpc && actualTry) { @@ -1580,7 +1580,7 @@ void genTry(JCTree body, List catchers, Env env) { genCatch(l.head, env, startpc, endpc, gaps); genFinalizer(env); if (hasFinalizer || l.tail.nonEmpty()) { - code.statBegin(TreeInfo.endPos(env.tree)); + code.statBegin(TreeInfo.endPos(endPosTable, env.tree)); exitChain = Code.mergeChains(exitChain, code.branch(goto_)); } @@ -1608,14 +1608,14 @@ void genTry(JCTree body, List catchers, Env env) { catchallpc, 0); startseg = env.info.gaps.next().intValue(); } - code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS)); + code.statBegin(TreeInfo.finalizerPos(endPosTable, env.tree, PosKind.FIRST_STAT_POS)); code.markStatBegin(); Item excVar = makeTemp(syms.throwableType); excVar.store(); genFinalizer(env); code.resolvePending(); - code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.END_POS)); + code.statBegin(TreeInfo.finalizerPos(endPosTable, env.tree, PosKind.END_POS)); code.markStatBegin(); excVar.load(); @@ -1631,7 +1631,7 @@ void genTry(JCTree body, List catchers, Env env) { code.resolve(env.info.cont); // Mark statement line number - code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS)); + code.statBegin(TreeInfo.finalizerPos(endPosTable, env.tree, PosKind.FIRST_STAT_POS)); code.markStatBegin(); // Save return address. @@ -1718,7 +1718,7 @@ void genCatchBlock(JCCatch tree, Env env) { code.statBegin(TreeInfo.firstStatPos(tree.body)); genStat(tree.body, env, CRT_BLOCK); code.endScopes(limit); - code.statBegin(TreeInfo.endPos(tree.body)); + code.statBegin(TreeInfo.endPos(endPosTable, tree.body)); } // where List, JCExpression>> catchTypesWithAnnotations(JCCatch tree) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index b2279d21255fb..a64ad869af1b9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -228,8 +228,8 @@ protected JavacParser(JavacParser parser, protected AbstractEndPosTable newEndPosTable(boolean keepEndPositions) { return keepEndPositions - ? new SimpleEndPosTable(this) - : new EmptyEndPosTable(this); + ? new SimpleEndPosTable() + : new MinimalEndPosTable(); } protected DocCommentTable newDocCommentTable(boolean keepDocComments, ParserFactory fac) { @@ -630,12 +630,14 @@ private void saveDanglingDocComments(Comment dc) { * * @param tree The tree to be used as index in the hashtable * @param dc The doc comment to associate with the tree, or null. + * @return {@code tree} */ - protected void attach(JCTree tree, Comment dc) { + protected T attach(T tree, Comment dc) { if (keepDocComments && dc != null) { docComments.putComment(tree, dc); } reportDanglingComments(tree, dc); + return tree; } /** Reports all dangling comments associated with the @@ -699,37 +701,35 @@ protected void setErrorEndPos(int errPos) { endPosTable.setErrorEndPos(errPos); } - protected void storeEnd(JCTree tree, int endpos) { - endPosTable.storeEnd(tree, endpos); - - // Module, package, class, method, and variable declarations remember their end positions - switch (tree.getTag()) { - case MODULEDEF: - ((JCModuleDecl)tree).endPos = endpos; - break; - case PACKAGEDEF: - ((JCPackageDecl)tree).endPos = endpos; - break; - case CLASSDEF: - ((JCClassDecl)tree).endPos = endpos; - break; - case METHODDEF: - ((JCMethodDecl)tree).endPos = endpos; - break; - case VARDEF: - ((JCVariableDecl)tree).endPos = endpos; - break; - default: - break; - } + /** + * Store ending position for a tree, the value of which is the greater of + * last error position in {@link #endPosTable} and the given ending position. + * @param tree tree node + * @param endpos the ending position to associate with {@code tree} + * @return {@code tree} + */ + protected T storeEnd(T tree, int endpos) { + return endPosTable.storeEnd(tree, endpos); } - protected T to(T t) { - return endPosTable.to(t); + /** + * Store current token's ending position for a tree, the value of which + * will be the greater of last error position in {@link #endPosTable} + * and the ending position of the current token. + * @param tree tree node + */ + protected T to(T tree) { + return storeEnd(tree, token.endPos); } - protected T toP(T t) { - return endPosTable.toP(t); + /** + * Store current token's ending position for a tree, the value of which + * will be the greater of last error position in {@link #endPosTable} + * and the ending position of the previous token. + * @param tree tree node + */ + protected T toP(T tree) { + return storeEnd(tree, S.prevToken().endPos); } /** Get the start position for a tree node. The start position is @@ -1759,7 +1759,7 @@ protected JCExpression term3() { case RBRACE: case EOF: JCSwitchExpression e = to(F.at(switchPos).SwitchExpression(selector, cases.toList())); - e.endpos = token.pos; + e.bracePos = token.pos; accept(RBRACE); return e; default: @@ -2782,7 +2782,6 @@ JCNewClass classCreatorRest(int newpos, List defs = classInterfaceOrRecordBody(names.empty, false, false); JCModifiers mods = F.at(Position.NOPOS).Modifiers(0); body = toP(F.at(pos).AnonymousClassDef(mods, defs)); - storeEnd(body, S.prevToken().endPos); } return toP(F.at(newpos).NewClass(encl, typeArgs, t, args, body)); } @@ -2838,9 +2837,9 @@ JCBlock block(int pos, long flags) { syntaxError(token.pos, Errors.Orphaned(token.kind)); switchBlockStatementGroups(); } - // the Block node has a field "endpos" for first char of last token, which is + // the Block node has a field "bracePos" for first char of last token, which is // usually but not necessarily the last char of the last token. - t.endpos = token.pos; + t.bracePos = token.pos; accept(RBRACE); return toP(t); } @@ -3161,7 +3160,6 @@ public JCStatement parseSimpleStatement() { accept(LBRACE); List cases = switchBlockStatementGroups(); JCSwitch t = to(F.at(pos).Switch(selector, cases)); - t.endpos = token.endPos; accept(RBRACE); return t; } @@ -3677,9 +3675,7 @@ JCAnnotation annotation(int pos, Tag kind) { } else { throw new AssertionError("Unhandled annotation kind: " + kind); } - - storeEnd(ann, S.prevToken().endPos); - return ann; + return toP(ann); } List annotationFieldValuesOpt() { @@ -3854,10 +3850,8 @@ JCVariableDecl variableDeclaratorRest(int pos, JCModifiers mods, JCExpression ty } } JCVariableDecl result = toP(F.at(pos).VarDef(mods, name, type, init, declaredUsingVar)); - attach(result, dc); result.startPos = startPos; - storeEnd(result, S.prevToken().endPos); - return result; + return attach(result, dc); } Name restrictedTypeName(JCExpression e, boolean shouldWarn) { @@ -3945,9 +3939,7 @@ JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type, boolean log.error(token.pos, Errors.WrongReceiver); } } - JCVariableDecl result = toP(F.at(pos).ReceiverVarDef(mods, pn, type)); - storeEnd(result, S.prevToken().endPos); - return result; + return toP(F.at(pos).ReceiverVarDef(mods, pn, type)); } } else { /** if it is a lambda parameter and the token kind is not an identifier, @@ -3972,10 +3964,8 @@ JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type, boolean name = names.empty; } - JCVariableDecl result = toP(F.at(pos).VarDef(mods, name, type, null, + return toP(F.at(pos).VarDef(mods, name, type, null, type != null && type.hasTag(IDENT) && ((JCIdent)type).name == names.var)); - storeEnd(result, S.prevToken().endPos); - return result; } /** Resources = Resource { ";" Resources } @@ -4045,7 +4035,6 @@ public JCTree.JCCompilationUnit parseCompilationUnit() { JCExpression pid = qualident(false); accept(SEMI); JCPackageDecl pd = toP(F.at(packagePos).PackageDecl(annotations, pid)); - storeEnd(pd, S.prevToken().endPos); attach(pd, firstToken.docComment()); consumedToplevelDoc = true; defs.append(pd); @@ -4165,7 +4154,6 @@ public JCTree.JCCompilationUnit parseCompilationUnit() { toplevel.docComments = docComments; if (keepLineMap) toplevel.lineMap = S.getLineMap(); - this.endPosTable.setParser(null); // remove reference to parser toplevel.endPositions = this.endPosTable; return toplevel; } @@ -4220,10 +4208,9 @@ JCModuleDecl moduleDecl(JCModifiers mods, ModuleKind kind, Comment dc) { int endPos = S.prevToken().endPos; accept(EOF); - JCModuleDecl result = toP(F.at(pos).ModuleDef(mods, kind, name, directives)); + JCModuleDecl result = F.at(pos).ModuleDef(mods, kind, name, directives); storeEnd(result, endPos); - attach(result, dc); - return result; + return attach(result, dc); } List moduleDirectiveList() { @@ -4424,9 +4411,7 @@ protected JCClassDecl classDeclaration(JCModifiers mods, Comment dc) { List defs = classInterfaceOrRecordBody(name, false, false); JCClassDecl result = toP(F.at(pos).ClassDef( mods, name, typarams, extending, implementing, permitting, defs)); - storeEnd(result, S.prevToken().endPos); - attach(result, dc); - return result; + return attach(result, dc); } protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) { @@ -4448,7 +4433,6 @@ protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) { saveDanglingDocComments(dc); List defs = classInterfaceOrRecordBody(name, false, true); - int endPos = S.prevToken().endPos; java.util.List fields = new ArrayList<>(); for (JCVariableDecl field : headerFields) { fields.add(field); @@ -4474,9 +4458,7 @@ protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) { defs = defs.prepend(field); } JCClassDecl result = toP(F.at(pos).ClassDef(mods, name, typarams, null, implementing, defs)); - storeEnd(result, endPos); - attach(result, dc); - return result; + return attach(result, dc); } Name typeName() { @@ -4515,9 +4497,7 @@ protected JCClassDecl interfaceDeclaration(JCModifiers mods, Comment dc) { defs = classInterfaceOrRecordBody(name, true, false); JCClassDecl result = toP(F.at(pos).ClassDef( mods, name, typarams, null, extending, permitting, defs)); - storeEnd(result, S.prevToken().endPos); - attach(result, dc); - return result; + return attach(result, dc); } List permitsClause(JCModifiers mods, String classOrInterface) { @@ -4564,9 +4544,7 @@ protected JCClassDecl enumDeclaration(JCModifiers mods, Comment dc) { JCClassDecl result = toP(F.at(pos). ClassDef(mods, name, List.nil(), null, implementing, defs)); - storeEnd(result, S.prevToken().endPos); - attach(result, dc); - return result; + return attach(result, dc); } /** EnumBody = "{" { EnumeratorDeclarationList } [","] @@ -4703,14 +4681,11 @@ JCTree enumeratorDeclaration(Name enumName) { createPos = identPos; JCIdent ident = F.at(identPos).Ident(enumName); JCNewClass create = F.at(createPos).NewClass(null, typeArgs, ident, args, body); - int endPos = S.prevToken().endPos; if (createPos != identPos) - storeEnd(create, endPos); + storeEnd(create, S.prevToken().endPos); ident = F.at(identPos).Ident(enumName); JCTree result = toP(F.at(pos).VarDef(mods, name, ident, create)); - storeEnd(result, endPos); - attach(result, dc); - return result; + return attach(result, dc); } /** TypeList = Type {"," Type} @@ -5138,9 +5113,7 @@ protected JCTree methodDeclaratorRest(int pos, toP(F.at(pos).MethodDef(mods, name, type, typarams, receiverParam, params, thrown, body, defaultValue)); - storeEnd(result, S.prevToken().endPos); - attach(result, dc); - return result; + return attach(result, dc); } finally { this.receiverParam = prevReceiverParam; } @@ -5437,8 +5410,7 @@ private JCExpression insertAnnotationsToMostInner( return mostInnerTypeToReturn; } else { mostInnerArrayType.elemtype = mostInnerTypeToReturn; - storeEnd(type, origEndPos); - return type; + return storeEnd(type, origEndPos); } } @@ -5651,38 +5623,27 @@ protected void checkSourceLevel(int pos, Feature feature) { } } - /* - * a functional source tree and end position mappings + /** + * A straightforward {@link EndPosTable} implementation. */ protected static class SimpleEndPosTable extends AbstractEndPosTable { - private final IntHashTable endPosMap; - - SimpleEndPosTable(JavacParser parser) { - super(parser); - endPosMap = new IntHashTable(); - } - - public void storeEnd(JCTree tree, int endpos) { - endPosMap.put(tree, errorEndPos > endpos ? errorEndPos : endpos); - } + private final IntHashTable endPosMap = new IntHashTable(); - protected T to(T t) { - storeEnd(t, parser.token.endPos); - return t; - } - - protected T toP(T t) { - storeEnd(t, parser.S.prevToken().endPos); - return t; + @Override + public T storeEnd(T tree, int endpos) { + endPosMap.put(tree, Math.max(endpos, errorEndPos)); + return tree; } + @Override public int getEndPos(JCTree tree) { int value = endPosMap.get(tree); // As long as Position.NOPOS==-1, this just returns value. return (value == -1) ? Position.NOPOS : value; } + @Override public int replaceTree(JCTree oldTree, JCTree newTree) { int pos = endPosMap.remove(oldTree); if (pos != -1) { @@ -5693,79 +5654,42 @@ public int replaceTree(JCTree oldTree, JCTree newTree) { } } - /* - * a default skeletal implementation without any mapping overhead. + /** + * A minimal implementation that only stores what's required. */ - protected static class EmptyEndPosTable extends AbstractEndPosTable { - - EmptyEndPosTable(JavacParser parser) { - super(parser); - } - - public void storeEnd(JCTree tree, int endpos) { /* empty */ } + protected static class MinimalEndPosTable extends SimpleEndPosTable { - protected T to(T t) { - return t; - } - - protected T toP(T t) { - return t; - } - - public int getEndPos(JCTree tree) { - return Position.NOPOS; - } - - public int replaceTree(JCTree oldTree, JCTree newTree) { - return Position.NOPOS; + @Override + public T storeEnd(T tree, int endpos) { + switch (tree.getTag()) { + case MODULEDEF: + case PACKAGEDEF: + case CLASSDEF: + case METHODDEF: + case VARDEF: + case BLOCK: + case SWITCH: + case SWITCH_EXPRESSION: + break; + default: + return tree; + } + return super.storeEnd(tree, endpos); } - } protected abstract static class AbstractEndPosTable implements EndPosTable { - /** - * The current parser. - */ - protected JavacParser parser; /** * Store the last error position. */ public int errorEndPos = Position.NOPOS; - public AbstractEndPosTable(JavacParser parser) { - this.parser = parser; - } - - /** - * Store current token's ending position for a tree, the value of which - * will be the greater of last error position and the ending position of - * the current token. - * @param t The tree. - */ - protected abstract T to(T t); - - /** - * Store current token's ending position for a tree, the value of which - * will be the greater of last error position and the ending position of - * the previous token. - * @param t The tree. - */ - protected abstract T toP(T t); - - /** - * Set the error position during the parsing phases, the value of which - * will be set only if it is greater than the last stored error position. - * @param errPos The error position - */ + @Override public void setErrorEndPos(int errPos) { if (errPos > errorEndPos) { errorEndPos = errPos; } } - - public void setParser(JavacParser parser) { - this.parser = parser; - } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java index d68ba838ba198..c81e7e8d6d618 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java @@ -25,8 +25,24 @@ package com.sun.tools.javac.tree; +import com.sun.tools.javac.util.Position; + /** * Specifies the methods to access a mappings of syntax trees to end positions. + * + *

+ * Implementations must store end positions for at least these node types: + *

    + *
  • {@link JCTree.JCModuleDecl} + *
  • {@link JCTree.JCPackageDecl} + *
  • {@link JCTree.JCClassDecl} + *
  • {@link JCTree.JCMethodDecl} + *
  • {@link JCTree.JCVariableDecl} + *
  • {@link JCTree.JCBlock} + *
  • {@link JCTree.JCSwitch} + *
  • {@link JCTree.JCSwitchExpression} + *
+ * *

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 @@ -40,15 +56,23 @@ public interface EndPosTable { * @param tree JCTree * @return position of the source tree or Positions.NOPOS for non-existent mapping */ - public int getEndPos(JCTree tree); + int getEndPos(JCTree tree); /** * Store ending position for a tree, the value of which is the greater of * last error position and the given ending position. * @param tree The tree. * @param endpos The ending position to associate with the tree. + * @return the {@code tree} + */ + T storeEnd(T tree, int endpos); + + /** + * Set the error position during the parsing phases, the value of which + * will be set only if it is greater than the last stored error position. + * @param errPos The error position */ - public abstract void storeEnd(JCTree tree, int endpos); + void setErrorEndPos(int errPos); /** * Give an old tree and a new tree, the old tree will be replaced with @@ -58,5 +82,5 @@ public interface EndPosTable { * @param newtree a JCTree to be replaced with * @return position of the old tree or Positions.NOPOS for non-existent mapping */ - public int replaceTree(JCTree oldtree, JCTree newtree); + int replaceTree(JCTree oldtree, JCTree newtree); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index 5d39a914b2d77..b4d357e7b0823 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -641,8 +641,6 @@ public static class JCPackageDecl extends JCTree implements PackageTree { /** The tree representing the package clause. */ public JCExpression pid; public PackageSymbol packge; - /** Position of closing semicolon, optional. */ - public int endPos = Position.NOPOS; public JCPackageDecl(List annotations, JCExpression pid) { this.annotations = annotations; this.pid = pid; @@ -839,8 +837,6 @@ public static class JCClassDecl extends JCStatement implements ClassTree { public List defs; /** the symbol */ public ClassSymbol sym; - /** position of closing brace, optional. */ - public int endPos = Position.NOPOS; protected JCClassDecl(JCModifiers mods, Name name, List typarams, @@ -935,8 +931,6 @@ public static class JCMethodDecl extends JCTree implements MethodTree { public MethodSymbol sym; /** does this method completes normally */ public boolean completesNormally; - /** position of closing brace or semicolon, optional. */ - public int endPos = Position.NOPOS; protected JCMethodDecl(JCModifiers mods, Name name, @@ -1022,8 +1016,6 @@ public static class JCVariableDecl extends JCStatement implements VariableTree { public VarSymbol sym; /** explicit start pos */ public int startPos = Position.NOPOS; - /** position of closing semicolon, optional. */ - public int endPos = Position.NOPOS; /** declared using `var` */ private boolean declaredUsingVar; @@ -1129,7 +1121,7 @@ public static class JCBlock extends JCStatement implements BlockTree { /** statements */ public List stats; /** Position of closing brace, optional. */ - public int endpos = Position.NOPOS; + public int bracePos = Position.NOPOS; /** If this block contains record pattern, it is necessary to catch * exceptions from the deconstructors and wrap them. * The {@code patternMatchingCatch} keeps the list of the deconstructor @@ -1337,8 +1329,6 @@ public Tag getTag() { public static class JCSwitch extends JCStatement implements SwitchTree { public JCExpression selector; public List cases; - /** Position of closing brace, optional. */ - public int endpos = Position.NOPOS; public boolean hasUnconditionalPattern; public boolean isExhaustive; public boolean patternSwitch; @@ -1438,7 +1428,7 @@ public static class JCSwitchExpression extends JCPolyExpression implements Switc public JCExpression selector; public List cases; /** Position of closing brace, optional. */ - public int endpos = Position.NOPOS; + public int bracePos = Position.NOPOS; public boolean hasUnconditionalPattern; public boolean isExhaustive; public boolean patternSwitch; @@ -3134,8 +3124,6 @@ public static class JCModuleDecl extends JCTree implements ModuleTree { public JCExpression qualId; public List directives; public ModuleSymbol sym; - /** position of closing brace, optional. */ - public int endPos = Position.NOPOS; protected JCModuleDecl(JCModifiers mods, ModuleKind kind, JCExpression qualId, List directives) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index 196beced712af..d42c9b0031218 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -52,7 +52,7 @@ import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.ToIntFunction; +import java.util.function.ToIntBiFunction; import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT; import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.RIGHT; @@ -502,42 +502,22 @@ public static int firstStatPos(JCTree tree) { /** The end position of the given tree, if defined. */ - public static int endPos(JCTree tree) { - int endPos; + public static int endPos(EndPosTable endPosTable, JCTree tree) { switch (tree.getTag()) { case BLOCK: - endPos = ((JCBlock) tree).endpos; - break; + return ((JCBlock)tree).bracePos; + case SWITCH_EXPRESSION: + return ((JCSwitchExpression)tree).bracePos; case SYNCHRONIZED: - return endPos(((JCSynchronized) tree).body); + return endPos(endPosTable, ((JCSynchronized)tree).body); case TRY: JCTry t = (JCTry) tree; - return endPos((t.finalizer != null) ? t.finalizer + return endPos(endPosTable, (t.finalizer != null) ? t.finalizer : (t.catchers.nonEmpty() ? t.catchers.last().body : t.body)); - case SWITCH: - endPos = ((JCSwitch) tree).endpos; - break; - case SWITCH_EXPRESSION: - endPos = ((JCSwitchExpression) tree).endpos; - break; - case MODULEDEF: - endPos = ((JCModuleDecl) tree).endPos; - break; - case PACKAGEDEF: - endPos = ((JCPackageDecl) tree).endPos; - break; - case CLASSDEF: - endPos = ((JCClassDecl) tree).endPos; - break; - case METHODDEF: - endPos = ((JCMethodDecl) tree).endPos; - break; - case VARDEF: - endPos = ((JCVariableDecl) tree).endPos; - break; default: - return tree.pos; + break; } + int endPos = endPosTable.getEndPos(tree); if (endPos != Position.NOPOS) return endPos; return tree.pos; @@ -667,11 +647,6 @@ public static int getEndPos(JCTree tree, EndPosTable endPosTable) { if (tree == null) return Position.NOPOS; - if (endPosTable == null) { - // fall back on limited info in the tree - return endPos(tree); - } - int mapPos = endPosTable.getEndPos(tree); if (mapPos != Position.NOPOS) return mapPos; @@ -755,8 +730,8 @@ public static int getEndPos(JCTree tree, EndPosTable endPosTable) { * end position of given tree, if it is a block with * defined endpos. */ - public static DiagnosticPosition diagEndPos(final JCTree tree) { - final int endPos = TreeInfo.endPos(tree); + public static DiagnosticPosition diagEndPos(EndPosTable endPosTable, final JCTree tree) { + final int endPos = TreeInfo.endPos(endPosTable, tree); return new DiagnosticPosition() { public JCTree getTree() { return tree; } public int getStartPosition() { return TreeInfo.getStartPos(tree); } @@ -768,30 +743,30 @@ public int getEndPosition(EndPosTable endPosTable) { } public enum PosKind { - START_POS(TreeInfo::getStartPos), - FIRST_STAT_POS(TreeInfo::firstStatPos), + START_POS((table, tree) -> TreeInfo.getStartPos(tree)), + FIRST_STAT_POS((table, tree) -> TreeInfo.firstStatPos(tree)), END_POS(TreeInfo::endPos); - final ToIntFunction posFunc; + final ToIntBiFunction posFunc; - PosKind(ToIntFunction posFunc) { + PosKind(ToIntBiFunction posFunc) { this.posFunc = posFunc; } - int toPos(JCTree tree) { - return posFunc.applyAsInt(tree); + int toPos(EndPosTable endPosTable, JCTree tree) { + return posFunc.applyAsInt(endPosTable, tree); } } /** The position of the finalizer of given try/synchronized statement. */ - public static int finalizerPos(JCTree tree, PosKind posKind) { + public static int finalizerPos(EndPosTable endPosTable, JCTree tree, PosKind posKind) { if (tree.hasTag(TRY)) { JCTry t = (JCTry) tree; Assert.checkNonNull(t.finalizer); - return posKind.toPos(t.finalizer); + return posKind.toPos(endPosTable, t.finalizer); } else if (tree.hasTag(SYNCHRONIZED)) { - return endPos(((JCSynchronized) tree).body); + return endPos(endPosTable, ((JCSynchronized)tree).body); } else { throw new AssertionError(); } diff --git a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java index 2968bc3e17336..ce16eb2e699c7 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java @@ -141,7 +141,6 @@ public ReplUnit(List defs) { storeEnd(toplevel, S.prevToken().endPos); } toplevel.lineMap = S.getLineMap(); - this.endPosTable.setParser(null); // remove reference to parser toplevel.endPositions = this.endPosTable; return toplevel; } diff --git a/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java b/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java index ff3f2cf32002b..eae937fb03fa3 100644 --- a/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java +++ b/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java @@ -151,7 +151,7 @@ public com.sun.tools.javac.code.Type attribStat(JCTree tree, Env en com.sun.tools.javac.code.Type result = super.attribStat(tree, env); if (tree.hasTag(TRY)) { JCTry tryTree = (JCTry)tree; - lineNumber = env.toplevel.lineMap.getLineNumber(tryTree.finalizer.endpos); + lineNumber = env.toplevel.lineMap.getLineNumber(tryTree.finalizer.bracePos); } return result; } diff --git a/test/langtools/tools/javac/parser/DeclarationEndPositions.java b/test/langtools/tools/javac/parser/DeclarationEndPositions.java index b32970d147eba..6ab9d6f6f4cd4 100644 --- a/test/langtools/tools/javac/parser/DeclarationEndPositions.java +++ b/test/langtools/tools/javac/parser/DeclarationEndPositions.java @@ -71,7 +71,7 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept public Void scan(Tree node, Void aVoid) { if (nodeType.isInstance(node)) { JCTree tree = (JCTree)node; - int actual = TreeInfo.endPos(tree); + int actual = TreeInfo.endPos(unit.endPositions, tree); int expected = marker.indexOf('^') + 1; if (actual != expected) { throw new AssertionError(String.format( diff --git a/test/langtools/tools/javac/parser/extend/TrialParser.java b/test/langtools/tools/javac/parser/extend/TrialParser.java index 539676a90fb98..f508b23f4033b 100644 --- a/test/langtools/tools/javac/parser/extend/TrialParser.java +++ b/test/langtools/tools/javac/parser/extend/TrialParser.java @@ -139,7 +139,6 @@ public TrialUnit(List defs) { storeEnd(toplevel, S.prevToken().endPos); } toplevel.lineMap = S.getLineMap(); - this.endPosTable.setParser(null); // remove reference to parser toplevel.endPositions = this.endPosTable; return toplevel; } From f831a1081ac25ca9a6fb1e1af713726030b252dc Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Tue, 8 Apr 2025 15:46:32 -0500 Subject: [PATCH 12/44] Add field LintCategory.annotationSuppression. --- .../com/sun/tools/javac/code/Lint.java | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index ca888676009ba..5133d84af8b86 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -212,9 +212,12 @@ public enum LintCategory { CAST("cast"), /** - * Warn about issues related to classfile contents + * Warn about issues related to classfile contents. + * + *

+ * This category is not supported by {@code @SuppressWarnings}. */ - CLASSFILE("classfile"), + CLASSFILE("classfile", false), /** * Warn about "dangling" documentation comments, @@ -260,8 +263,11 @@ public enum LintCategory { /** * Warn about use of incubating modules. + * + *

+ * This category is not supported by {@code @SuppressWarnings}. */ - INCUBATING("incubating"), + INCUBATING("incubating", false), /** * Warn about compiler possible lossy conversions. @@ -284,14 +290,20 @@ public enum LintCategory { OPENS("opens"), /** - * Warn about issues relating to use of command line options + * Warn about issues relating to use of command line options. + * + *

+ * This category is not supported by {@code @SuppressWarnings}. */ - OPTIONS("options"), + OPTIONS("options", false), /** * Warn when any output file is written to more than once. + * + *

+ * This category is not supported by {@code @SuppressWarnings}. */ - OUTPUT_FILE_CLASH("output-file-clash"), + OUTPUT_FILE_CLASH("output-file-clash", false), /** * Warn about issues regarding method overloads. @@ -305,10 +317,11 @@ public enum LintCategory { /** * Warn about invalid path elements on the command line. - * Such warnings cannot be suppressed with the SuppressWarnings - * annotation. + * + *

+ * This category is not supported by {@code @SuppressWarnings}. */ - PATH("path"), + PATH("path", false), /** * Warn about issues regarding annotation processing. @@ -357,8 +370,11 @@ public enum LintCategory { /** * Warn about issues relating to use of text blocks + * + *

+ * This category is not supported by {@code @SuppressWarnings}. */ - TEXT_BLOCKS("text-blocks"), + TEXT_BLOCKS("text-blocks", false), /** * Warn about possible 'this' escapes before subclass instance is fully initialized. @@ -391,7 +407,12 @@ public enum LintCategory { RESTRICTED("restricted"); LintCategory(String option) { + this(option, true); + } + + LintCategory(String option, boolean annotationSuppression) { this.option = option; + this.annotationSuppression = annotationSuppression; map.put(option, this); } @@ -411,6 +432,9 @@ public static EnumSet newEmptySet() { /** Get the string representing this category in @SuppressAnnotations and -Xlint options. */ public final String option; + + /** Does this category support being suppressed by the {@code @SuppressWarnings} annotation? */ + public final boolean annotationSuppression; } /** @@ -504,6 +528,7 @@ private EnumSet suppressionsFrom(Attribute.Compound suppressWarnin for (Attribute value : values.values) { Optional.of((String)((Attribute.Constant)value).value) .flatMap(LintCategory::get) + .filter(lc -> lc.annotationSuppression) .ifPresent(result::add); } return result; From 0791ff7489f9523c925a9d04d15fd2c18d905e8b Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Tue, 8 Apr 2025 20:18:31 -0500 Subject: [PATCH 13/44] Refactor handling of DEPRECATION_ON_IMPORT via new flag Check.importSuppression. --- .../com/sun/tools/javac/comp/Check.java | 33 ++++++++++--------- .../com/sun/tools/javac/comp/TypeEnter.java | 5 ++- .../javac/preview/PreviewAutoSuppress.java | 4 +-- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index dbf66c88f0a9f..a49751e91e9ff 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -218,6 +218,12 @@ protected Check(Context context) { */ private final boolean allowSealed; + /** Whether to force suppression of deprecation and preview warnings. + * This happens when attributing import statements for JDK 9+. + * @see Feature#DEPRECATION_ON_IMPORT + */ + private boolean importSuppression; + /* ************************************************************************* * Errors and Warnings **************************************************************************/ @@ -228,6 +234,12 @@ Lint setLint(Lint newLint) { return prev; } + boolean setImportSuppression(boolean newImportSuppression) { + boolean prev = importSuppression; + importSuppression = newImportSuppression; + return prev; + } + MethodSymbol setMethod(MethodSymbol newMethod) { MethodSymbol prev = method; method = newMethod; @@ -261,19 +273,10 @@ void warnDeprecated(DiagnosticPosition pos, Symbol sym) { * @param msg A Warning describing the problem. */ public void warnPreviewAPI(DiagnosticPosition pos, LintWarning warnKey) { - if (!lint.isSuppressed(LintCategory.PREVIEW)) + if (!importSuppression && !lint.isSuppressed(LintCategory.PREVIEW)) preview.reportPreviewWarning(pos, warnKey); } - /** Log a preview warning. - * @param pos Position to be used for error reporting. - * @param msg A Warning describing the problem. - */ - public void warnDeclaredUsingPreview(DiagnosticPosition pos, Symbol sym) { - if (!lint.isSuppressed(LintCategory.PREVIEW)) - preview.reportPreviewWarning(pos, LintWarnings.DeclaredUsingPreview(kindName(sym), sym)); - } - /** Log a preview warning. * @param pos Position to be used for error reporting. * @param msg A Warning describing the problem. @@ -3773,8 +3776,8 @@ void checkDeprecated(final DiagnosticPosition pos, final Symbol other, final Sym } void checkDeprecated(Supplier pos, final Symbol other, final Symbol s) { - if ( (s.isDeprecatedForRemoval() - || s.isDeprecated() && !other.isDeprecated()) + if (!importSuppression + && (s.isDeprecatedForRemoval() || s.isDeprecated() && !other.isDeprecated()) && (s.outermostClass() != other.outermostClass() || s.outermostClass() == null) && s.kind != Kind.PCK) { deferredLintHandler.report(_l -> warnDeprecated(pos.get(), s)); @@ -3823,10 +3826,10 @@ void checkPreview(DiagnosticPosition pos, Symbol other, Type site, Symbol s) { log.error(pos, Errors.IsPreview(s)); } else { preview.markUsesPreview(pos); - deferredLintHandler.report(_l -> warnPreviewAPI(pos, LintWarnings.IsPreview(s))); + warnPreviewAPI(pos, LintWarnings.IsPreview(s)); } } else { - deferredLintHandler.report(_l -> warnPreviewAPI(pos, LintWarnings.IsPreviewReflective(s))); + warnPreviewAPI(pos, LintWarnings.IsPreviewReflective(s)); } } if (preview.declaredUsingPreviewFeature(s)) { @@ -3835,7 +3838,7 @@ void checkPreview(DiagnosticPosition pos, Symbol other, Type site, Symbol s) { //If "s" is compiled from source, then there was an error for it already; //if "s" is from classfile, there already was an error for the classfile. preview.markUsesPreview(pos); - deferredLintHandler.report(_l -> warnDeclaredUsingPreview(pos, s)); + warnPreviewAPI(pos, LintWarnings.DeclaredUsingPreview(kindName(s), s)); } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java index a15ae9943d720..d0b85b3d1cb99 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java @@ -527,8 +527,7 @@ private void doModuleImport(JCModuleImport tree) { Type attribImportType(JCTree tree, Env env) { Assert.check(completionEnabled); - Lint prevLint = chk.setLint(allowDeprecationOnImport ? - lint : lint.suppress(LintCategory.DEPRECATION, LintCategory.REMOVAL, LintCategory.PREVIEW)); + boolean prevImportSuppression = chk.setImportSuppression(!allowDeprecationOnImport); try { // To prevent deep recursion, suppress completion of some // types. @@ -536,7 +535,7 @@ Type attribImportType(JCTree tree, Env env) { return attr.attribType(tree, env); } finally { completionEnabled = true; - chk.setLint(prevLint); + chk.setImportSuppression(prevImportSuppression); } } diff --git a/test/langtools/tools/javac/preview/PreviewAutoSuppress.java b/test/langtools/tools/javac/preview/PreviewAutoSuppress.java index c7cffda1f6972..501eea58331a5 100644 --- a/test/langtools/tools/javac/preview/PreviewAutoSuppress.java +++ b/test/langtools/tools/javac/preview/PreviewAutoSuppress.java @@ -190,8 +190,8 @@ public static class C extends Outer {} .getOutputLines(Task.OutputKind.DIRECT); expected = - List.of("Use.java:5:13: compiler.warn.is.preview: preview.api.Outer", - "Use.java:7:35: compiler.warn.is.preview: preview.api.Outer", + List.of("Use.java:7:35: compiler.warn.is.preview: preview.api.Outer", + "Use.java:5:13: compiler.warn.is.preview: preview.api.Outer", "2 warnings"); if (!log.equals(expected)) From c49164395469e75b54667a554b4043f45acf3ee9 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Tue, 8 Apr 2025 20:21:36 -0500 Subject: [PATCH 14/44] Bump copyright. --- test/langtools/tools/javac/preview/PreviewAutoSuppress.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/langtools/tools/javac/preview/PreviewAutoSuppress.java b/test/langtools/tools/javac/preview/PreviewAutoSuppress.java index 501eea58331a5..058ccdf0a2a9d 100644 --- a/test/langtools/tools/javac/preview/PreviewAutoSuppress.java +++ b/test/langtools/tools/javac/preview/PreviewAutoSuppress.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 From 5bb0b11431e6aa24dac1e940b8eef5d49366f64f Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Wed, 9 Apr 2025 10:04:56 -0500 Subject: [PATCH 15/44] Add "not yet" comment to TEXT_BLOCKS. --- .../share/classes/com/sun/tools/javac/code/Lint.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index 5133d84af8b86..fb1b782d87e39 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -372,7 +372,7 @@ public enum LintCategory { * Warn about issues relating to use of text blocks * *

- * This category is not supported by {@code @SuppressWarnings}. + * This category is not supported by {@code @SuppressWarnings} (yet - see JDK-8224228). */ TEXT_BLOCKS("text-blocks", false), From 2e68d7a3437f80df199541f9dc78a98dac21123c Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Wed, 9 Apr 2025 10:41:56 -0500 Subject: [PATCH 16/44] Small cleanups relating to Log.DiagnosticHandler. --- .../com/sun/tools/javac/api/JavacTrees.java | 8 +-- .../com/sun/tools/javac/comp/Analyzer.java | 2 +- .../com/sun/tools/javac/comp/Attr.java | 2 +- .../com/sun/tools/javac/comp/Check.java | 2 +- .../sun/tools/javac/comp/DeferredAttr.java | 4 +- .../com/sun/tools/javac/comp/Flow.java | 8 +-- .../sun/tools/javac/main/JavaCompiler.java | 4 +- .../tools/javac/parser/ReferenceParser.java | 11 +-- .../JavacProcessingEnvironment.java | 21 ++---- .../classes/com/sun/tools/javac/util/Log.java | 72 +++++++++---------- .../share/classes/jdk/jshell/TaskFactory.java | 4 +- 11 files changed, 60 insertions(+), 78 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java index ce719230455d3..7bb473fc2b956 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -357,8 +357,7 @@ public TypeMirror getType(DocTreePath path) { if (tree instanceof DCReference dcReference) { JCTree qexpr = dcReference.qualifierExpression; if (qexpr != null) { - Log.DeferredDiagnosticHandler deferredDiagnosticHandler = - new Log.DeferredDiagnosticHandler(log); + Log.DeferredDiagnosticHandler deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); try { Env env = getAttrContext(path.getTreePath()); Type t = attr.attribType(dcReference.qualifierExpression, env); @@ -388,8 +387,7 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { // module name and member name without type return null; } - Log.DeferredDiagnosticHandler deferredDiagnosticHandler = - new Log.DeferredDiagnosticHandler(log); + Log.DeferredDiagnosticHandler deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); try { final TypeSymbol tsym; final Name memberName; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java index 3caa17a4dd289..514de5e2fe812 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java @@ -730,7 +730,7 @@ class RewritingContext { * Simple deferred diagnostic handler which filters out all messages and keep track of errors. */ Log.DeferredDiagnosticHandler diagHandler() { - return new Log.DeferredDiagnosticHandler(log, d -> { + return log.new DeferredDiagnosticHandler(d -> { if (d.getType() == DiagnosticType.ERROR) { erroneous = true; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index fec6017657320..cc168d729078d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -2038,7 +2038,7 @@ void checkAutoCloseable(DiagnosticPosition pos, Env env, Type resou types.asSuper(resource, syms.autoCloseableType.tsym) != null && !types.isSameType(resource, syms.autoCloseableType)) { // Don't emit warning for AutoCloseable itself Symbol close = syms.noSymbol; - Log.DiagnosticHandler discardHandler = new Log.DiscardDiagnosticHandler(log); + Log.DiagnosticHandler discardHandler = log.new DiscardDiagnosticHandler(); try { close = rs.resolveQualifiedMethod(pos, env, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index dbf66c88f0a9f..7396b89de1c90 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -3675,7 +3675,7 @@ private Name[] defaultTargetMetaInfo() { */ public boolean validateAnnotationDeferErrors(JCAnnotation a) { boolean res = false; - final Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); + final Log.DiagnosticHandler diagHandler = log.new DiscardDiagnosticHandler(); try { res = validateAnnotation(a); } finally { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java index 56249fefad985..d16bbf2a2eee0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -547,7 +547,7 @@ public void scan(JCTree tree) { } DeferredAttrDiagHandler(Log log, JCTree newTree) { - super(log, d -> { + log.super(d -> { PosScanner posScanner = new PosScanner(d.getDiagnosticPosition()); posScanner.scan(newTree); return posScanner.found; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 6e16700b49cb4..e685f139b680c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -239,7 +239,7 @@ public void analyzeLambda(Env env, JCLambda that, TreeMaker make, b //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis //related errors, which will allow for more errors to be detected if (!speculative) { - diagHandler = new Log.DiscardDiagnosticHandler(log); + diagHandler = log.new DiscardDiagnosticHandler(); } try { new LambdaAliveAnalyzer().analyzeTree(env, that, make); @@ -257,7 +257,7 @@ public List analyzeLambdaThrownTypes(final Env env, //message will be reported and will cause compilation to skip the flow analysis //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis //related errors, which will allow for more errors to be detected - Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); + Log.DiagnosticHandler diagHandler = log.new DiscardDiagnosticHandler(); try { new LambdaAssignAnalyzer(env).analyzeTree(env, that, make); LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer(); @@ -274,7 +274,7 @@ public boolean aliveAfter(Env env, JCTree that, TreeMaker make) { //message will be reported and will cause compilation to skip the flow analysis //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis //related errors, which will allow for more errors to be detected - Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); + Log.DiagnosticHandler diagHandler = log.new DiscardDiagnosticHandler(); try { SnippetAliveAnalyzer analyzer = new SnippetAliveAnalyzer(); @@ -291,7 +291,7 @@ public boolean breaksToTree(Env env, JCTree breakTo, JCTree body, T //message will be reported and will cause compilation to skip the flow analysis //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis //related errors, which will allow for more errors to be detected - Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); + Log.DiagnosticHandler diagHandler = log.new DiscardDiagnosticHandler(); try { SnippetBreakToAnalyzer analyzer = new SnippetBreakToAnalyzer(breakTo); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 05efcf7c04112..b6a4546e28b27 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -1166,7 +1166,7 @@ public void initProcessAnnotations(Iterable processors, genEndPos = true; if (!taskListener.isEmpty()) taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING)); - deferredDiagnosticHandler = new Log.DeferredDiagnosticHandler(log); + deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); procEnvImpl.getFiler().setInitialState(initialFiles, initialClassNames); } } else { // free resources @@ -1898,7 +1898,7 @@ private Name findPackageInFile(JavaFileObject fo) { private Name parseAndGetName(JavaFileObject fo, Function tree2Name) { - DiagnosticHandler dh = new DiscardDiagnosticHandler(log); + DiagnosticHandler dh = log.new DiscardDiagnosticHandler(); JavaFileObject prevSource = log.useSource(fo); try { JCTree.JCCompilationUnit t = parse(fo, fo.getCharContent(false), true); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ReferenceParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ReferenceParser.java index e4b5965d02074..acc655df2764a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ReferenceParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ReferenceParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -119,7 +119,7 @@ public Reference parse(String sig, Mode mode) throws ParseException { Name member; List paramTypes; - Log.DeferredDiagnosticHandler dh = new Log.DeferredDiagnosticHandler(fac.log); + Log.DeferredDiagnosticHandler dh = fac.log.new DeferredDiagnosticHandler(); try { int slash = sig.indexOf("/"); @@ -278,9 +278,10 @@ private List parseParams(String sig, int beginIndex, int endIndex, Log.D } private void checkDiags(Log.DeferredDiagnosticHandler h, int offset) throws ParseException { - JCDiagnostic d = h.getDiagnostics().peek(); - if (d != null) { - throw new ParseException(offset + ((int) d.getPosition()), "dc.ref.syntax.error"); + java.util.List diagnostics = h.getDiagnostics(); + if (!diagnostics.isEmpty()) { + int pos = offset + (int)diagnostics.get(0).getPosition(); + throw new ParseException(pos, "dc.ref.syntax.error"); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java index 8147ac275285d..7bc538a1d1e81 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java @@ -39,6 +39,7 @@ import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.util.*; +import javax.tools.Diagnostic; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; @@ -1004,7 +1005,7 @@ private Round(int number, Set treesToClean, Assert.checkNonNull(deferredDiagnosticHandler); this.deferredDiagnosticHandler = deferredDiagnosticHandler; } else { - this.deferredDiagnosticHandler = new Log.DeferredDiagnosticHandler(log); + this.deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); compiler.setDeferredDiagnosticHandler(this.deferredDiagnosticHandler); } @@ -1107,21 +1108,9 @@ boolean unrecoverableError() { if (messager.errorRaised()) return true; - for (JCDiagnostic d: deferredDiagnosticHandler.getDiagnostics()) { - switch (d.getKind()) { - case WARNING: - if (werror) - return true; - break; - - case ERROR: - if (fatalErrors || !d.isFlagSet(RECOVERABLE)) - return true; - break; - } - } - - return false; + return deferredDiagnosticHandler.getDiagnostics().stream() + .anyMatch(d -> (d.getKind() == Diagnostic.Kind.WARNING && werror) || + (d.getKind() == Diagnostic.Kind.ERROR && (fatalErrors || !d.isFlagSet(RECOVERABLE)))); } /** Find the set of annotations present in the set of top level diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index 56ee413ea0716..3a5671b66a80e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -26,12 +26,14 @@ package com.sun.tools.javac.util; import java.io.*; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.EnumMap; import java.util.HashSet; +import java.util.List; import java.util.Map; -import java.util.Queue; +import java.util.Optional; import java.util.Set; import java.util.function.Predicate; @@ -83,26 +85,26 @@ public String key(String k) { /** * DiagnosticHandler's provide the initial handling for diagnostics. * When a diagnostic handler is created and has been initialized, it - * should install itself as the current diagnostic handler. When a + * will install itself as the current diagnostic handler. When a * client has finished using a handler, the client should call * {@code log.removeDiagnosticHandler();} * * Note that javax.tools.DiagnosticListener (if set) is called later in the * diagnostic pipeline. */ - public abstract static class DiagnosticHandler { + public abstract class DiagnosticHandler { /** * The previously installed diagnostic handler. */ - protected DiagnosticHandler prev; + protected final DiagnosticHandler prev; /** * Install this diagnostic handler as the current one, * recording the previous one. */ - protected void install(Log log) { - prev = log.diagnosticHandler; - log.diagnosticHandler = this; + protected DiagnosticHandler() { + prev = diagnosticHandler; + diagnosticHandler = this; } /** @@ -114,11 +116,7 @@ protected void install(Log log) { /** * A DiagnosticHandler that discards all diagnostics. */ - public static class DiscardDiagnosticHandler extends DiagnosticHandler { - @SuppressWarnings("this-escape") - public DiscardDiagnosticHandler(Log log) { - install(log); - } + public class DiscardDiagnosticHandler extends DiagnosticHandler { @Override public void report(JCDiagnostic diag) { } @@ -131,39 +129,38 @@ public void report(JCDiagnostic diag) { } * with reportAllDiagnostics(), it will be reported to the previously * active diagnostic handler. */ - public static class DeferredDiagnosticHandler extends DiagnosticHandler { - private Queue deferred = new ListBuffer<>(); + public class DeferredDiagnosticHandler extends DiagnosticHandler { + private List deferred = new ArrayList<>(); private final Predicate filter; private final boolean passOnNonDeferrable; - public DeferredDiagnosticHandler(Log log) { - this(log, null); + public DeferredDiagnosticHandler() { + this(null); } - public DeferredDiagnosticHandler(Log log, Predicate filter) { - this(log, filter, true); + public DeferredDiagnosticHandler(Predicate filter) { + this(filter, true); } - @SuppressWarnings("this-escape") - public DeferredDiagnosticHandler(Log log, Predicate filter, boolean passOnNonDeferrable) { - this.filter = filter; + public DeferredDiagnosticHandler(Predicate filter, boolean passOnNonDeferrable) { + this.filter = Optional.ofNullable(filter).orElse(d -> true); this.passOnNonDeferrable = passOnNonDeferrable; - install(log); + } + + private boolean deferrable(JCDiagnostic diag) { + return !(diag.isFlagSet(DiagnosticFlag.NON_DEFERRABLE) && passOnNonDeferrable) && filter.test(diag); } @Override public void report(JCDiagnostic diag) { - boolean deferrable = !passOnNonDeferrable || - !diag.isFlagSet(JCDiagnostic.DiagnosticFlag.NON_DEFERRABLE); - if (deferrable && - (filter == null || filter.test(diag))) { + if (deferrable(diag)) { deferred.add(diag); } else { prev.report(diag); } } - public Queue getDiagnostics() { + public List getDiagnostics() { return deferred; } @@ -174,22 +171,18 @@ public void reportDeferredDiagnostics() { /** Report selected deferred diagnostics. */ public void reportDeferredDiagnostics(Predicate accepter) { - JCDiagnostic d; - while ((d = deferred.poll()) != null) { - if (accepter.test(d)) - prev.report(d); - } + + // Flush matching reports to the previous handler + deferred.stream() + .filter(accepter) + .forEach(prev::report); deferred = null; // prevent accidental ongoing use } - /** Report selected deferred diagnostics. */ + /** Report all deferred diagnostics in the specified order. */ public void reportDeferredDiagnostics(Comparator order) { - JCDiagnostic[] diags = deferred.toArray(s -> new JCDiagnostic[s]); - Arrays.sort(diags, order); - for (JCDiagnostic d : diags) { - prev.report(d); - } - deferred = null; // prevent accidental ongoing use + deferred.sort(order); + reportDeferredDiagnostics(); } } @@ -483,6 +476,7 @@ public void setWriters(PrintWriter pw) { */ public void popDiagnosticHandler(DiagnosticHandler h) { Assert.check(diagnosticHandler == h); + Assert.check(h.prev != null); diagnosticHandler = h.prev; } diff --git a/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java b/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java index c0e01f128b7f1..13c07ea32c0df 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -717,7 +717,7 @@ private void setVariableType(VarSnippet s) { //if it was not enhanced yet: //ignore any errors: JavaFileObject prev = log.useSource(null); - DiscardDiagnosticHandler h = new DiscardDiagnosticHandler(log); + DiscardDiagnosticHandler h = log.new DiscardDiagnosticHandler(); parserFactory.runPermitIntersectionTypes(() -> { try { //parse the type as a cast, i.e. "() x". This is to support From dc3b98c26854fa75df10abf81521c03deb2da94d Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Thu, 10 Apr 2025 09:26:13 -0500 Subject: [PATCH 17/44] Add comment per review suggestion. --- .../share/classes/com/sun/tools/javac/util/Log.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index 3a5671b66a80e..57d4e5f66baeb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -181,7 +181,7 @@ public void reportDeferredDiagnostics(Predicate accepter) { /** Report all deferred diagnostics in the specified order. */ public void reportDeferredDiagnostics(Comparator order) { - deferred.sort(order); + deferred.sort(order); // ok to sort in place: reportDeferredDiagnostics() is going to discard it reportDeferredDiagnostics(); } } From de2d3abe2663bd8eb6db56563e2def2e23565ce6 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Thu, 10 Apr 2025 14:07:13 -0500 Subject: [PATCH 18/44] Initial prototype for JDK-8348611. --- .../sun/tools/javac/api/JavacTaskImpl.java | 7 +- .../sun/tools/javac/api/JavacTaskPool.java | 4 +- .../com/sun/tools/javac/api/JavacTrees.java | 13 +- .../tools/javac/code/DeferredLintHandler.java | 176 -------- .../com/sun/tools/javac/code/Lint.java | 26 +- .../com/sun/tools/javac/code/Preview.java | 42 +- .../com/sun/tools/javac/comp/Annotate.java | 60 +-- .../com/sun/tools/javac/comp/Attr.java | 39 +- .../com/sun/tools/javac/comp/Check.java | 206 +++------ .../com/sun/tools/javac/comp/Flow.java | 323 ++++++--------- .../com/sun/tools/javac/comp/MemberEnter.java | 44 +- .../com/sun/tools/javac/comp/Modules.java | 42 +- .../com/sun/tools/javac/comp/TypeEnter.java | 27 +- .../sun/tools/javac/file/BaseFileManager.java | 2 +- .../com/sun/tools/javac/file/Locations.java | 25 +- .../com/sun/tools/javac/jvm/ClassReader.java | 17 +- .../tools/javac/launcher/MemoryContext.java | 23 +- .../com/sun/tools/javac/main/Arguments.java | 42 +- .../sun/tools/javac/main/JavaCompiler.java | 18 +- .../sun/tools/javac/parser/JavaTokenizer.java | 40 +- .../sun/tools/javac/parser/JavacParser.java | 60 +-- .../com/sun/tools/javac/parser/Lexer.java | 12 +- .../sun/tools/javac/parser/ParserFactory.java | 5 +- .../com/sun/tools/javac/parser/Scanner.java | 9 +- .../tools/javac/parser/ScannerFactory.java | 5 +- .../sun/tools/javac/parser/VirtualParser.java | 10 +- .../tools/javac/processing/JavacFiler.java | 27 +- .../JavacProcessingEnvironment.java | 29 +- .../com/sun/tools/javac/util/AbstractLog.java | 34 +- .../sun/tools/javac/util/JCDiagnostic.java | 37 +- .../com/sun/tools/javac/util/LintMapper.java | 390 ++++++++++++++++++ .../classes/com/sun/tools/javac/util/Log.java | 232 ++++++++++- ...r.java => MandatoryWarningAggregator.java} | 108 ++--- .../tools/javac/6304921/TestLog.java | 12 +- .../javac/ImplicitClass/ErrorRecovery.java | 2 - .../ImplicitClass/ImplicitClassRecovery.out | 2 - test/langtools/tools/javac/ImportModule.java | 2 +- .../OverrideChecks/6400189/T6400189a.out | 2 +- .../OverrideChecks/6400189/T6400189b.out | 2 +- .../examples/ImplicitClassBad-Filename.java | 2 - .../examples/ImplicitClassHasPackage.java | 2 - .../generics/diamond/7188968/T7188968.out | 6 +- .../tools/javac/lambda/TargetType22.out | 2 +- .../tools/javac/lint/LexicalLintNesting.java | 170 ++++++++ .../tools/javac/lint/LexicalLintNesting.out | 10 + .../tools/javac/lint/TextBlockSuppress.java | 61 +++ .../tools/javac/lint/TextBlockSuppress.out | 5 + .../mandatoryWarnings/deprecated/Test5.out | 2 +- .../mandatoryWarnings/deprecated/Test5b.out | 2 +- .../javac/modules/AnnotationsOnModules.java | 2 +- .../tools/javac/preview/PreviewErrors.java | 6 +- .../tools/javac/preview/PreviewTest.java | 97 ++++- .../model/util/printing/module-info.java | 4 +- .../tools/javac/varargs/7097436/T7097436.out | 4 +- .../javac/warnings/6594914/T6594914a.out | 2 +- .../tools/javac/warnings/7090499/T7090499.out | 8 +- .../UnneededStrictfpWarningToolBox.java | 2 +- .../javac/warnings/suppress/T6480588.out | 12 +- 58 files changed, 1466 insertions(+), 1089 deletions(-) delete mode 100644 src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredLintHandler.java create mode 100644 src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java rename src/jdk.compiler/share/classes/com/sun/tools/javac/util/{MandatoryWarningHandler.java => MandatoryWarningAggregator.java} (72%) create mode 100644 test/langtools/tools/javac/lint/LexicalLintNesting.java create mode 100644 test/langtools/tools/javac/lint/LexicalLintNesting.out create mode 100644 test/langtools/tools/javac/lint/TextBlockSuppress.java create mode 100644 test/langtools/tools/javac/lint/TextBlockSuppress.out diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java index 980ce060c3395..65ce640ef761f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -412,6 +412,7 @@ public void process(Env env) { f.run(compiler.todo, classes); } } finally { + compiler.log.reportOutstandingWarnings(); compiler.log.flush(); } return results; @@ -483,8 +484,10 @@ public void process(Env env) { } } finally { - if (compiler != null) + if (compiler != null) { + compiler.log.reportOutstandingWarnings(); compiler.log.flush(); + } } return results; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java index 87bc64c4e1976..fc91bc13a6bc5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java @@ -76,6 +76,7 @@ import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; +import com.sun.tools.javac.util.LintMapper; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Options; @@ -268,12 +269,11 @@ void clear() { if (ht.get(Log.logKey) instanceof ReusableLog) { //log already inited - not first round Log.instance(this).clear(); + LintMapper.instance(this).clear(); Enter.instance(this).newRound(); ((ReusableJavaCompiler)ReusableJavaCompiler.instance(this)).clear(); Types.instance(this).newRound(); Check.instance(this).newRound(); - Check.instance(this).clear(); //clear mandatory warning handlers - Preview.instance(this).clear(); //clear mandatory warning handlers Modules.instance(this).newRound(); Annotate.instance(this).newRound(); CompileStates.instance(this).clear(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java index 7bb473fc2b956..3bc45c6ffab98 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java @@ -360,9 +360,14 @@ public TypeMirror getType(DocTreePath path) { Log.DeferredDiagnosticHandler deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); try { Env env = getAttrContext(path.getTreePath()); - Type t = attr.attribType(dcReference.qualifierExpression, env); - if (t != null && !t.isErroneous()) { - return t; + JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); + try { + Type t = attr.attribType(dcReference.qualifierExpression, env); + if (t != null && !t.isErroneous()) { + return t; + } + } finally { + log.useSource(prevSource); } } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; @@ -388,6 +393,7 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { return null; } Log.DeferredDiagnosticHandler deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); + JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); try { final TypeSymbol tsym; final Name memberName; @@ -509,6 +515,7 @@ private Symbol attributeDocReference(TreePath path, DCReference ref) { } catch (Abort e) { // may be thrown by Check.completionError in case of bad class file return null; } finally { + log.useSource(prevSource); log.popDiagnosticHandler(deferredDiagnosticHandler); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredLintHandler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredLintHandler.java deleted file mode 100644 index 11544b92b7f6f..0000000000000 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/DeferredLintHandler.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2011, 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.code; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Optional; -import java.util.function.Consumer; - -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.Tag; -import com.sun.tools.javac.util.Assert; -import com.sun.tools.javac.util.Context; - -/** - * Holds pending {@link Lint} warnings until the {@lint Lint} instance associated with the containing - * module, package, class, method, or variable declaration is known so that {@link @SupressWarnings} - * suppressions may be applied. - * - *

- * Warnings are regsistered at any time prior to attribution via {@link #report}. The warning will be - * associated with the declaration placed in context by the most recent invocation of {@link #push push()} - * not yet {@link #pop}'d. Warnings are actually emitted later, during attribution, via {@link #flush}. - * - *

- * There is also an "immediate" mode, where warnings are emitted synchronously; see {@link #pushImmediate}. - * - *

- * Deferred warnings are grouped by the innermost containing module, package, class, method, or variable - * declaration (represented by {@link JCTree} nodes), so that the corresponding {@link Lint} configuration - * can be applied when the warning is eventually generated. - * - *

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. - */ -public class DeferredLintHandler { - - protected static final Context.Key deferredLintHandlerKey = new Context.Key<>(); - - public static DeferredLintHandler instance(Context context) { - DeferredLintHandler instance = context.get(deferredLintHandlerKey); - if (instance == null) - instance = new DeferredLintHandler(context); - return instance; - } - - /** - * Registered {@link LintLogger}s grouped by the innermost containing module, package, class, - * method, or variable declaration. - */ - private final HashMap> deferralMap = new HashMap<>(); - - /** - * The current "reporter" stack, reflecting calls to {@link #push} and {@link #pop}. - * - *

- * The top of the stack determines how calls to {@link #report} are handled. - */ - private final ArrayDeque> reporterStack = new ArrayDeque<>(); - - @SuppressWarnings("this-escape") - protected DeferredLintHandler(Context context) { - context.put(deferredLintHandlerKey, this); - Lint rootLint = Lint.instance(context); - pushImmediate(rootLint); // default to "immediate" mode - } - -// LintLogger - - /**An interface for deferred lint reporting - loggers passed to - * {@link #report(LintLogger) } will be called when - * {@link #flush(DiagnosticPosition) } is invoked. - */ - public interface LintLogger { - - /** - * Generate a warning if appropriate. - * - * @param lint the applicable lint configuration - */ - void report(Lint lint); - } - -// Reporter Stack - - /** - * Defer {@link #report}ed warnings until the given declaration is flushed. - * - * @param decl module, package, class, method, or variable declaration - * @see #pop - */ - public void push(JCTree decl) { - Assert.check(decl.getTag() == Tag.MODULEDEF - || decl.getTag() == Tag.PACKAGEDEF - || decl.getTag() == Tag.CLASSDEF - || decl.getTag() == Tag.METHODDEF - || decl.getTag() == Tag.VARDEF); - reporterStack.push(logger -> deferralMap - .computeIfAbsent(decl, s -> new ArrayList<>()) - .add(logger)); - } - - /** - * Enter "immediate" mode so that {@link #report}ed warnings are emitted synchonously. - * - * @param lint lint configuration to use for reported warnings - */ - public void pushImmediate(Lint lint) { - reporterStack.push(logger -> logger.report(lint)); - } - - /** - * Revert to the previous configuration in effect prior to the most recent invocation - * of {@link #push} or {@link #pushImmediate}. - * - * @see #pop - */ - public void pop() { - Assert.check(reporterStack.size() > 1); // the bottom stack entry should never be popped - reporterStack.pop(); - } - - /** - * Report a warning. - * - *

- * In immediate mode, the warning is emitted synchronously. Otherwise, the warning is emitted later - * when the current declaration is flushed. - */ - public void report(LintLogger logger) { - Assert.check(!reporterStack.isEmpty()); - reporterStack.peek().accept(logger); - } - -// Warning Flush - - /** - * Emit deferred warnings encompassed by the given declaration. - * - * @param decl module, package, class, method, or variable declaration - * @param lint lint configuration corresponding to {@code decl} - */ - public void flush(JCTree decl, Lint lint) { - Optional.of(decl) - .map(deferralMap::remove) - .stream() - .flatMap(ArrayList::stream) - .forEach(logger -> logger.report(lint)); - } -} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index fb1b782d87e39..27df573fe4f91 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -370,11 +370,8 @@ public enum LintCategory { /** * Warn about issues relating to use of text blocks - * - *

- * This category is not supported by {@code @SuppressWarnings} (yet - see JDK-8224228). */ - TEXT_BLOCKS("text-blocks", false), + TEXT_BLOCKS("text-blocks"), /** * Warn about possible 'this' escapes before subclass instance is fully initialized. @@ -458,27 +455,6 @@ public boolean isSuppressed(LintCategory lc) { return suppressedValues.contains(lc); } - /** - * Helper method. Log a lint warning if its lint category is enabled. - * - * @param warning key for the localized warning message - */ - public void logIfEnabled(LintWarning warning) { - logIfEnabled(null, warning); - } - - /** - * Helper method. Log a lint warning if its lint category is enabled. - * - * @param pos source position at which to report the warning - * @param warning key for the localized warning message - */ - public void logIfEnabled(DiagnosticPosition pos, LintWarning warning) { - if (isEnabled(warning.getLintCategory())) { - log.warning(pos, warning); - } - } - /** * Obtain the set of recognized lint warning categories suppressed at the given symbol's declaration. * diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java index 9d7b1e6535245..7927930a526b6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java @@ -34,13 +34,13 @@ import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Error; import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Warning; import com.sun.tools.javac.util.Log; -import com.sun.tools.javac.util.MandatoryWarningHandler; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Options; @@ -68,12 +68,6 @@ public class Preview { /** flag: are preview features enabled */ private final boolean enabled; - /** flag: is the "preview" lint category enabled? */ - private final boolean verbose; - - /** the diag handler to manage preview feature usage diagnostics */ - private final MandatoryWarningHandler previewHandler; - /** test flag: should all features be considered as preview features? */ private final boolean forcePreview; @@ -104,8 +98,6 @@ protected Preview(Context context) { enabled = options.isSet(PREVIEW); log = Log.instance(context); source = Source.instance(context); - verbose = Lint.instance(context).isEnabled(LintCategory.PREVIEW); - previewHandler = new MandatoryWarningHandler(log, source, verbose, true, LintCategory.PREVIEW); forcePreview = options.isSet("forcePreview"); majorVersionToSource = initMajorVersionToSourceMap(); } @@ -178,9 +170,11 @@ public void warnPreview(DiagnosticPosition pos, Feature feature) { Assert.check(isEnabled()); Assert.check(isPreview(feature)); markUsesPreview(pos); - previewHandler.report(pos, feature.isPlural() ? + log.mandatoryWarning(pos, + feature.isPlural() ? LintWarnings.PreviewFeatureUsePlural(feature.nameFragment()) : - LintWarnings.PreviewFeatureUse(feature.nameFragment())); + LintWarnings.PreviewFeatureUse(feature.nameFragment()), + DiagnosticFlag.AGGREGATE); } /** @@ -190,10 +184,8 @@ public void warnPreview(DiagnosticPosition pos, Feature feature) { */ public void warnPreview(JavaFileObject classfile, int majorVersion) { Assert.check(isEnabled()); - if (verbose) { - log.mandatoryWarning(null, - LintWarnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name)); - } + log.warning(LintWarnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name), + DiagnosticFlag.MANDATORY); // make it mandatory but don't include DiagnosticFlag.DEFAULT_ENABLED } /** @@ -205,10 +197,6 @@ public void markUsesPreview(DiagnosticPosition pos) { sourcesWithPreviewFeatures.add(log.currentSourceFile()); } - public void reportPreviewWarning(DiagnosticPosition pos, LintWarning warnKey) { - previewHandler.report(pos, warnKey); - } - public boolean usesPreview(JavaFileObject file) { return sourcesWithPreviewFeatures.contains(file); } @@ -275,25 +263,13 @@ public boolean declaredUsingPreviewFeature(Symbol sym) { return false; } - /** - * Report any deferred diagnostics. - */ - public void reportDeferredDiagnostics() { - previewHandler.reportDeferredDiagnostic(); - } - - public void clear() { - previewHandler.clear(); - } - public void checkSourceLevel(DiagnosticPosition pos, Feature feature) { if (isPreview(feature) && !isEnabled()) { //preview feature without --preview flag, error - log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, disabledError(feature)); + log.error(DiagnosticFlag.SOURCE_LEVEL, pos, disabledError(feature)); } else { if (!feature.allowedInSource(source)) { - log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, - feature.error(source.name)); + log.error(DiagnosticFlag.SOURCE_LEVEL, pos, feature.error(source.name)); } if (isEnabled() && isPreview(feature)) { warnPreview(pos, feature); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index 49f5a7472aa74..73c4a95cf5f3d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -89,9 +89,7 @@ public static Annotate instance(Context context) { private final Attr attr; private final Check chk; private final ConstFold cfolder; - private final DeferredLintHandler deferredLintHandler; private final Enter enter; - private final Lint lint; private final Log log; private final Names names; private final Resolve resolve; @@ -110,10 +108,8 @@ protected Annotate(Context context) { attr = Attr.instance(context); chk = Check.instance(context); cfolder = ConstFold.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); enter = Enter.instance(context); log = Log.instance(context); - lint = Lint.instance(context); make = TreeMaker.instance(context); names = Names.instance(context); resolve = Resolve.instance(context); @@ -230,10 +226,8 @@ public void afterTypes(Runnable a) { * @param annotations the list of JCAnnotations to attribute and enter * @param localEnv the enclosing env * @param s the Symbol on which to enter the annotations - * @param deferDecl enclosing declaration for DeferredLintHandler, or null for no deferral */ - public void annotateLater(List annotations, Env localEnv, - Symbol s, JCTree deferDecl) + public void annotateLater(List annotations, Env localEnv, Symbol s) { if (annotations.isEmpty()) { return; @@ -251,8 +245,6 @@ public void annotateLater(List annotations, Env local // been handled, meaning that the set of annotations pending completion is now empty. Assert.check(s.kind == PCK || s.annotationsPendingCompletion()); JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile); - Assert.check(deferDecl != null); - deferredLintHandler.push(deferDecl); try { if (s.hasAnnotations() && annotations.nonEmpty()) log.error(annotations.head.pos, Errors.AlreadyAnnotated(Kinds.kindName(s), s)); @@ -263,7 +255,6 @@ public void annotateLater(List annotations, Env local // never called for a type parameter annotateNow(s, annotations, localEnv, false, false); } finally { - deferredLintHandler.pop(); log.useSource(prev); } }); @@ -280,16 +271,13 @@ public void annotateLater(List annotations, Env local /** Queue processing of an attribute default value. */ - public void annotateDefaultValueLater(JCExpression defaultValue, Env localEnv, - MethodSymbol m, JCTree deferDecl) + public void annotateDefaultValueLater(JCExpression defaultValue, Env localEnv, MethodSymbol m) { normal(() -> { JavaFileObject prev = log.useSource(localEnv.toplevel.sourcefile); - deferredLintHandler.push(deferDecl); try { enterDefaultValue(defaultValue, localEnv, m); } finally { - deferredLintHandler.pop(); log.useSource(prev); } }); @@ -671,7 +659,7 @@ private Attribute getAnnotationPrimitiveValue(Type expectedElementType, JCExpres // Scan the annotation element value and then attribute nested annotations if present if (tree.type != null && tree.type.tsym != null) { - queueScanTreeAndTypeAnnotate(tree, env, tree.type.tsym, null); + queueScanTreeAndTypeAnnotate(tree, env, tree.type.tsym); } result = cfolder.coerce(result, expectedElementType); @@ -1023,20 +1011,14 @@ private T makeContainerAnnotation(List toBeRep /** * Attribute the list of annotations and enter them onto s. */ - public void enterTypeAnnotations(List annotations, Env env, - Symbol s, JCTree deferDecl, boolean isTypeParam) + public void enterTypeAnnotations(List annotations, Env env, Symbol s, boolean isTypeParam) { Assert.checkNonNull(s, "Symbol argument to actualEnterTypeAnnotations is nul/"); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); - if (deferDecl != null) { - deferredLintHandler.push(deferDecl); - } try { annotateNow(s, annotations, env, true, isTypeParam); } finally { - if (deferDecl != null) - deferredLintHandler.pop(); log.useSource(prev); } } @@ -1044,10 +1026,10 @@ public void enterTypeAnnotations(List annotations, Env env, Symbol sym, JCTree deferDecl) + public void queueScanTreeAndTypeAnnotate(JCTree tree, Env env, Symbol sym) { Assert.checkNonNull(sym); - normal(() -> tree.accept(new TypeAnnotate(env, sym, deferDecl))); + normal(() -> tree.accept(new TypeAnnotate(env, sym))); } /** @@ -1082,32 +1064,30 @@ public void annotateTypeParameterSecondStage(JCTree tree, List ann private class TypeAnnotate extends TreeScanner { private final Env env; private final Symbol sym; - private JCTree deferDecl; - public TypeAnnotate(Env env, Symbol sym, JCTree deferDecl) { + public TypeAnnotate(Env env, Symbol sym) { this.env = env; this.sym = sym; - this.deferDecl = deferDecl; } @Override public void visitAnnotatedType(JCAnnotatedType tree) { - enterTypeAnnotations(tree.annotations, env, sym, deferDecl, false); + enterTypeAnnotations(tree.annotations, env, sym, false); scan(tree.underlyingType); } @Override public void visitTypeParameter(JCTypeParameter tree) { - enterTypeAnnotations(tree.annotations, env, sym, deferDecl, true); + enterTypeAnnotations(tree.annotations, env, sym, true); scan(tree.bounds); } @Override public void visitNewArray(JCNewArray tree) { - enterTypeAnnotations(tree.annotations, env, sym, deferDecl, false); + enterTypeAnnotations(tree.annotations, env, sym, false); for (List dimAnnos : tree.dimAnnotations) - enterTypeAnnotations(dimAnnos, env, sym, deferDecl, false); + enterTypeAnnotations(dimAnnos, env, sym, false); scan(tree.elemtype); scan(tree.elems); } @@ -1126,19 +1106,13 @@ public void visitMethodDef(JCMethodDecl tree) { @Override public void visitVarDef(JCVariableDecl tree) { - JCTree prevDecl = deferDecl; - deferDecl = tree; - try { - if (sym != null && sym.kind == VAR) { - // Don't visit a parameter once when the sym is the method - // and once when the sym is the parameter. - scan(tree.mods); - scan(tree.vartype); - } - scan(tree.init); - } finally { - deferDecl = prevDecl; + if (sym != null && sym.kind == VAR) { + // Don't visit a parameter once when the sym is the method + // and once when the sym is the parameter. + scan(tree.mods); + scan(tree.vartype); } + scan(tree.init); } @Override diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index c15e4cc554112..400466f2d52e4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -69,6 +69,7 @@ import com.sun.tools.javac.util.JCDiagnostic.Error; import com.sun.tools.javac.util.JCDiagnostic.Fragment; import com.sun.tools.javac.util.JCDiagnostic.Warning; +import com.sun.tools.javac.util.LintMapper; import com.sun.tools.javac.util.List; import static com.sun.tools.javac.code.Flags.*; @@ -99,6 +100,7 @@ public class Attr extends JCTree.Visitor { final Names names; final Log log; + final LintMapper lintMapper; final Symtab syms; final Resolve rs; final Operators operators; @@ -117,7 +119,6 @@ public class Attr extends JCTree.Visitor { final Preview preview; final JCDiagnostic.Factory diags; final TypeAnnotations typeAnnotations; - final DeferredLintHandler deferredLintHandler; final TypeEnvs typeEnvs; final Dependencies dependencies; final Annotate annotate; @@ -138,6 +139,7 @@ protected Attr(Context context) { names = Names.instance(context); log = Log.instance(context); + lintMapper = LintMapper.instance(context); syms = Symtab.instance(context); rs = Resolve.instance(context); operators = Operators.instance(context); @@ -157,7 +159,6 @@ protected Attr(Context context) { diags = JCDiagnostic.Factory.instance(context); annotate = Annotate.instance(context); typeAnnotations = TypeAnnotations.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); argumentAttr = ArgumentAttr.instance(context); @@ -854,7 +855,6 @@ public Object attribLazyConstantValue(Env env, Env enclosingEnv, JCVariableDecl variable, Type type) { - deferredLintHandler.push(variable); final JavaFileObject prevSource = log.useSource(env.toplevel.sourcefile); try { doQueueScanTreeAndTypeAnnotateForVarInit(variable, enclosingEnv); @@ -870,7 +870,6 @@ public Object attribLazyConstantValue(Env env, } } finally { log.useSource(prevSource); - deferredLintHandler.pop(); } } @@ -999,7 +998,6 @@ public void visitMethodDef(JCMethodDecl tree) { Assert.check(!env.info.ctorPrologue); MethodSymbol prevMethod = chk.setMethod(m); try { - deferredLintHandler.flush(tree, lint); chk.checkDeprecatedAnnotation(tree.pos(), m); @@ -1232,7 +1230,7 @@ public void visitMethodDef(JCMethodDecl tree) { } // Attribute all type annotations in the body - annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m, null); + annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m); annotate.flush(); // Start of constructor prologue @@ -1296,7 +1294,6 @@ public void visitVarDef(JCVariableDecl tree) { try { v.getConstValue(); // ensure compile-time constant initializer is evaluated - deferredLintHandler.flush(tree, lint); chk.checkDeprecatedAnnotation(tree.pos(), v); if (tree.init != null) { @@ -1340,7 +1337,7 @@ private void doQueueScanTreeAndTypeAnnotateForVarInit(JCVariableDecl tree, Env env, Type resou if (close.kind == MTH && close.overrides(syms.autoCloseableClose, resource.tsym, types, true) && chk.isHandled(syms.interruptedExceptionType, types.memberType(resource, close).getThrownTypes())) { - env.info.lint.logIfEnabled(pos, LintWarnings.TryResourceThrowsInterruptedExc(resource)); + log.warning(pos, LintWarnings.TryResourceThrowsInterruptedExc(resource)); } } } @@ -4214,9 +4211,9 @@ public void visitBindingPattern(JCBindingPattern tree) { setSyntheticVariableType(tree.var, type == Type.noType ? syms.errType : type); } - annotate.annotateLater(tree.var.mods.annotations, env, v, tree.var); + annotate.annotateLater(tree.var.mods.annotations, env, v); if (!tree.var.isImplicitlyTyped()) { - annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, env, v, tree.var); + annotate.queueScanTreeAndTypeAnnotate(tree.var.vartype, env, v); } annotate.flush(); result = tree.type; @@ -4453,7 +4450,7 @@ public void visitSelect(JCFieldAccess tree) { sym.kind == MTH && sym.name.equals(names.close) && sym.overrides(syms.autoCloseableClose, sitesym.type.tsym, types, true)) { - env.info.lint.logIfEnabled(tree, LintWarnings.TryExplicitCloseCall); + log.warning(tree, LintWarnings.TryExplicitCloseCall); } // Disallow selecting a type from an expression @@ -4480,9 +4477,9 @@ public void visitSelect(JCFieldAccess tree) { // If the qualified item is not a type and the selected item is static, report // a warning. Make allowance for the class of an array type e.g. Object[].class) if (!sym.owner.isAnonymous()) { - chk.lint.logIfEnabled(tree, LintWarnings.StaticNotQualifiedByType(sym.kind.kindName(), sym.owner)); + log.warning(tree, LintWarnings.StaticNotQualifiedByType(sym.kind.kindName(), sym.owner)); } else { - chk.lint.logIfEnabled(tree, LintWarnings.StaticNotQualifiedByType2(sym.kind.kindName())); + log.warning(tree, LintWarnings.StaticNotQualifiedByType2(sym.kind.kindName())); } } @@ -5287,6 +5284,8 @@ public void attrib(Env env) { } annotate.flush(); + + lintMapper.calculateLints(env.toplevel.sourcefile, env.tree, env.toplevel.endPositions); } public void attribPackage(DiagnosticPosition pos, PackageSymbol p) { @@ -5329,7 +5328,6 @@ private void attribWithLint(TypeSymbol sym, Consumer> attrib) { JavaFileObject prev = log.useSource(env.toplevel.sourcefile); try { - deferredLintHandler.flush(env.tree, lint); attrib.accept(env); } finally { log.useSource(prev); @@ -5508,7 +5506,6 @@ void attribClass(ClassSymbol c) throws CompletionFailure { } } - deferredLintHandler.flush(env.tree, env.info.lint); env.info.returnResult = null; // java.lang.Enum may not be subclassed by a non-enum if (st.tsym == syms.enumSym && @@ -5554,11 +5551,9 @@ public void visitModuleDef(JCModuleDecl tree) { ModuleSymbol msym = tree.sym; Lint lint = env.outer.info.lint = env.outer.info.lint.augment(msym); Lint prevLint = chk.setLint(lint); - chk.checkModuleName(tree); - chk.checkDeprecatedAnnotation(tree, msym); - try { - deferredLintHandler.flush(tree, lint); + chk.checkModuleName(tree); + chk.checkDeprecatedAnnotation(tree, msym); } finally { chk.setLint(prevLint); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 01449b7f7faf6..016fc39eabed9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -162,20 +162,6 @@ protected Check(Context context) { profile = Profile.instance(context); preview = Preview.instance(context); - boolean verboseDeprecated = lint.isEnabled(LintCategory.DEPRECATION); - boolean verboseRemoval = lint.isEnabled(LintCategory.REMOVAL); - boolean verboseUnchecked = lint.isEnabled(LintCategory.UNCHECKED); - boolean enforceMandatoryWarnings = true; - - deprecationHandler = new MandatoryWarningHandler(log, null, verboseDeprecated, - enforceMandatoryWarnings, LintCategory.DEPRECATION, "deprecated"); - removalHandler = new MandatoryWarningHandler(log, null, verboseRemoval, - enforceMandatoryWarnings, LintCategory.REMOVAL); - uncheckedHandler = new MandatoryWarningHandler(log, null, verboseUnchecked, - enforceMandatoryWarnings, LintCategory.UNCHECKED); - - deferredLintHandler = DeferredLintHandler.instance(context); - allowModules = Feature.MODULES.allowedInSource(source); allowRecords = Feature.RECORDS.allowedInSource(source); allowSealed = Feature.SEALED_CLASSES.allowedInSource(source); @@ -190,22 +176,6 @@ protected Check(Context context) { */ private Map,ClassSymbol> compiled = new HashMap<>(); - /** A handler for messages about deprecated usage. - */ - private MandatoryWarningHandler deprecationHandler; - - /** A handler for messages about deprecated-for-removal usage. - */ - private MandatoryWarningHandler removalHandler; - - /** A handler for messages about unchecked or unsafe usage. - */ - private MandatoryWarningHandler uncheckedHandler; - - /** A handler for deferred lint warnings. - */ - private DeferredLintHandler deferredLintHandler; - /** Are modules allowed */ private final boolean allowModules; @@ -251,21 +221,15 @@ MethodSymbol setMethod(MethodSymbol newMethod) { * @param sym The deprecated symbol. */ void warnDeprecated(DiagnosticPosition pos, Symbol sym) { - if (sym.isDeprecatedForRemoval()) { - if (!lint.isSuppressed(LintCategory.REMOVAL)) { - if (sym.kind == MDL) { - removalHandler.report(pos, LintWarnings.HasBeenDeprecatedForRemovalModule(sym)); - } else { - removalHandler.report(pos, LintWarnings.HasBeenDeprecatedForRemoval(sym, sym.location())); - } - } - } else if (!lint.isSuppressed(LintCategory.DEPRECATION)) { - if (sym.kind == MDL) { - deprecationHandler.report(pos, LintWarnings.HasBeenDeprecatedModule(sym)); - } else { - deprecationHandler.report(pos, LintWarnings.HasBeenDeprecated(sym, sym.location())); - } - } + Assert.check(!importSuppression); + LintWarning warningKey = sym.isDeprecatedForRemoval() ? + (sym.kind == MDL ? + LintWarnings.HasBeenDeprecatedForRemovalModule(sym) : + LintWarnings.HasBeenDeprecatedForRemoval(sym, sym.location())) : + (sym.kind == MDL ? + LintWarnings.HasBeenDeprecatedModule(sym) : + LintWarnings.HasBeenDeprecated(sym, sym.location())); + log.mandatoryWarning(pos, warningKey, DiagnosticFlag.AGGREGATE); } /** Log a preview warning. @@ -273,16 +237,9 @@ void warnDeprecated(DiagnosticPosition pos, Symbol sym) { * @param msg A Warning describing the problem. */ public void warnPreviewAPI(DiagnosticPosition pos, LintWarning warnKey) { - if (!importSuppression && !lint.isSuppressed(LintCategory.PREVIEW)) - preview.reportPreviewWarning(pos, warnKey); - } - - /** Log a preview warning. - * @param pos Position to be used for error reporting. - * @param msg A Warning describing the problem. - */ - public void warnRestrictedAPI(DiagnosticPosition pos, Symbol sym) { - lint.logIfEnabled(pos, LintWarnings.RestrictedMethod(sym.enclClass(), sym)); + if (!importSuppression) { + log.mandatoryWarning(pos, warnKey, DiagnosticFlag.AGGREGATE); + } } /** Warn about unchecked operation. @@ -290,26 +247,15 @@ public void warnRestrictedAPI(DiagnosticPosition pos, Symbol sym) { * @param msg A string describing the problem. */ public void warnUnchecked(DiagnosticPosition pos, LintWarning warnKey) { - if (!lint.isSuppressed(LintCategory.UNCHECKED)) - uncheckedHandler.report(pos, warnKey); + log.mandatoryWarning(pos, warnKey, DiagnosticFlag.AGGREGATE); } - /** - * Report any deferred diagnostics. - */ - public void reportDeferredDiagnostics() { - deprecationHandler.reportDeferredDiagnostic(); - removalHandler.reportDeferredDiagnostic(); - uncheckedHandler.reportDeferredDiagnostic(); - } - - /** Report a failure to complete a class. * @param pos Position to be used for error reporting. * @param ex The failure to report. */ public Type completionError(DiagnosticPosition pos, CompletionFailure ex) { - log.error(JCDiagnostic.DiagnosticFlag.NON_DEFERRABLE, pos, Errors.CantAccess(ex.sym, ex.getDetailValue())); + log.error(DiagnosticFlag.NON_DEFERRABLE, pos, Errors.CantAccess(ex.sym, ex.getDetailValue())); return syms.errType; } @@ -472,12 +418,6 @@ public void newRound() { localClassNameIndexes.clear(); } - public void clear() { - deprecationHandler.clear(); - removalHandler.clear(); - uncheckedHandler.clear(); - } - public void putCompiled(ClassSymbol csym) { compiled.put(Pair.of(csym.packge().modle, csym.flatname), csym); } @@ -643,9 +583,7 @@ public void checkRedundantCast(Env env, final JCTypeCast tree) { && types.isSameType(tree.expr.type, tree.clazz.type) && !(ignoreAnnotatedCasts && TreeInfo.containsTypeAnnotation(tree.clazz)) && !is292targetTypeCast(tree)) { - deferredLintHandler.report(_l -> { - lint.logIfEnabled(tree.pos(), LintWarnings.RedundantCast(tree.clazz.type)); - }); + log.warning(tree.pos(), LintWarnings.RedundantCast(tree.clazz.type)); } } //where @@ -949,7 +887,7 @@ void checkVarargsMethodDecl(Env env, JCMethodDecl tree) { } } else if (hasTrustMeAnno && varargElemType != null && types.isReifiable(varargElemType)) { - lint.logIfEnabled(tree, LintWarnings.VarargsRedundantTrustmeAnno( + log.warning(tree.pos(), LintWarnings.VarargsRedundantTrustmeAnno( syms.trustMeType.tsym, diags.fragment(Fragments.VarargsTrustmeOnReifiableVarargs(varargElemType)))); } @@ -1208,7 +1146,7 @@ else if ((sym.owner.flags_field & INTERFACE) != 0) mask = MethodFlags; } if ((flags & STRICTFP) != 0) { - warnOnExplicitStrictfp(tree); + log.warning(tree.pos(), LintWarnings.Strictfp); } // Imply STRICTFP if owner has STRICTFP set. if (((flags|implicit) & Flags.ABSTRACT) == 0 || @@ -1252,7 +1190,7 @@ else if ((sym.owner.flags_field & INTERFACE) != 0) implicit |= FINAL; } if ((flags & STRICTFP) != 0) { - warnOnExplicitStrictfp(tree); + log.warning(tree.pos(), LintWarnings.Strictfp); } // Imply STRICTFP if owner has STRICTFP set. implicit |= sym.owner.flags_field & STRICTFP; @@ -1316,16 +1254,6 @@ && checkDisjoint(pos, flags, return flags & (mask | ~ExtendedStandardFlags) | implicit; } - private void warnOnExplicitStrictfp(JCTree tree) { - deferredLintHandler.push(tree); - try { - deferredLintHandler.report(_ -> lint.logIfEnabled(tree.pos(), LintWarnings.Strictfp)); - } finally { - deferredLintHandler.pop(); - } - } - - /** Determine if this enum should be implicitly final. * * If the enum has no specialized enum constants, it is final. @@ -1538,7 +1466,7 @@ void checkRaw(JCTree tree, Env env) { !TreeInfo.isDiamond(tree) && !withinAnonConstr(env) && tree.type.isRaw()) { - lint.logIfEnabled(tree.pos(), LintWarnings.RawClassUse(tree.type, tree.type.tsym.type)); + log.warning(tree.pos(), LintWarnings.RawClassUse(tree.type, tree.type.tsym.type)); } } //where @@ -1862,7 +1790,7 @@ else if (unhandledUnerased.nonEmpty()) { // Optional warning if varargs don't agree if ((((m.flags() ^ other.flags()) & Flags.VARARGS) != 0)) { - lint.logIfEnabled(TreeInfo.diagnosticPositionFor(m, tree), + log.warning(TreeInfo.diagnosticPositionFor(m, tree), ((m.flags() & Flags.VARARGS) != 0) ? LintWarnings.OverrideVarargsMissing(varargsOverrides(m, other)) : LintWarnings.OverrideVarargsExtra(varargsOverrides(m, other))); @@ -2943,42 +2871,34 @@ boolean potentiallyAmbiguousOverload(Type site, MethodSymbol msym1, MethodSymbol // Apply special flag "-XDwarnOnAccessToMembers" which turns on just this particular warning for all types of access void checkAccessFromSerializableElement(final JCTree tree, boolean isLambda) { - final Lint prevLint = setLint(warnOnAnyAccessToMembers ? lint.enable(LintCategory.SERIAL) : lint); - try { - if (warnOnAnyAccessToMembers || isLambda) - checkAccessFromSerializableElementInner(tree, isLambda); - } finally { - setLint(prevLint); - } + if (warnOnAnyAccessToMembers || isLambda) + checkAccessFromSerializableElementInner(tree, isLambda); } private void checkAccessFromSerializableElementInner(final JCTree tree, boolean isLambda) { - if (lint.isEnabled(LintCategory.SERIAL)) { - Symbol sym = TreeInfo.symbol(tree); - if (!sym.kind.matches(KindSelector.VAL_MTH)) { - return; - } + Symbol sym = TreeInfo.symbol(tree); + if (!sym.kind.matches(KindSelector.VAL_MTH)) { + return; + } - if (sym.kind == VAR) { - if ((sym.flags() & PARAMETER) != 0 || - sym.isDirectlyOrIndirectlyLocal() || - sym.name == names._this || - sym.name == names._super) { - return; - } + if (sym.kind == VAR) { + if ((sym.flags() & PARAMETER) != 0 || + sym.isDirectlyOrIndirectlyLocal() || + sym.name == names._this || + sym.name == names._super) { + return; } + } - if (!types.isSubtype(sym.owner.type, syms.serializableType) && - isEffectivelyNonPublic(sym)) { - if (isLambda) { - if (belongsToRestrictedPackage(sym)) { - log.warning(tree.pos(), - LintWarnings.AccessToMemberFromSerializableLambda(sym)); - } - } else { - log.warning(tree.pos(), - LintWarnings.AccessToMemberFromSerializableElement(sym)); + if (!types.isSubtype(sym.owner.type, syms.serializableType) && isEffectivelyNonPublic(sym)) { + DiagnosticFlag[] flags = warnOnAnyAccessToMembers ? + new DiagnosticFlag[] { DiagnosticFlag.DEFAULT_ENABLED } : new DiagnosticFlag[0]; + if (isLambda) { + if (belongsToRestrictedPackage(sym)) { + log.warning(tree.pos(), LintWarnings.AccessToMemberFromSerializableLambda(sym), flags); } + } else { + log.warning(tree.pos(), LintWarnings.AccessToMemberFromSerializableElement(sym), flags); } } } @@ -3765,8 +3685,7 @@ void checkDeprecatedAnnotation(DiagnosticPosition pos, Symbol s) { // Note: @Deprecated has no effect on local variables, parameters and package decls. if (lint.isEnabled(LintCategory.DEPRECATION) && !s.isDeprecatableViaAnnotation()) { if (!syms.deprecatedType.isErroneous() && s.attribute(syms.deprecatedType.tsym) != null) { - log.warning(pos, - LintWarnings.DeprecatedAnnotationHasNoEffect(Kinds.kindName(s))); + log.warning(pos, LintWarnings.DeprecatedAnnotationHasNoEffect(Kinds.kindName(s))); } } } @@ -3780,15 +3699,13 @@ void checkDeprecated(Supplier pos, final Symbol other, final && (s.isDeprecatedForRemoval() || s.isDeprecated() && !other.isDeprecated()) && (s.outermostClass() != other.outermostClass() || s.outermostClass() == null) && s.kind != Kind.PCK) { - deferredLintHandler.report(_l -> warnDeprecated(pos.get(), s)); + warnDeprecated(pos.get(), s); } } void checkSunAPI(final DiagnosticPosition pos, final Symbol s) { if ((s.flags() & PROPRIETARY) != 0) { - deferredLintHandler.report(_l -> { - log.mandatoryWarning(pos, Warnings.SunProprietary(s)); - }); + log.mandatoryWarning(pos, Warnings.SunProprietary(s)); } } @@ -3845,7 +3762,7 @@ void checkPreview(DiagnosticPosition pos, Symbol other, Type site, Symbol s) { void checkRestricted(DiagnosticPosition pos, Symbol s) { if (s.kind == MTH && (s.flags() & RESTRICTED) != 0) { - deferredLintHandler.report(_l -> warnRestrictedAPI(pos, s)); + log.warning(pos, LintWarnings.RestrictedMethod(s.enclClass(), s)); } } @@ -4117,7 +4034,7 @@ void checkDivZero(final DiagnosticPosition pos, Symbol operator, Type operand) { int opc = ((OperatorSymbol)operator).opcode; if (opc == ByteCodes.idiv || opc == ByteCodes.imod || opc == ByteCodes.ldiv || opc == ByteCodes.lmod) { - deferredLintHandler.report(_ -> lint.logIfEnabled(pos, LintWarnings.DivZero)); + log.warning(pos, LintWarnings.DivZero); } } } @@ -4130,8 +4047,7 @@ void checkDivZero(final DiagnosticPosition pos, Symbol operator, Type operand) { */ void checkLossOfPrecision(final DiagnosticPosition pos, Type found, Type req) { if (found.isNumeric() && req.isNumeric() && !types.isAssignable(found, req)) { - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.PossibleLossOfPrecision(found, req))); + log.warning(pos, LintWarnings.PossibleLossOfPrecision(found, req)); } } @@ -4140,7 +4056,7 @@ void checkLossOfPrecision(final DiagnosticPosition pos, Type found, Type req) { */ void checkEmptyIf(JCIf tree) { if (tree.thenpart.hasTag(SKIP) && tree.elsepart == null) { - lint.logIfEnabled(tree.thenpart.pos(), LintWarnings.EmptyIf); + log.warning(tree.thenpart.pos(), LintWarnings.EmptyIf); } } @@ -4287,8 +4203,7 @@ void checkForBadAuxiliaryClassAccess(DiagnosticPosition pos, Env en rs.isAccessible(env, c) && !fileManager.isSameFile(c.sourcefile, env.toplevel.sourcefile)) { - lint.logIfEnabled(pos, - LintWarnings.AuxiliaryClassAccessedFromOutsideOfItsSourceFile(c, c.sourcefile)); + log.warning(pos, LintWarnings.AuxiliaryClassAccessedFromOutsideOfItsSourceFile(c, c.sourcefile)); } } @@ -4330,8 +4245,7 @@ void checkDefaultConstructor(ClassSymbol c, DiagnosticPosition pos) { // Warning may be suppressed by // annotations; check again for being // enabled in the deferred context. - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.MissingExplicitCtor(c, pkg, modle))); + log.warning(pos, LintWarnings.MissingExplicitCtor(c, pkg, modle)); } else { return; } @@ -4367,7 +4281,7 @@ public void warn(LintCategory lint) { method.attribute(syms.trustMeType.tsym) != null && isTrustMeAllowedOnMethod(method) && !types.isReifiable(method.type.getParameterTypes().last())) { - Check.this.lint.logIfEnabled(pos(), LintWarnings.VarargsUnsafeUseVarargsParam(method.params.last())); + log.warning(pos(), LintWarnings.VarargsUnsafeUseVarargsParam(method.params.last())); } break; default: @@ -4665,28 +4579,24 @@ private void checkVisible(DiagnosticPosition pos, Symbol what, PackageSymbol inP void checkModuleExists(final DiagnosticPosition pos, ModuleSymbol msym) { if (msym.kind != MDL) { - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.ModuleNotFound(msym))); + log.warning(pos, LintWarnings.ModuleNotFound(msym)); } } void checkPackageExistsForOpens(final DiagnosticPosition pos, PackageSymbol packge) { if (packge.members().isEmpty() && ((packge.flags() & Flags.HAS_RESOURCE) == 0)) { - deferredLintHandler.report(_ -> - lint.logIfEnabled(pos, LintWarnings.PackageEmptyOrNotFound(packge))); + log.warning(pos, LintWarnings.PackageEmptyOrNotFound(packge)); } } void checkModuleRequires(final DiagnosticPosition pos, final RequiresDirective rd) { if ((rd.module.flags() & Flags.AUTOMATIC_MODULE) != 0) { - deferredLintHandler.report(_ -> { - if (rd.isTransitive() && lint.isEnabled(LintCategory.REQUIRES_TRANSITIVE_AUTOMATIC)) { - log.warning(pos, LintWarnings.RequiresTransitiveAutomatic); - } else { - lint.logIfEnabled(pos, LintWarnings.RequiresAutomatic); - } - }); + if (rd.isTransitive()) { // see comment in Log.applyLint() for special logic that applies + log.warning(pos, LintWarnings.RequiresTransitiveAutomatic); + } else { + log.warning(pos, LintWarnings.RequiresAutomatic); + } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 1b31ea8a78cef..65978c34e5153 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -214,7 +214,6 @@ public class Flow { private final Resolve rs; private final JCDiagnostic.Factory diags; private Env attrEnv; - private Lint lint; private final Infer infer; public static Flow instance(Context context) { @@ -337,7 +336,6 @@ protected Flow(Context context) { syms = Symtab.instance(context); types = Types.instance(context); chk = Check.instance(context); - lint = Lint.instance(context); infer = Infer.instance(context); rs = Resolve.instance(context); diags = JCDiagnostic.Factory.instance(context); @@ -566,10 +564,8 @@ public void visitClassDef(JCClassDecl tree) { if (tree.sym == null) return; Liveness alivePrev = alive; ListBuffer pendingExitsPrev = pendingExits; - Lint lintPrev = lint; pendingExits = new ListBuffer<>(); - lint = lint.augment(tree.sym); try { // process all the nested classes @@ -600,30 +596,22 @@ public void visitClassDef(JCClassDecl tree) { } finally { pendingExits = pendingExitsPrev; alive = alivePrev; - lint = lintPrev; } } public void visitMethodDef(JCMethodDecl tree) { if (tree.body == null) return; - Lint lintPrev = lint; - - lint = lint.augment(tree.sym); Assert.check(pendingExits.isEmpty()); - try { - alive = Liveness.ALIVE; - scanStat(tree.body); - tree.completesNormally = alive != Liveness.DEAD; + alive = Liveness.ALIVE; + scanStat(tree.body); + tree.completesNormally = alive != Liveness.DEAD; - if (alive == Liveness.ALIVE && !tree.sym.type.getReturnType().hasTag(VOID)) - log.error(TreeInfo.diagEndPos(endPositions, tree.body), Errors.MissingRetStmt); + if (alive == Liveness.ALIVE && !tree.sym.type.getReturnType().hasTag(VOID)) + log.error(TreeInfo.diagEndPos(endPositions, tree.body), Errors.MissingRetStmt); - clearPendingExits(true); - } finally { - lint = lintPrev; - } + clearPendingExits(true); } private void clearPendingExits(boolean inMethod) { @@ -638,15 +626,7 @@ private void clearPendingExits(boolean inMethod) { } public void visitVarDef(JCVariableDecl tree) { - if (tree.init != null) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try{ - scan(tree.init); - } finally { - lint = lintPrev; - } - } + scan(tree.init); } public void visitBlock(JCBlock tree) { @@ -728,8 +708,7 @@ public void visitSwitch(JCSwitch tree) { // Warn about fall-through if lint switch fallthrough enabled. if (alive == Liveness.ALIVE && c.stats.nonEmpty() && l.tail.nonEmpty()) - lint.logIfEnabled(l.tail.head.pos(), - LintWarnings.PossibleFallThroughIntoCase); + log.warning(l.tail.head.pos(), LintWarnings.PossibleFallThroughIntoCase); } tree.isExhaustive = tree.hasUnconditionalPattern || TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases); @@ -1236,7 +1215,7 @@ public void visitTry(JCTry tree) { scanStat(tree.finalizer); tree.finallyCanCompleteNormally = alive != Liveness.DEAD; if (alive == Liveness.DEAD) { - lint.logIfEnabled(TreeInfo.diagEndPos(endPositions, tree.finalizer), + log.warning(TreeInfo.diagEndPos(endPositions, tree.finalizer), LintWarnings.FinallyCannotComplete); } else { while (exits.nonEmpty()) { @@ -1459,7 +1438,6 @@ public void visitClassDef(JCClassDecl tree) { List thrownPrev = thrown; List caughtPrev = caught; ListBuffer pendingExitsPrev = pendingExits; - Lint lintPrev = lint; boolean anonymousClass = tree.name == names.empty; pendingExits = new ListBuffer<>(); if (!anonymousClass) { @@ -1467,7 +1445,6 @@ public void visitClassDef(JCClassDecl tree) { } classDef = tree; thrown = List.nil(); - lint = lint.augment(tree.sym); try { // process all the nested classes @@ -1516,7 +1493,6 @@ public void visitClassDef(JCClassDecl tree) { pendingExits = pendingExitsPrev; caught = caughtPrev; classDef = classDefPrev; - lint = lintPrev; } } @@ -1525,9 +1501,6 @@ public void visitMethodDef(JCMethodDecl tree) { List caughtPrev = caught; List mthrown = tree.sym.type.getThrownTypes(); - Lint lintPrev = lint; - - lint = lint.augment(tree.sym); Assert.check(pendingExits.isEmpty()); @@ -1560,20 +1533,11 @@ else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK) } } finally { caught = caughtPrev; - lint = lintPrev; } } public void visitVarDef(JCVariableDecl tree) { - if (tree.init != null) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try{ - scan(tree.init); - } finally { - lint = lintPrev; - } - } + scan(tree.init); } public void visitBlock(JCBlock tree) { @@ -2395,82 +2359,76 @@ public void visitClassDef(JCClassDecl tree) { return; } - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try { - JCClassDecl classDefPrev = classDef; - int firstadrPrev = firstadr; - int nextadrPrev = nextadr; - ListBuffer pendingExitsPrev = pendingExits; + JCClassDecl classDefPrev = classDef; + int firstadrPrev = firstadr; + int nextadrPrev = nextadr; + ListBuffer pendingExitsPrev = pendingExits; - pendingExits = new ListBuffer<>(); - if (tree.name != names.empty) { - firstadr = nextadr; - } - classDef = tree; - try { - // define all the static fields - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(VARDEF)) { - JCVariableDecl def = (JCVariableDecl)l.head; - if ((def.mods.flags & STATIC) != 0) { - VarSymbol sym = def.sym; - if (trackable(sym)) { - newVar(def); - } + pendingExits = new ListBuffer<>(); + if (tree.name != names.empty) { + firstadr = nextadr; + } + classDef = tree; + try { + // define all the static fields + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(VARDEF)) { + JCVariableDecl def = (JCVariableDecl)l.head; + if ((def.mods.flags & STATIC) != 0) { + VarSymbol sym = def.sym; + if (trackable(sym)) { + newVar(def); } } } + } - // process all the static initializers - forEachInitializer(tree, true, def -> { - scan(def); - clearPendingExits(false); - }); + // process all the static initializers + forEachInitializer(tree, true, def -> { + scan(def); + clearPendingExits(false); + }); - // verify all static final fields got initialized - for (int i = firstadr; i < nextadr; i++) { - JCVariableDecl vardecl = vardecls[i]; - VarSymbol var = vardecl.sym; - if (var.owner == classDef.sym && var.isStatic()) { - checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); - } + // verify all static final fields got initialized + for (int i = firstadr; i < nextadr; i++) { + JCVariableDecl vardecl = vardecls[i]; + VarSymbol var = vardecl.sym; + if (var.owner == classDef.sym && var.isStatic()) { + checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); } + } - // define all the instance fields - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(VARDEF)) { - JCVariableDecl def = (JCVariableDecl)l.head; - if ((def.mods.flags & STATIC) == 0) { - VarSymbol sym = def.sym; - if (trackable(sym)) { - newVar(def); - } + // define all the instance fields + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(VARDEF)) { + JCVariableDecl def = (JCVariableDecl)l.head; + if ((def.mods.flags & STATIC) == 0) { + VarSymbol sym = def.sym; + if (trackable(sym)) { + newVar(def); } } } + } - // process all the methods - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(METHODDEF)) { - scan(l.head); - } + // process all the methods + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(METHODDEF)) { + scan(l.head); } + } - // process all the nested classes - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (l.head.hasTag(CLASSDEF)) { - scan(l.head); - } + // process all the nested classes + for (List l = tree.defs; l.nonEmpty(); l = l.tail) { + if (l.head.hasTag(CLASSDEF)) { + scan(l.head); } - } finally { - pendingExits = pendingExitsPrev; - nextadr = nextadrPrev; - firstadr = firstadrPrev; - classDef = classDefPrev; } } finally { - lint = lintPrev; + pendingExits = pendingExitsPrev; + nextadr = nextadrPrev; + firstadr = firstadrPrev; + classDef = classDefPrev; } } @@ -2485,87 +2443,81 @@ public void visitMethodDef(JCMethodDecl tree) { return; } - Lint lintPrev = lint; - lint = lint.augment(tree.sym); + final Bits initsPrev = new Bits(inits); + final Bits uninitsPrev = new Bits(uninits); + int nextadrPrev = nextadr; + int firstadrPrev = firstadr; + int returnadrPrev = returnadr; + + Assert.check(pendingExits.isEmpty()); + boolean isConstructorPrev = isConstructor; try { - final Bits initsPrev = new Bits(inits); - final Bits uninitsPrev = new Bits(uninits); - int nextadrPrev = nextadr; - int firstadrPrev = firstadr; - int returnadrPrev = returnadr; - - Assert.check(pendingExits.isEmpty()); - boolean isConstructorPrev = isConstructor; - try { - isConstructor = TreeInfo.isConstructor(tree); + isConstructor = TreeInfo.isConstructor(tree); - // We only track field initialization inside constructors - if (!isConstructor) { - firstadr = nextadr; - } + // We only track field initialization inside constructors + if (!isConstructor) { + firstadr = nextadr; + } - // Mark all method parameters as DA - for (List l = tree.params; l.nonEmpty(); l = l.tail) { - JCVariableDecl def = l.head; - scan(def); - Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); - /* If we are executing the code from Gen, then there can be - * synthetic or mandated variables, ignore them. - */ - initParam(def); - } - // else we are in an instance initializer block; - // leave caught unchanged. - scan(tree.body); + // Mark all method parameters as DA + for (List l = tree.params; l.nonEmpty(); l = l.tail) { + JCVariableDecl def = l.head; + scan(def); + Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); + /* If we are executing the code from Gen, then there can be + * synthetic or mandated variables, ignore them. + */ + initParam(def); + } + // else we are in an instance initializer block; + // leave caught unchanged. + scan(tree.body); - boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || - (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD); - if (isConstructor) { - boolean isSynthesized = (tree.sym.flags() & - GENERATEDCONSTR) != 0; - for (int i = firstadr; i < nextadr; i++) { - JCVariableDecl vardecl = vardecls[i]; - VarSymbol var = vardecl.sym; - if (var.owner == classDef.sym && !var.isStatic()) { - // choose the diagnostic position based on whether - // the ctor is default(synthesized) or not - if (isSynthesized && !isCompactOrGeneratedRecordConstructor) { - checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), - var, Errors.VarNotInitializedInDefaultConstructor(var)); - } else if (isCompactOrGeneratedRecordConstructor) { - boolean isInstanceRecordField = var.enclClass().isRecord() && - (var.flags_field & (Flags.PRIVATE | Flags.FINAL | Flags.GENERATED_MEMBER | Flags.RECORD)) != 0 && - var.owner.kind == TYP; - if (isInstanceRecordField) { - boolean notInitialized = !inits.isMember(var.adr); - if (notInitialized && uninits.isMember(var.adr) && tree.completesNormally) { - /* this way we indicate Lower that it should generate an initialization for this field - * in the compact constructor - */ - var.flags_field |= UNINITIALIZED_FIELD; - } else { - checkInit(TreeInfo.diagEndPos(endPositions, tree.body), var); - } + boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || + (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD); + if (isConstructor) { + boolean isSynthesized = (tree.sym.flags() & + GENERATEDCONSTR) != 0; + for (int i = firstadr; i < nextadr; i++) { + JCVariableDecl vardecl = vardecls[i]; + VarSymbol var = vardecl.sym; + if (var.owner == classDef.sym && !var.isStatic()) { + // choose the diagnostic position based on whether + // the ctor is default(synthesized) or not + if (isSynthesized && !isCompactOrGeneratedRecordConstructor) { + checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), + var, Errors.VarNotInitializedInDefaultConstructor(var)); + } else if (isCompactOrGeneratedRecordConstructor) { + boolean isInstanceRecordField = var.enclClass().isRecord() && + (var.flags_field & (Flags.PRIVATE | Flags.FINAL | Flags.GENERATED_MEMBER | Flags.RECORD)) != 0 && + var.owner.kind == TYP; + if (isInstanceRecordField) { + boolean notInitialized = !inits.isMember(var.adr); + if (notInitialized && uninits.isMember(var.adr) && tree.completesNormally) { + /* this way we indicate Lower that it should generate an initialization for this field + * in the compact constructor + */ + var.flags_field |= UNINITIALIZED_FIELD; } else { - checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); + checkInit(TreeInfo.diagEndPos(endPositions, tree.body), var); } } else { - checkInit(TreeInfo.diagEndPos(endPositions, tree.body), var); + checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); } + } else { + checkInit(TreeInfo.diagEndPos(endPositions, tree.body), var); } } } - clearPendingExits(true); - } finally { - inits.assign(initsPrev); - uninits.assign(uninitsPrev); - nextadr = nextadrPrev; - firstadr = firstadrPrev; - returnadr = returnadrPrev; - isConstructor = isConstructorPrev; } + clearPendingExits(true); } finally { - lint = lintPrev; + inits.assign(initsPrev); + uninits.assign(uninitsPrev); + nextadr = nextadrPrev; + firstadr = firstadrPrev; + returnadr = returnadrPrev; + isConstructor = isConstructorPrev; } } @@ -2593,21 +2545,15 @@ protected void initParam(JCVariableDecl def) { } public void visitVarDef(JCVariableDecl tree) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try{ - boolean track = trackable(tree.sym); - if (track && (tree.sym.owner.kind == MTH || tree.sym.owner.kind == VAR)) { - newVar(tree); - } - if (tree.init != null) { - scanExpr(tree.init); - if (track) { - letInit(tree.pos(), tree.sym); - } + boolean track = trackable(tree.sym); + if (track && (tree.sym.owner.kind == MTH || tree.sym.owner.kind == VAR)) { + newVar(tree); + } + if (tree.init != null) { + scanExpr(tree.init); + if (track) { + letInit(tree.pos(), tree.sym); } - } finally { - lint = lintPrev; } } @@ -2859,8 +2805,7 @@ public void visitTry(JCTry tree) { final Bits uninitsEnd = new Bits(uninits); int nextadrCatch = nextadr; - if (!resourceVarDecls.isEmpty() && - lint.isEnabled(Lint.LintCategory.TRY)) { + if (!resourceVarDecls.isEmpty()) { for (JCVariableDecl resVar : resourceVarDecls) { if (unrefdResources.includes(resVar.sym) && !resVar.sym.isUnnamedVariable()) { log.warning(resVar.pos(), diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java index b726cc7a61d8b..d63ba1677d6a0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java @@ -66,7 +66,6 @@ public class MemberEnter extends JCTree.Visitor { private final Annotate annotate; private final Types types; private final Names names; - private final DeferredLintHandler deferredLintHandler; public static MemberEnter instance(Context context) { MemberEnter instance = context.get(memberEnterKey); @@ -87,7 +86,6 @@ protected MemberEnter(Context context) { types = Types.instance(context); source = Source.instance(context); names = Names.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); } /** Construct method type from method signature. @@ -194,16 +192,11 @@ public void visitMethodDef(JCMethodDecl tree) { } Env localEnv = methodEnv(tree, env); - deferredLintHandler.push(tree); - try { - // Compute the method type - m.type = signature(m, tree.typarams, tree.params, - tree.restype, tree.recvparam, - tree.thrown, - localEnv); - } finally { - deferredLintHandler.pop(); - } + // Compute the method type + m.type = signature(m, tree.typarams, tree.params, + tree.restype, tree.recvparam, + tree.thrown, + localEnv); if (types.isSignaturePolymorphic(m)) { m.flags_field |= SIGNATURE_POLYMORPHIC; @@ -227,14 +220,14 @@ public void visitMethodDef(JCMethodDecl tree) { enclScope.enter(m); } - annotate.annotateLater(tree.mods.annotations, localEnv, m, tree); + annotate.annotateLater(tree.mods.annotations, localEnv, m); // Visit the signature of the method. Note that // TypeAnnotate doesn't descend into the body. - annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, m, tree); + annotate.queueScanTreeAndTypeAnnotate(tree, localEnv, m); if (tree.defaultValue != null) { m.defaultValue = annotate.unfinishedDefaultValue(); // set it to temporary sentinel for now - annotate.annotateDefaultValueLater(tree.defaultValue, localEnv, m, tree); + annotate.annotateDefaultValueLater(tree.defaultValue, localEnv, m); } } @@ -263,18 +256,13 @@ public void visitVarDef(JCVariableDecl tree) { localEnv = env.dup(tree, env.info.dup()); localEnv.info.staticLevel++; } - deferredLintHandler.push(tree); - try { - if (TreeInfo.isEnumInit(tree)) { - attr.attribIdentAsEnumType(localEnv, (JCIdent)tree.vartype); - } else if (!tree.isImplicitlyTyped()) { - attr.attribType(tree.vartype, localEnv); - if (TreeInfo.isReceiverParam(tree)) - checkReceiver(tree, localEnv); - } - } finally { - deferredLintHandler.pop(); + if (TreeInfo.isEnumInit(tree)) { + attr.attribIdentAsEnumType(localEnv, (JCIdent)tree.vartype); + } else if (!tree.isImplicitlyTyped()) { + attr.attribType(tree.vartype, localEnv); + if (TreeInfo.isReceiverParam(tree)) + checkReceiver(tree, localEnv); } if ((tree.mods.flags & VARARGS) != 0) { @@ -315,9 +303,9 @@ public void visitVarDef(JCVariableDecl tree) { } } - annotate.annotateLater(tree.mods.annotations, localEnv, v, tree); + annotate.annotateLater(tree.mods.annotations, localEnv, v); if (!tree.isImplicitlyTyped()) { - annotate.queueScanTreeAndTypeAnnotate(tree.vartype, localEnv, v, tree); + annotate.queueScanTreeAndTypeAnnotate(tree.vartype, localEnv, v); } v.pos = tree.pos; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java index 596c5704cbef3..895a58014b16c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java @@ -52,7 +52,6 @@ import javax.tools.StandardLocation; import com.sun.source.tree.ModuleTree.ModuleKind; -import com.sun.tools.javac.code.DeferredLintHandler; import com.sun.tools.javac.code.Directive; import com.sun.tools.javac.code.Directive.ExportsDirective; import com.sun.tools.javac.code.Directive.ExportsFlag; @@ -103,6 +102,7 @@ import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; @@ -141,7 +141,6 @@ public class Modules extends JCTree.Visitor { private final Attr attr; private final Check chk; private final Preview preview; - private final DeferredLintHandler deferredLintHandler; private final TypeEnvs typeEnvs; private final Types types; private final JavaFileManager fileManager; @@ -169,8 +168,6 @@ public class Modules extends JCTree.Visitor { private final String moduleVersionOpt; private final boolean sourceLauncher; - private final boolean lintOptions; - private Set rootModules = null; private final Set warnedMissing = new HashSet<>(); @@ -193,7 +190,6 @@ protected Modules(Context context) { attr = Attr.instance(context); chk = Check.instance(context); preview = Preview.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); typeEnvs = TypeEnvs.instance(context); moduleFinder = ModuleFinder.instance(context); types = Types.instance(context); @@ -205,8 +201,6 @@ protected Modules(Context context) { allowAccessIntoSystem = options.isUnset(Option.RELEASE); - lintOptions = options.isUnset(Option.XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option); - multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH); ClassWriter classWriter = ClassWriter.instance(context); classWriter.multiModuleMode = multiModuleMode; @@ -746,7 +740,6 @@ public void complete(Symbol sym) throws CompletionFailure { ModuleVisitor v = new ModuleVisitor(); JavaFileObject prev = log.useSource(tree.sourcefile); JCModuleDecl moduleDecl = tree.getModuleDecl(); - deferredLintHandler.push(moduleDecl); try { moduleDecl.accept(v); @@ -754,7 +747,6 @@ public void complete(Symbol sym) throws CompletionFailure { checkCyclicDependencies(moduleDecl); } finally { log.useSource(prev); - deferredLintHandler.pop(); msym.flags_field &= ~UNATTRIBUTED; } } @@ -795,7 +787,12 @@ public void visitModuleDef(JCModuleDecl tree) { sym.requires = List.nil(); sym.exports = List.nil(); sym.opens = List.nil(); - tree.directives.forEach(t -> t.accept(this)); + Lint prevLint = chk.setLint(lint.augment(sym)); + try { + tree.directives.forEach(t -> t.accept(this)); + } finally { + chk.setLint(prevLint); + } sym.requires = sym.requires.reverse(); sym.exports = sym.exports.reverse(); sym.opens = sym.opens.reverse(); @@ -991,13 +988,11 @@ public Completer getUsesProvidesCompleter() { UsesProvidesVisitor v = new UsesProvidesVisitor(msym, env); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); JCModuleDecl decl = env.toplevel.getModuleDecl(); - deferredLintHandler.push(decl); try { decl.accept(v); } finally { log.useSource(prev); - deferredLintHandler.pop(); } }; } @@ -1263,12 +1258,9 @@ private void setupAllModules() { } observable = computeTransitiveClosure(limitMods, rootModules, null); observable.addAll(rootModules); - if (lintOptions) { - for (ModuleSymbol msym : limitMods) { - if (!observable.contains(msym)) { - log.warning( - LintWarnings.ModuleForOptionNotFound(Option.LIMIT_MODULES, msym)); - } + for (ModuleSymbol msym : limitMods) { + if (!observable.contains(msym)) { + log.warning(LintWarnings.ModuleForOptionNotFound(Option.LIMIT_MODULES, msym), DiagnosticFlag.DEFAULT_ENABLED); } } } @@ -1721,10 +1713,7 @@ private boolean isKnownModule(ModuleSymbol msym, Set unknownModule } if (!unknownModules.contains(msym)) { - if (lintOptions) { - log.warning( - LintWarnings.ModuleForOptionNotFound(Option.ADD_EXPORTS, msym)); - } + log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_EXPORTS, msym), DiagnosticFlag.DEFAULT_ENABLED); unknownModules.add(msym); } return false; @@ -1760,9 +1749,7 @@ private void initAddReads() { ModuleSymbol msym = syms.enterModule(names.fromString(sourceName)); if (!allModules.contains(msym)) { - if (lintOptions) { - log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, msym)); - } + log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, msym), DiagnosticFlag.DEFAULT_ENABLED); continue; } @@ -1780,9 +1767,8 @@ private void initAddReads() { continue; targetModule = syms.enterModule(names.fromString(targetName)); if (!allModules.contains(targetModule)) { - if (lintOptions) { - log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, targetModule)); - } + log.warning(LintWarnings.ModuleForOptionNotFound(Option.ADD_READS, targetModule), + DiagnosticFlag.DEFAULT_ENABLED); continue; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java index d0b85b3d1cb99..14fd937ecf0aa 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java @@ -108,7 +108,6 @@ public class TypeEnter implements Completer { private final Annotate annotate; private final TypeAnnotations typeAnnotations; private final Types types; - private final DeferredLintHandler deferredLintHandler; private final Lint lint; private final TypeEnvs typeEnvs; private final Dependencies dependencies; @@ -135,7 +134,6 @@ protected TypeEnter(Context context) { annotate = Annotate.instance(context); typeAnnotations = TypeAnnotations.instance(context); types = Types.instance(context); - deferredLintHandler = DeferredLintHandler.instance(context); lint = Lint.instance(context); typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); @@ -274,7 +272,6 @@ protected void doCompleteEnvs(List> envs) { queue.add(env); JavaFileObject prev = log.useSource(env.toplevel.sourcefile); - deferredLintHandler.push(tree); try { dependencies.push(env.enclClass.sym, phaseName); runPhase(env); @@ -282,7 +279,6 @@ protected void doCompleteEnvs(List> envs) { chk.completionError(tree.pos(), ex); } finally { dependencies.pop(); - deferredLintHandler.pop(); log.useSource(prev); } } @@ -365,7 +361,6 @@ private void resolveImports(JCCompilationUnit tree, Env env) { ImportFilter prevStaticImportFilter = staticImportFilter; ImportFilter prevTypeImportFilter = typeImportFilter; - deferredLintHandler.pushImmediate(lint); Lint prevLint = chk.setLint(lint); Env prevEnv = this.env; try { @@ -390,20 +385,14 @@ private void resolveImports(JCCompilationUnit tree, Env env) { handleImports(tree.getImports()); if (decl != null) { - deferredLintHandler.push(decl); - try { - //check @Deprecated: - markDeprecated(decl.sym, decl.mods.annotations, env); - } finally { - deferredLintHandler.pop(); - } + //check for @Deprecated annotations + markDeprecated(decl.sym, decl.mods.annotations, env); // process module annotations - annotate.annotateLater(decl.mods.annotations, env, env.toplevel.modle, decl); + annotate.annotateLater(decl.mods.annotations, env, env.toplevel.modle); } } finally { this.env = prevEnv; chk.setLint(prevLint); - deferredLintHandler.pop(); this.staticImportFilter = prevStaticImportFilter; this.typeImportFilter = prevTypeImportFilter; } @@ -436,7 +425,7 @@ private void checkClassPackageClash(JCPackageDecl tree) { } } // process package annotations - annotate.annotateLater(tree.annotations, env, env.toplevel.packge, tree); + annotate.annotateLater(tree.annotations, env, env.toplevel.packge); } private void doImport(JCImport tree, boolean fromModuleImport) { @@ -928,9 +917,9 @@ protected void runPhase(Env env) { Env baseEnv = baseEnv(tree, env); if (tree.extending != null) - annotate.queueScanTreeAndTypeAnnotate(tree.extending, baseEnv, sym, tree); + annotate.queueScanTreeAndTypeAnnotate(tree.extending, baseEnv, sym); for (JCExpression impl : tree.implementing) - annotate.queueScanTreeAndTypeAnnotate(impl, baseEnv, sym, tree); + annotate.queueScanTreeAndTypeAnnotate(impl, baseEnv, sym); annotate.flush(); attribSuperTypes(env, baseEnv); @@ -945,11 +934,11 @@ protected void runPhase(Env env) { chk.checkNotRepeated(iface.pos(), types.erasure(it), interfaceSet); } - annotate.annotateLater(tree.mods.annotations, baseEnv, sym, tree); + annotate.annotateLater(tree.mods.annotations, baseEnv, sym); attr.attribTypeVariables(tree.typarams, baseEnv, false); for (JCTypeParameter tp : tree.typarams) - annotate.queueScanTreeAndTypeAnnotate(tp, baseEnv, sym, tree); + annotate.queueScanTreeAndTypeAnnotate(tp, baseEnv, sym); // check that no package exists with same fully qualified name, // but admit classes in the unnamed package which have the same diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java index 73f8623971381..5964c16c1517b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java @@ -92,7 +92,7 @@ public void setContext(Context context) { options = Options.instance(context); // Initialize locations - locations.update(log, lint, FSInfo.instance(context)); + locations.update(log, FSInfo.instance(context)); // Apply options options.whenReady(this::applyOptions); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java index 5ff55d4be3aa2..7661e4331e40d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java @@ -77,14 +77,11 @@ import javax.tools.StandardJavaFileManager.PathFactory; import javax.tools.StandardLocation; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; import jdk.internal.jmod.JmodFile; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.main.Option; import com.sun.tools.javac.resources.CompilerProperties.Errors; +import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; @@ -127,11 +124,6 @@ public class Locations { */ private FSInfo fsInfo; - /** - * The root {@link Lint} instance. - */ - private Lint lint; - private ModuleNameReader moduleNameReader; private PathFactory pathFactory = Paths::get; @@ -172,9 +164,8 @@ public void close() throws IOException { } } - void update(Log log, Lint lint, FSInfo fsInfo) { + void update(Log log, FSInfo fsInfo) { this.log = log; - this.lint = lint; this.fsInfo = fsInfo; } @@ -225,7 +216,7 @@ private Iterable getPathEntries(String searchPath, Path emptyPathDefault) try { entries.add(getPath(s)); } catch (IllegalArgumentException e) { - lint.logIfEnabled(LintWarnings.InvalidPath(s)); + log.warning(LintWarnings.InvalidPath(s)); } } } @@ -319,7 +310,7 @@ public SearchPath addDirectories(String dirs) { private void addDirectory(Path dir, boolean warn) { if (!Files.isDirectory(dir)) { if (warn) { - lint.logIfEnabled(LintWarnings.DirPathElementNotFound(dir)); + log.warning(LintWarnings.DirPathElementNotFound(dir)); } return; } @@ -364,7 +355,7 @@ public void addFile(Path file, boolean warn) { if (!fsInfo.exists(file)) { /* No such file or directory exists */ if (warn) { - lint.logIfEnabled(LintWarnings.PathElementNotFound(file)); + log.warning(LintWarnings.PathElementNotFound(file)); } super.add(file); return; @@ -386,12 +377,12 @@ public void addFile(Path file, boolean warn) { try { FileSystems.newFileSystem(file, (ClassLoader)null).close(); if (warn) { - lint.logIfEnabled(LintWarnings.UnexpectedArchiveFile(file)); + log.warning(LintWarnings.UnexpectedArchiveFile(file)); } } catch (IOException | ProviderNotFoundException e) { // FIXME: include e.getLocalizedMessage in warning if (warn) { - lint.logIfEnabled(LintWarnings.InvalidArchiveFile(file)); + log.warning(LintWarnings.InvalidArchiveFile(file)); } return; } @@ -1654,7 +1645,7 @@ private boolean isSeparator(char ch) { void add(Map> map, Path prefix, Path suffix) { if (!Files.isDirectory(prefix)) { - lint.logIfEnabled(Files.exists(prefix) ? + log.warning(Files.exists(prefix) ? LintWarnings.DirPathElementNotDirectory(prefix) : LintWarnings.DirPathElementNotFound(prefix)); return; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 6f55903f489ab..b5f94f903b018 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -51,7 +51,6 @@ import com.sun.tools.javac.comp.Annotate.AnnotationTypeCompleter; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Directive.*; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Scope.WriteableScope; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Symtab; @@ -139,9 +138,6 @@ public class ClassReader { /** The symbol table. */ Symtab syms; - /** The root Lint config. */ - Lint lint; - Types types; /** The name table. */ @@ -303,8 +299,6 @@ protected ClassReader(Context context) { typevars = WriteableScope.create(syms.noSymbol); - lint = Lint.instance(context); - initAttributeReaders(); } @@ -854,8 +848,7 @@ protected boolean accepts(AttributeKind kind) { if (!warnedAttrs.contains(name)) { JavaFileObject prev = log.useSource(currentClassFile); try { - lint.logIfEnabled( - LintWarnings.FutureAttr(name, version.major, version.minor, majorVersion, minorVersion)); + log.warning(LintWarnings.FutureAttr(name, version.major, version.minor, majorVersion, minorVersion)); } finally { log.useSource(prev); } @@ -1608,7 +1601,7 @@ void readParameterAnnotations(Symbol meth) { } else if (parameterAnnotations.length != numParameters) { //the RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations //provide annotations for a different number of parameters, ignore: - lint.logIfEnabled(LintWarnings.RuntimeVisibleInvisibleParamAnnotationsMismatch(currentClassFile)); + log.warning(LintWarnings.RuntimeVisibleInvisibleParamAnnotationsMismatch(currentClassFile)); for (int pnum = 0; pnum < numParameters; pnum++) { readAnnotations(); } @@ -2074,9 +2067,9 @@ MethodSymbol findAccessMethod(Type container, Name name) { JavaFileObject prevSource = log.useSource(requestingOwner.classfile); try { if (failure == null) { - lint.logIfEnabled(LintWarnings.AnnotationMethodNotFound(container, name)); + log.warning(LintWarnings.AnnotationMethodNotFound(container, name)); } else { - lint.logIfEnabled(LintWarnings.AnnotationMethodNotFoundReason(container, + log.warning(LintWarnings.AnnotationMethodNotFoundReason(container, name, failure.getDetailValue()));//diagnostic, if present } @@ -2954,7 +2947,7 @@ void adjustParameterAnnotations(MethodSymbol sym, Type methodDescriptor, private void dropParameterAnnotations() { parameterAnnotations = null; - lint.logIfEnabled(LintWarnings.RuntimeInvisibleParameterAnnotations(currentClassFile)); + log.warning(LintWarnings.RuntimeInvisibleParameterAnnotations(currentClassFile)); } /** * Creates the parameter at the position {@code mpIndex} in the parameter list of the owning method. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java index 5fa76c14d3f8f..3f3f5e2f27b67 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java @@ -28,11 +28,12 @@ import com.sun.source.util.TaskEvent; import com.sun.source.util.TaskListener; import com.sun.tools.javac.api.JavacTool; -import com.sun.tools.javac.code.Preview; +import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.resources.LauncherProperties.Errors; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Context.Factory; +import com.sun.tools.javac.util.Log; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; @@ -120,8 +121,11 @@ void compileProgram() throws Fault { } var opts = options.forProgramCompilation(); var context = new Context(); - MemoryPreview.registerInstance(context); var task = compiler.getTask(out, memoryFileManager, null, opts, null, units, context); + + // This suppresses diagnostics like "Note: Recompile with -Xlint:preview for details." + Log.instance(context).suppressAggregatedWarningNotes(LintCategory.PREVIEW); + var ok = task.call(); if (!ok) { throw new Fault(Errors.CompilationFailed); @@ -269,19 +273,4 @@ private void enableNativeAccess(ModuleLayer.Controller controller, boolean shoul controller.enableNativeAccess(module); } } - - static class MemoryPreview extends Preview { - static void registerInstance(Context context) { - context.put(previewKey, (Factory)MemoryPreview::new); - } - - MemoryPreview(Context context) { - super(context); - } - - @Override - public void reportDeferredDiagnostics() { - // suppress diagnostics like "Note: Recompile with -Xlint:preview for details." - } - } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java index 14b42bc01c5d3..542b44d72430b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java @@ -67,6 +67,7 @@ import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.JCDiagnostic; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticInfo; import com.sun.tools.javac.util.JCDiagnostic.Fragment; import com.sun.tools.javac.util.List; @@ -503,13 +504,9 @@ public boolean validate() { } } else { // single-module or legacy mode - boolean lintPaths = options.isUnset(Option.XLINT_CUSTOM, - "-" + LintCategory.PATH.option); - if (lintPaths) { - Path outDirParent = outDir.getParent(); - if (outDirParent != null && Files.exists(outDirParent.resolve("module-info.class"))) { - log.warning(LintWarnings.OutdirIsInExplodedModule(outDir)); - } + Path outDirParent = outDir.getParent(); + if (outDirParent != null && Files.exists(outDirParent.resolve("module-info.class"))) { + log.warning(LintWarnings.OutdirIsInExplodedModule(outDir), DiagnosticFlag.DEFAULT_ENABLED); } } } @@ -577,15 +574,16 @@ public boolean validate() { reportDiag(Errors.SourcepathModulesourcepathConflict); } - boolean lintOptions = options.isUnset(Option.XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option); - if (lintOptions && source.compareTo(Source.DEFAULT) < 0 && !options.isSet(Option.RELEASE)) { + if (source.compareTo(Source.DEFAULT) < 0 && !options.isSet(Option.RELEASE)) { if (fm instanceof BaseFileManager baseFileManager) { if (source.compareTo(Source.JDK8) <= 0) { - if (baseFileManager.isDefaultBootClassPath()) - log.warning(LintWarnings.SourceNoBootclasspath(source.name, releaseNote(source, targetString))); - } else { - if (baseFileManager.isDefaultSystemModulesPath()) - log.warning(LintWarnings.SourceNoSystemModulesPath(source.name, releaseNote(source, targetString))); + if (baseFileManager.isDefaultBootClassPath()) { + log.warning(LintWarnings.SourceNoBootclasspath(source.name, releaseNote(source, targetString)), + DiagnosticFlag.DEFAULT_ENABLED); + } + } else if (baseFileManager.isDefaultSystemModulesPath()) { + log.warning(LintWarnings.SourceNoSystemModulesPath(source.name, releaseNote(source, targetString)), + DiagnosticFlag.DEFAULT_ENABLED); } } } @@ -594,15 +592,15 @@ public boolean validate() { if (source.compareTo(Source.MIN) < 0) { log.error(Errors.OptionRemovedSource(source.name, Source.MIN.name)); - } else if (source == Source.MIN && lintOptions) { - log.warning(LintWarnings.OptionObsoleteSource(source.name)); + } else if (source == Source.MIN) { + log.warning(LintWarnings.OptionObsoleteSource(source.name), DiagnosticFlag.DEFAULT_ENABLED); obsoleteOptionFound = true; } if (target.compareTo(Target.MIN) < 0) { log.error(Errors.OptionRemovedTarget(target, Target.MIN)); - } else if (target == Target.MIN && lintOptions) { - log.warning(LintWarnings.OptionObsoleteTarget(target)); + } else if (target == Target.MIN) { + log.warning(LintWarnings.OptionObsoleteTarget(target), DiagnosticFlag.DEFAULT_ENABLED); obsoleteOptionFound = true; } @@ -635,8 +633,8 @@ public boolean validate() { log.error(Errors.ProcessorpathNoProcessormodulepath); } - if (obsoleteOptionFound && lintOptions) { - log.warning(LintWarnings.OptionObsoleteSuppression); + if (obsoleteOptionFound) { + log.warning(LintWarnings.OptionObsoleteSuppression, DiagnosticFlag.DEFAULT_ENABLED); } SourceVersion sv = Source.toSourceVersion(source); @@ -646,8 +644,8 @@ public boolean validate() { validateLimitModules(sv); validateDefaultModuleForCreatedFiles(sv); - if (lintOptions && options.isSet(Option.ADD_OPENS)) { - log.warning(LintWarnings.AddopensIgnored); + if (options.isSet(Option.ADD_OPENS)) { + log.warning(LintWarnings.AddopensIgnored, DiagnosticFlag.DEFAULT_ENABLED); } return !errors && (log.nerrors == 0); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 52979ab16b412..205d50d8319b9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -79,6 +79,7 @@ import com.sun.tools.javac.util.Context.Key; import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.JCDiagnostic.Factory; +import com.sun.tools.javac.util.LintMapper; import com.sun.tools.javac.util.Log.DiagnosticHandler; import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler; import com.sun.tools.javac.util.Log.WriterKind; @@ -262,6 +263,10 @@ else if (option.equals("class")) */ protected JNIWriter jniWriter; + /** The Lint mapper. + */ + protected LintMapper lintMapper; + /** The module for the symbol table entry phases. */ protected Enter enter; @@ -274,10 +279,6 @@ else if (option.equals("class")) */ protected Source source; - /** The preview language version. - */ - protected Preview preview; - /** The module for code generation. */ protected Gen gen; @@ -392,6 +393,7 @@ public JavaCompiler(Context context) { names = Names.instance(context); log = Log.instance(context); + lintMapper = LintMapper.instance(context); diagFactory = JCDiagnostic.Factory.instance(context); finder = ClassFinder.instance(context); reader = ClassReader.instance(context); @@ -413,7 +415,6 @@ public JavaCompiler(Context context) { log.error(Errors.CantAccess(ex.sym, ex.getDetailValue())); } source = Source.instance(context); - preview = Preview.instance(context); attr = Attr.instance(context); analyzer = Analyzer.instance(context); chk = Check.instance(context); @@ -584,6 +585,7 @@ protected boolean shouldStop(CompileState cs) { /** The number of errors reported so far. */ public int errorCount() { + log.reportOutstandingWarnings(); if (werror && log.nerrors == 0 && log.nwarnings > 0) { log.error(Errors.WarningsAndWerror); } @@ -634,6 +636,7 @@ protected JCCompilationUnit parse(JavaFileObject filename, CharSequence content) private JCCompilationUnit parse(JavaFileObject filename, CharSequence content, boolean silent) { long msec = now(); JCCompilationUnit tree = make.TopLevel(List.nil()); + lintMapper.startParsingFile(filename); if (content != null) { if (verbose) { log.printVerbose("parsing.started", filename); @@ -653,6 +656,7 @@ private JCCompilationUnit parse(JavaFileObject filename, CharSequence content, b } tree.sourcefile = filename; + lintMapper.finishParsingFile(tree); if (content != null && !taskListener.isEmpty() && !silent) { TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, tree); @@ -1852,8 +1856,8 @@ public void reportDeferredDiagnostics() { else log.warning(Warnings.ProcUseProcOrImplicit); } - chk.reportDeferredDiagnostics(); - preview.reportDeferredDiagnostics(); + log.reportOutstandingWarnings(); + log.reportOutstandingNotes(); if (log.compressedOutput) { log.mandatoryNote(null, Notes.CompressedDiags); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java index 2cec3549c7a78..88249632fc226 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java @@ -25,7 +25,6 @@ package com.sun.tools.javac.parser; -import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; @@ -83,7 +82,7 @@ public class JavaTokenizer extends UnicodeReader { /** * The log to be used for error reporting. Copied from scanner factory. */ - private final Log log; + protected final Log log; /** * The token factory. Copied from scanner factory. @@ -135,13 +134,6 @@ public class JavaTokenizer extends UnicodeReader { */ protected boolean hasEscapeSequences; - /** - * The set of lint options currently in effect. It is initialized - * from the context, and then is set/reset as needed by Attr as it - * visits all the various parts of the trees during attribution. - */ - protected final Lint lint; - /** * Construct a Java token scanner from the input character buffer. * @@ -168,7 +160,6 @@ protected JavaTokenizer(ScannerFactory fac, char[] array, int length) { this.source = fac.source; this.preview = fac.preview; this.enableLineDocComments = fac.enableLineDocComments; - this.lint = fac.lint; this.sb = new StringBuilder(256); } @@ -218,17 +209,6 @@ protected void lexError(DiagnosticFlag flags, int pos, JCDiagnostic.Error key) { errPos = pos; } - /** - * Report a warning at the given position using the provided arguments. - * - * @param pos position in input buffer. - * @param key error key to report. - */ - protected void lexWarning(int pos, JCDiagnostic.LintWarning key) { - DiagnosticPosition dp = new SimpleDiagnosticPosition(pos) ; - log.warning(dp, key); - } - /** * Add a character to the literal buffer. * @@ -1069,17 +1049,13 @@ public Token readToken() { // If a text block. if (isTextBlock) { // Verify that the incidental indentation is consistent. - if (lint.isEnabled(LintCategory.TEXT_BLOCKS)) { - Set checks = - TextBlockSupport.checkWhitespace(string); - if (checks.contains(TextBlockSupport.WhitespaceChecks.INCONSISTENT)) { - lexWarning(pos, - LintWarnings.InconsistentWhiteSpaceIndentation); - } - if (checks.contains(TextBlockSupport.WhitespaceChecks.TRAILING)) { - lexWarning(pos, - LintWarnings.TrailingWhiteSpaceWillBeRemoved); - } + Set checks = + TextBlockSupport.checkWhitespace(string); + if (checks.contains(TextBlockSupport.WhitespaceChecks.INCONSISTENT)) { + log.warning(pos, LintWarnings.InconsistentWhiteSpaceIndentation); + } + if (checks.contains(TextBlockSupport.WhitespaceChecks.TRAILING)) { + log.warning(pos, LintWarnings.TrailingWhiteSpaceWillBeRemoved); } // Remove incidental indentation. try { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index a64ad869af1b9..3555fcc5a29e1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -113,12 +113,6 @@ public class JavacParser implements Parser { /** End position mappings container */ protected final AbstractEndPosTable endPosTable; - /** A map associating "other nearby documentation comments" - * with the preferred documentation comment for a declaration. */ - protected Map> danglingComments = new HashMap<>(); - /** Handler for deferred diagnostics. */ - protected final DeferredLintHandler deferredLintHandler; - // Because of javac's limited lookahead, some contexts are ambiguous in // the presence of type annotations even though they are not ambiguous // in the absence of type annotations. Consider this code: @@ -189,7 +183,6 @@ protected JavacParser(ParserFactory fac, this.names = fac.names; this.source = fac.source; this.preview = fac.preview; - this.deferredLintHandler = fac.deferredLintHandler; this.allowStringFolding = fac.options.getBoolean("allowStringFolding", true); this.keepDocComments = keepDocComments; this.parseModuleInfo = parseModuleInfo; @@ -214,7 +207,6 @@ protected JavacParser(JavacParser parser, this.names = parser.names; this.source = parser.source; this.preview = parser.preview; - this.deferredLintHandler = parser.deferredLintHandler; this.allowStringFolding = parser.allowStringFolding; this.keepDocComments = parser.keepDocComments; this.parseModuleInfo = false; @@ -582,17 +574,8 @@ protected void checkNoMods(int pos, long mods) { * (using {@code token.getDocComment()}. * 3. At the end of the "signature" of the declaration * (that is, before any initialization or body for the - * declaration) any other "recent" comments are saved - * in a map using the primary comment as a key, - * using this method, {@code saveDanglingComments}. - * 4. When the tree node for the declaration is finally - * available, and the primary comment, if any, - * is "attached", (in {@link #attach}) any related - * dangling comments are also attached to the tree node - * by registering them using the {@link #deferredLintHandler}. - * 5. (Later) Warnings may be generated for the dangling - * comments, subject to the {@code -Xlint} and - * {@code @SuppressWarnings}. + * declaration) any other "recent" comments are + * reported to the log as warnings. * * @param dc the primary documentation comment */ @@ -612,21 +595,16 @@ private void saveDanglingDocComments(Comment dc) { } } - var lb = new ListBuffer(); while (!recentComments.isEmpty()) { var c = recentComments.remove(); if (c != dc) { - lb.add(c); + reportDanglingDocComment(c); } } - danglingComments.put(dc, lb.toList()); } /** Make an entry into docComments hashtable, * provided flag keepDocComments is set and given doc comment is non-null. - * If there are any related "dangling comments", register - * diagnostics to be handled later, when @SuppressWarnings - * can be taken into account. * * @param tree The tree to be used as index in the hashtable * @param dc The doc comment to associate with the tree, or null. @@ -636,31 +614,11 @@ protected T attach(T tree, Comment dc) { if (keepDocComments && dc != null) { docComments.putComment(tree, dc); } - reportDanglingComments(tree, dc); return tree; } - /** Reports all dangling comments associated with the - * primary comment for a declaration against the position - * of the tree node for a declaration. - * - * @param tree the tree node for the declaration - * @param dc the primary comment for the declaration - */ - void reportDanglingComments(JCTree tree, Comment dc) { - var list = danglingComments.remove(dc); - if (list != null) { - deferredLintHandler.push(tree); - try { - list.forEach(this::reportDanglingDocComment); - } finally { - deferredLintHandler.pop(); - } - } - } - /** - * Reports an individual dangling comment using the {@link #deferredLintHandler}. + * Reports an individual dangling comment as a warning to the log. * The comment may or not may generate an actual diagnostic, depending on * the settings for {@code -Xlint} and/or {@code @SuppressWarnings}. * @@ -668,14 +626,8 @@ void reportDanglingComments(JCTree tree, Comment dc) { */ void reportDanglingDocComment(Comment c) { var pos = c.getPos(); - if (pos != null) { - deferredLintHandler.report(lint -> { - if (lint.isEnabled(Lint.LintCategory.DANGLING_DOC_COMMENTS) && - !shebang(c, pos)) { - log.warning( - pos, LintWarnings.DanglingDocComment); - } - }); + if (pos != null && !shebang(c, pos)) { + S.lintWarning(pos, LintWarnings.DanglingDocComment); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.java index bff4e027db75c..1d1e08194f7f1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Lexer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -28,6 +28,8 @@ import java.util.Queue; import com.sun.tools.javac.parser.Tokens.*; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.Position.LineMap; /** @@ -103,4 +105,12 @@ public interface Lexer { * token. */ Queue getDocComments(); + + /** + * Report a warning that is subject to possible suppression by {@code @SuppressWarnings}. + * + * @param pos the lexical position at which the warning occurs + * @param key the warning to report + */ + void lintWarning(DiagnosticPosition pos, LintWarning key); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java index c873c6f31b7d3..f9e187315ba6e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java @@ -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 @@ -28,7 +28,6 @@ import java.util.Locale; import com.sun.tools.javac.api.JavacTrees; -import com.sun.tools.javac.code.DeferredLintHandler; import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; @@ -70,7 +69,6 @@ public static ParserFactory instance(Context context) { final Options options; final ScannerFactory scannerFactory; final Locale locale; - final DeferredLintHandler deferredLintHandler; private final JavacTrees trees; @@ -88,7 +86,6 @@ protected ParserFactory(Context context) { this.options = Options.instance(context); this.scannerFactory = ScannerFactory.instance(context); this.locale = context.get(Locale.class); - this.deferredLintHandler = DeferredLintHandler.instance(context); this.trees = JavacTrees.instance(context); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.java index a24e73a41410a..7fcb87eac7a8e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/Scanner.java @@ -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 @@ -31,6 +31,8 @@ import java.util.ArrayList; import java.util.Queue; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.Position.LineMap; import static com.sun.tools.javac.parser.Tokens.*; @@ -150,6 +152,11 @@ public Queue getDocComments() { return docComments; } + @Override + public void lintWarning(DiagnosticPosition pos, LintWarning key) { + tokenizer.log.warning(pos, key); + } + public int errPos() { return tokenizer.errPos(); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.java index 188fe838b187a..66f2b66fabfa1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ScannerFactory.java @@ -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 @@ -27,7 +27,6 @@ import java.nio.CharBuffer; -import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.main.Option; @@ -62,7 +61,6 @@ public static ScannerFactory instance(Context context) { final Source source; final Preview preview; final Tokens tokens; - final Lint lint; final boolean enableLineDocComments; /** Create a new scanner factory. */ @@ -74,7 +72,6 @@ protected ScannerFactory(Context context) { this.source = Source.instance(context); this.preview = Preview.instance(context); this.tokens = Tokens.instance(context); - this.lint = Lint.instance(context); var options = Options.instance(context); this.enableLineDocComments = !options.isSet(Option.DISABLE_LINE_DOC_COMMENTS); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java index ec3a373ab4ec2..2a819152eed3d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/VirtualParser.java @@ -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 @@ -30,6 +30,7 @@ import com.sun.tools.javac.tree.JCTree.JCErroneous; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Error; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Position.LineMap; @@ -167,10 +168,9 @@ public LineMap getLineMap() { return S.getLineMap(); } - public void commit() { - for (int i = 0 ; i < offset ; i++) { - S.nextToken(); // advance underlying lexer until position matches - } + @Override + public void lintWarning(DiagnosticPosition pos, LintWarning key) { + // ignore } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java index 012ac628ecd89..dc25de30b36fc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacFiler.java @@ -51,7 +51,6 @@ import static javax.tools.StandardLocation.SOURCE_OUTPUT; import static javax.tools.StandardLocation.CLASS_OUTPUT; -import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.ModuleSymbol; import com.sun.tools.javac.code.Symtab; @@ -62,7 +61,6 @@ import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.DefinedBy.Api; -import static com.sun.tools.javac.code.Lint.LintCategory.PROCESSING; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.main.Option; @@ -338,7 +336,6 @@ public synchronized void close() throws IOException { JavaFileManager fileManager; JavacElements elementUtils; Log log; - Lint lint; Modules modules; Names names; Symtab syms; @@ -421,8 +418,6 @@ public synchronized void close() throws IOException { aggregateGeneratedClassNames = new LinkedHashSet<>(); initialClassNames = new LinkedHashSet<>(); - lint = Lint.instance(context); - Options options = Options.instance(context); defaultTargetModule = options.get(Option.DEFAULT_MODULE_FOR_CREATED_FILES); @@ -486,14 +481,12 @@ private Pair checkOrInferModule(CharSequence moduleAndPkg) private JavaFileObject createSourceOrClassFile(ModuleSymbol mod, boolean isSourceFile, String name, Element... originatingElements) throws IOException { Assert.checkNonNull(mod); - if (lint.isEnabled(PROCESSING)) { - int periodIndex = name.lastIndexOf("."); - if (periodIndex != -1) { - String base = name.substring(periodIndex); - String extn = (isSourceFile ? ".java" : ".class"); - if (base.equals(extn)) - log.warning(LintWarnings.ProcSuspiciousClassName(name, extn)); - } + int periodIndex = name.lastIndexOf("."); + if (periodIndex != -1) { + String base = name.substring(periodIndex); + String extn = (isSourceFile ? ".java" : ".class"); + if (base.equals(extn)) + log.warning(LintWarnings.ProcSuspiciousClassName(name, extn)); } checkNameAndExistence(mod, name, isSourceFile); Location loc = (isSourceFile ? SOURCE_OUTPUT : CLASS_OUTPUT); @@ -707,7 +700,7 @@ private void checkName(String name) throws FilerException { private void checkName(String name, boolean allowUnnamedPackageInfo) throws FilerException { if (!SourceVersion.isName(name) && !isPackageInfo(name, allowUnnamedPackageInfo)) { - lint.logIfEnabled(LintWarnings.ProcIllegalFileName(name)); + log.warning(LintWarnings.ProcIllegalFileName(name)); throw new FilerException("Illegal name " + name); } } @@ -735,11 +728,11 @@ private void checkNameAndExistence(ModuleSymbol mod, String typename, boolean al initialClassNames.contains(typename) || containedInInitialInputs(typename); if (alreadySeen) { - lint.logIfEnabled(LintWarnings.ProcTypeRecreate(typename)); + log.warning(LintWarnings.ProcTypeRecreate(typename)); throw new FilerException("Attempt to recreate a file for type " + typename); } if (existing != null) { - lint.logIfEnabled(LintWarnings.ProcTypeAlreadyExists(typename)); + log.warning(LintWarnings.ProcTypeAlreadyExists(typename)); } if (!mod.isUnnamed() && !typename.contains(".")) { throw new FilerException("Attempt to create a type in unnamed package of a named module: " + typename); @@ -768,7 +761,7 @@ private boolean containedInInitialInputs(String typename) { */ private void checkFileReopening(FileObject fileObject, boolean forWriting) throws FilerException { if (isInFileObjectHistory(fileObject, forWriting)) { - lint.logIfEnabled(LintWarnings.ProcFileReopening(fileObject.getName())); + log.warning(LintWarnings.ProcFileReopening(fileObject.getName())); throw new FilerException("Attempt to reopen a file for path " + fileObject.getName()); } if (forWriting) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java index 7bc538a1d1e81..b28f19bd3aff8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java @@ -123,7 +123,6 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea private final Modules modules; private final Types types; private final Annotate annotate; - private final Lint lint; /** * Holds relevant state history of which processors have been @@ -206,7 +205,6 @@ protected JavacProcessingEnvironment(Context context) { printProcessorInfo = options.isSet(Option.XPRINTPROCESSORINFO); printRounds = options.isSet(Option.XPRINTROUNDS); verbose = options.isSet(Option.VERBOSE); - lint = Lint.instance(context); compiler = JavaCompiler.instance(context); if (options.isSet(Option.PROC, "only") || options.isSet(Option.XPRINT)) { compiler.shouldStopPolicyIfNoError = CompileState.PROCESS; @@ -626,7 +624,7 @@ static class ProcessorState { private Set supportedOptionNames; ProcessorState(Processor p, Log log, Source source, DeferredCompletionFailureHandler dcfh, - boolean allowModules, ProcessingEnvironment env, Lint lint) { + boolean allowModules, ProcessingEnvironment env) { processor = p; contributed = false; @@ -647,10 +645,9 @@ static class ProcessorState { boolean patternAdded = supportedAnnotationStrings.add(annotationPattern); supportedAnnotationPatterns. - add(importStringToPattern(allowModules, annotationPattern, - processor, log, lint)); + add(importStringToPattern(allowModules, annotationPattern, processor, log)); if (!patternAdded) { - lint.logIfEnabled(LintWarnings.ProcDuplicateSupportedAnnotation(annotationPattern, + log.warning(LintWarnings.ProcDuplicateSupportedAnnotation(annotationPattern, p.getClass().getName())); } } @@ -663,7 +660,7 @@ static class ProcessorState { // and "foo.bar.*". if (supportedAnnotationPatterns.contains(MatchingUtils.validImportStringToPattern("*")) && supportedAnnotationPatterns.size() > 1) { - lint.logIfEnabled(LintWarnings.ProcRedundantTypesWithWildcard(p.getClass().getName())); + log.warning(LintWarnings.ProcRedundantTypesWithWildcard(p.getClass().getName())); } supportedOptionNames = new LinkedHashSet<>(); @@ -671,8 +668,7 @@ static class ProcessorState { if (checkOptionName(optionName, log)) { boolean optionAdded = supportedOptionNames.add(optionName); if (!optionAdded) { - lint.logIfEnabled(LintWarnings.ProcDuplicateOptionName(optionName, - p.getClass().getName())); + log.warning(LintWarnings.ProcDuplicateOptionName(optionName, p.getClass().getName())); } } } @@ -759,8 +755,7 @@ public ProcessorState next() { ProcessorState ps = new ProcessorState(psi.processorIterator.next(), log, source, dcfh, Feature.MODULES.allowedInSource(source), - JavacProcessingEnvironment.this, - lint); + JavacProcessingEnvironment.this); psi.procStateList.add(ps); return ps; } else @@ -888,7 +883,7 @@ private void discoverAndRunProcs(Set annotationsPresent, } unmatchedAnnotations.remove(""); - if (lint.isEnabled(PROCESSING) && unmatchedAnnotations.size() > 0) { + if (unmatchedAnnotations.size() > 0) { // Remove annotations processed by javac unmatchedAnnotations.keySet().removeAll(platformAnnotations); if (unmatchedAnnotations.size() > 0) { @@ -1649,7 +1644,7 @@ public Set getSpecifiedPackages() { * regex matching that string. If the string is not a valid * import-style string, return a regex that won't match anything. */ - private static Pattern importStringToPattern(boolean allowModules, String s, Processor p, Log log, Lint lint) { + private static Pattern importStringToPattern(boolean allowModules, String s, Processor p, Log log) { String module; String pkg; int slash = s.indexOf('/'); @@ -1662,7 +1657,7 @@ private static Pattern importStringToPattern(boolean allowModules, String s, Pro } else { String moduleName = s.substring(0, slash); if (!SourceVersion.isName(moduleName)) { - return warnAndNoMatches(s, p, log, lint); + return warnAndNoMatches(s, p, log); } module = Pattern.quote(moduleName + "/"); // And warn if module is specified if modules aren't supported, conditional on -Xlint:proc? @@ -1671,12 +1666,12 @@ private static Pattern importStringToPattern(boolean allowModules, String s, Pro if (MatchingUtils.isValidImportString(pkg)) { return Pattern.compile(module + MatchingUtils.validImportStringToPatternString(pkg)); } else { - return warnAndNoMatches(s, p, log, lint); + return warnAndNoMatches(s, p, log); } } - private static Pattern warnAndNoMatches(String s, Processor p, Log log, Lint lint) { - lint.logIfEnabled(LintWarnings.ProcMalformedSupportedString(s, p.getClass().getName())); + private static Pattern warnAndNoMatches(String s, Processor p, Log log) { + log.warning(LintWarnings.ProcMalformedSupportedString(s, p.getClass().getName())); return noMatches; // won't match any valid identifier } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java index 59730448bf53a..8cef7c059618d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, 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 @@ -25,6 +25,7 @@ package com.sun.tools.javac.util; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import javax.tools.JavaFileObject; @@ -159,35 +160,46 @@ public void error(DiagnosticFlag flag, int pos, Error errorKey) { * maximum number of warnings has been reached. * * @param warningKey The key for the localized warning message. + * @param flags Any additional flags required */ - public void warning(Warning warningKey) { - report(diags.warning(source, null, warningKey)); + public void warning(Warning warningKey, DiagnosticFlag... flags) { + warning(null, warningKey, flags); } /** Report a warning, unless suppressed by the -nowarn option or the * maximum number of warnings has been reached. * @param pos The source position at which to report the warning. * @param warningKey The key for the localized warning message. + * @param flags Any additional flags required */ - public void warning(DiagnosticPosition pos, Warning warningKey) { - report(diags.warning(source, pos, warningKey)); + public void warning(int pos, Warning warningKey, DiagnosticFlag... flags) { + warning(wrap(pos), warningKey, flags); } /** Report a warning, unless suppressed by the -nowarn option or the * maximum number of warnings has been reached. * @param pos The source position at which to report the warning. * @param warningKey The key for the localized warning message. + * @param flags Any additional flags required */ - public void warning(int pos, Warning warningKey) { - report(diags.warning(source, wrap(pos), warningKey)); + public void warning(DiagnosticPosition pos, Warning warningKey, DiagnosticFlag... flags) { + EnumSet flagSet = EnumSet.noneOf(DiagnosticFlag.class); + for (DiagnosticFlag flag : flags) + flagSet.add(flag); + report(diags.create(flagSet, source, pos, warningKey)); } - /** Report a warning. + /** Report a mandatory warning. * @param pos The source position at which to report the warning. * @param warningKey The key for the localized warning message. - */ - public void mandatoryWarning(DiagnosticPosition pos, Warning warningKey) { - report(diags.mandatoryWarning(source, pos, warningKey)); + * @param flags Any additional flags required + */ + public void mandatoryWarning(DiagnosticPosition pos, Warning warningKey, DiagnosticFlag... flags) { + DiagnosticFlag[] flags2 = new DiagnosticFlag[flags.length + 2]; + System.arraycopy(flags, 0, flags2, 0, flags.length); + flags2[flags.length + 0] = DiagnosticFlag.MANDATORY; + flags2[flags.length + 1] = DiagnosticFlag.DEFAULT_ENABLED; + warning(pos, warningKey, flags2); } /** Provide a non-fatal notification, unless suppressed by the -nowarn option. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java index 220ec856d62fd..19f7104420e6e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java @@ -115,33 +115,6 @@ public JCDiagnostic error( return diag; } - /** - * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options. - * @param lc The lint category for the diagnostic - * @param source The source of the compilation unit, if any, in which to report the warning. - * @param pos The source position at which to report the warning. - * @param key The key for the localized warning message. - * @param args Fields of the warning message. - * @see MandatoryWarningHandler - */ - public JCDiagnostic mandatoryWarning( - LintCategory lc, - DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { - return mandatoryWarning(source, pos, warningKey(lc, key, args)); - } - - /** - * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options. - * @param source The source of the compilation unit, if any, in which to report the warning. - * @param pos The source position at which to report the warning. - * @param warningKey The key for the localized warning message. - * @see MandatoryWarningHandler - */ - public JCDiagnostic mandatoryWarning( - DiagnosticSource source, DiagnosticPosition pos, Warning warningKey) { - return create(EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, warningKey); - } - /** * Create a warning diagnostic. * @param lc The lint category for the diagnostic @@ -447,6 +420,16 @@ public enum DiagnosticFlag { RECOVERABLE, NON_DEFERRABLE, COMPRESSED, + /** Flag for lint diagnostics that should be emitted even when their category + * is not explicitly enabled, as long as it is not explicitly suppressed. + */ + DEFAULT_ENABLED, + /** Flags mandatory warnings that should pass through a mandatory warning aggregator. + */ + AGGREGATE, + /** Flag that requests verbose logging through the mandatory warning aggregator. + */ + AGGREGATE_VERBOSE, /** Flag for diagnostics that were reported through API methods. */ API, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java new file mode 100644 index 0000000000000..68ff9e28f1dcb --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java @@ -0,0 +1,390 @@ +/* + * 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.util; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import javax.tools.DiagnosticListener; +import javax.tools.JavaFileObject; + +import com.sun.tools.javac.code.Lint; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.tree.EndPosTable; +import com.sun.tools.javac.tree.JCTree; +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.JCDiagnostic.DiagnosticPosition; + +/** + * Maps source code positions to the applicable {@link Lint} instance based on {@link -Xlint} + * command line flags and {@code @SuppressWarnings} annotations on containing declarations. + * + *

+ * This mapping can't be cannot be calculated until after attribution. As each top-level + * declaration (class, package, or module) is attributed, this singleton is notified by + * Attr and the {@link Lint}s contained in that declaration are calculated. + * + *

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. + */ +public class LintMapper { + + // The key for the context singleton + private static final Context.Key CONTEXT_KEY = new Context.Key<>(); + + // Per-source file lint information + private final Map fileInfoMap = new HashMap<>(); + + // Compiler context + private final Context context; + + // This calculates the Lint instances that apply to various source code ranges + private final LintSpanCalculator lintSpanCalculator = new LintSpanCalculator(); + + // The root Lint instance, calculated on-demand to avoid init loops + private Lint rootLint; + + /** + * Obtain the {@link LintMapper} context singleton. + */ + public static LintMapper instance(Context context) { + LintMapper instance = context.get(CONTEXT_KEY); + if (instance == null) + instance = new LintMapper(context); + return instance; + } + + /** + * Constructor. + */ + @SuppressWarnings("this-escape") + protected LintMapper(Context context) { + context.put(CONTEXT_KEY, this); + this.context = context; + } + + private Lint rootLint() { + if (rootLint == null) + rootLint = Lint.instance(context); + return rootLint; + } + + /** + * Determine if the given file is known to this instance. + * + * @param sourceFile source file + * @return true if file is recognized + */ + public boolean isKnown(JavaFileObject sourceFile) { + return fileInfoMap.containsKey(sourceFile); + } + + /** + * Obtain the {@link Lint} configuration that applies at the given position, if known. + * + * @param sourceFile source file + * @param pos source position + * @return the applicable {@link Lint}, if known + */ + public Optional lintAt(JavaFileObject sourceFile, DiagnosticPosition pos) { + + // If the file is completely unknown, we don't know + FileInfo fileInfo = fileInfoMap.get(sourceFile); + if (fileInfo == null) + return Optional.empty(); + + // If the file hasn't been fully parsed yet, we don't know what Lint applies yet + if (!fileInfo.parsed) + return Optional.empty(); + + // Find the top-level declaration that contains pos; if there is none, then the root lint applies + Span declSpan = fileInfo.findDeclSpan(pos); + if (declSpan == null) + return Optional.of(rootLint()); + + // Have we attributed this top-level declaration? If not, we don't know what Lint applies yet + List lintSpans = fileInfo.lintSpanMap.get(declSpan); + if (lintSpans == null) + return Optional.empty(); + + // Find the narrowest containing LintSpan; if there is none, then the root lint applies + return FileInfo.bestMatch(lintSpans, pos) + .map(lintSpan -> lintSpan.lint) + .or(() -> Optional.of(rootLint())); + } + + /** + * Calculate {@lint Lint} configurations for all positions within the given top-level declaration. + * + * @param sourceFile source file + * @param tree top-level declaration (class, package, or module) + */ + public void calculateLints(JavaFileObject sourceFile, JCTree tree, EndPosTable endPositions) { + + // Get the info for this file + FileInfo fileInfo = fileInfoMap.get(sourceFile); + + // Sanity checks + Assert.check(isTopLevelDecl(tree)); + Assert.check(fileInfo != null && fileInfo.parsed); + Span declSpan = new Span(tree, endPositions); + Assert.check(fileInfo.lintSpanMap.containsKey(declSpan), "unknown declaration"); + Assert.check(fileInfo.lintSpanMap.get(declSpan) == null, "duplicate calculateLints()"); + + // Build the list of lints for declarations within the top-level declaration + fileInfo.lintSpanMap.put(declSpan, lintSpanCalculator.calculate(endPositions, tree)); + } + + /** + * Reset this instance (except for listeners). + */ + public void clear() { + fileInfoMap.clear(); + } + + /** + * Invoked when file parsing starts to create an entry for the new file. + */ + public void startParsingFile(JavaFileObject sourceFile) { + fileInfoMap.put(sourceFile, new FileInfo()); + } + + /** + * Invoked when file parsing completes to identify the top-level declarations. + */ + public void finishParsingFile(JCCompilationUnit tree) { + + // Get info for this file + FileInfo fileInfo = fileInfoMap.get(tree.sourcefile); + Assert.check(fileInfo != null, () -> "unknown source " + tree.sourcefile); + Assert.check(!fileInfo.parsed, () -> "source already parsed: " + tree.sourcefile); + Assert.check(fileInfo.lintSpanMap.isEmpty(), () -> "duplicate invocation for " + tree.sourcefile); + + // Mark file as parsed + fileInfo.parsed = true; + + // Create an entry in lintSpanMap for each top-level declaration, with a null value for now + tree.defs.stream() + .filter(this::isTopLevelDecl) + .map(decl -> new Span(decl, tree.endPositions)) + .forEach(span -> fileInfo.lintSpanMap.put(span, null)); + } + + private boolean isTopLevelDecl(JCTree tree) { + return tree.getTag() == Tag.MODULEDEF + || tree.getTag() == Tag.PACKAGEDEF + || tree.getTag() == Tag.CLASSDEF; + } + +// FileInfo + + /** + * Holds the calculated {@link Lint}s for top-level declarations in some source file. + * + *

+ * Instances evolve through these states: + *

+ */ + private static class FileInfo { + + final Map> lintSpanMap = new HashMap<>(); + boolean parsed; + + // Find the top-level declaration containing the given position + Span findDeclSpan(DiagnosticPosition pos) { + return lintSpanMap.keySet().stream() + .filter(span -> span.contains(pos)) + .findFirst() + .orElse(null); + } + + // Find the narrowest span in the given list that contains the given position + static Optional bestMatch(List lintSpans, DiagnosticPosition pos) { + int position = pos.getStartPosition(); + Assert.check(position != Position.NOPOS); + LintSpan bestSpan = null; + for (LintSpan lintSpan : lintSpans) { + if (lintSpan.contains(position) && (bestSpan == null || bestSpan.contains(lintSpan))) { + bestSpan = lintSpan; + } + } + return Optional.ofNullable(bestSpan); + } + } + +// Span + + /** + * Represents a lexical range in a file. + */ + private static class Span { + + final int startPos; + final int endPos; + + Span(int startPos, int endPos) { + this.startPos = startPos; + this.endPos = endPos; + } + + Span(JCTree tree, EndPosTable endPositions) { + this(TreeInfo.getStartPos(tree), TreeInfo.endPos(endPositions, tree)); + } + + boolean contains(int pos) { + return pos == startPos || (pos > startPos && pos < endPos); + } + + boolean contains(DiagnosticPosition pos) { + return contains(pos.getStartPosition()); + } + + boolean contains(Span that) { + return this.startPos <= that.startPos && this.endPos >= that.endPos; + } + + @Override + public int hashCode() { + return Integer.hashCode(startPos) ^ Integer.hashCode(endPos); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + if (obj == null || obj.getClass() != this.getClass()) + return false; + final Span that = (Span)obj; + return this.startPos == that.startPos && this.endPos == that.endPos; + } + + @Override + public String toString() { + return String.format("Span[%d-%d]", startPos, endPos); + } + } + +// LintSpan + + /** + * Represents a lexical range and the {@link Lint} configuration that applies therein. + */ + private static class LintSpan extends Span { + + final Lint lint; + + LintSpan(int startPos, int endPos, Lint lint) { + super(startPos, endPos); + this.lint = lint; + } + + LintSpan(JCTree tree, EndPosTable endPositions, Lint lint) { + super(tree, endPositions); + this.lint = lint; + } + + // Note: no need for equals() or hashCode() here + + @Override + public String toString() { + return String.format("LintSpan[%d-%d, lint=%s]", startPos, endPos, lint); + } + } + +// LintSpanCalculator + + private class LintSpanCalculator extends TreeScanner { + + private EndPosTable endPositions; + private Lint currentLint; + private List lintSpans; + + List calculate(EndPosTable endPositions, JCTree tree) { + this.endPositions = endPositions; + currentLint = rootLint(); + lintSpans = new ArrayList<>(); + try { + scan(tree); + return lintSpans; + } finally { + lintSpans = null; + } + } + + @Override + public void visitModuleDef(JCModuleDecl tree) { + scanDecl(tree, tree.sym, super::visitModuleDef); + } + + @Override + public void visitPackageDef(JCPackageDecl tree) { + scanDecl(tree, tree.packge, super::visitPackageDef); + } + + @Override + public void visitClassDef(JCClassDecl tree) { + scanDecl(tree, tree.sym, super::visitClassDef); + } + + @Override + public void visitMethodDef(JCMethodDecl tree) { + scanDecl(tree, tree.sym, super::visitMethodDef); + } + + @Override + public void visitVarDef(JCVariableDecl tree) { + scanDecl(tree, tree.sym, super::visitVarDef); + } + + private void scanDecl(T tree, Symbol symbol, Consumer recursion) { + Lint previousLint = currentLint; + currentLint = Optional.ofNullable(symbol) // symbol can be null if there were earlier errors + .map(currentLint::augment) + .orElse(currentLint); + recursion.accept(tree); + if (currentLint != previousLint) { // Lint.augment() returns the same object if no change + lintSpans.add(new LintSpan(tree, endPositions, currentLint)); + currentLint = previousLint; + } + } + } +} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index ec74c860e0a99..4cdc340bc0b7c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -30,26 +30,48 @@ import java.util.Arrays; import java.util.Comparator; import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; import java.util.function.Predicate; import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; import com.sun.tools.javac.api.DiagnosticFormatter; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Lint; +import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.Source; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.comp.AttrContext; +import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.main.Main; import com.sun.tools.javac.main.Option; +import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; import com.sun.tools.javac.tree.EndPosTable; -import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; +import com.sun.tools.javac.tree.JCTree; +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.JCDiagnostic.DiagnosticInfo; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; +import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import static com.sun.tools.javac.main.Option.*; +import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*; +import static com.sun.tools.javac.code.Lint.LintCategory.*; +import static com.sun.tools.javac.tree.JCTree.Tag.*; /** A class for error logs. Reports errors and warnings, and * keeps track of error numbers and positions. @@ -98,6 +120,11 @@ public abstract class DiagnosticHandler { */ protected final DiagnosticHandler prev; + /** + * Diagnostics waiting for an applicable {@link Lint} instance. + */ + protected Map> lintWaitersMap = new LinkedHashMap<>(); + /** * Install this diagnostic handler as the current one, * recording the previous one. @@ -111,6 +138,55 @@ protected DiagnosticHandler() { * Handle a diagnostic. */ public abstract void report(JCDiagnostic diag); + + /** + * Defer a lint warning because we don't know the {@link Lint} configuration yet. + * + * @param sourceFile the source file + * @param diagnostic waiting diagnostic + */ + public void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { + Assert.check(sourceFile != null); + Assert.check(diagnostic.getDiagnosticPosition() != null); + Assert.check(diagnostic.getLintCategory() != null); + Assert.check(diagnostic.getLintCategory().annotationSuppression); + lintWaitersMap.computeIfAbsent(sourceFile, s -> new LinkedList<>()).add(diagnostic); + } + + /** + * Flush any lint waiters whose {@link Lint} configurations are now known. + */ + public void flushLintWaiters() { + for (Iterator>> i = lintWaitersMap.entrySet().iterator(); i.hasNext(); ) { + Map.Entry> entry = i.next(); + + // Is the file no longer recognized? If so, discard warnings (this can happen with JShell) + JavaFileObject sourceFile = entry.getKey(); + if (!lintMapper.isKnown(sourceFile)) { + i.remove(); + continue; + } + + // Flush those diagnostics for which we know the Lint that applies + List diagnostics = entry.getValue(); + JavaFileObject prevSourceFile = useSource(sourceFile); + try { + for (Iterator j = diagnostics.iterator(); j.hasNext(); ) { + JCDiagnostic diag = j.next(); + lintMapper.lintAt(sourceFile, diag.getDiagnosticPosition()).ifPresent(lint -> { + applyLint(lint, diag, this::report); + j.remove(); + }); + } + } finally { + useSource(prevSourceFile); + } + + // Discard list if now empty + if (diagnostics.isEmpty()) + i.remove(); + } + } } /** @@ -120,6 +196,9 @@ public class DiscardDiagnosticHandler extends DiagnosticHandler { @Override public void report(JCDiagnostic diag) { } + + @Override + public void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { } } /** @@ -148,7 +227,7 @@ public DeferredDiagnosticHandler(Predicate filter, boolean passOnN } private boolean deferrable(JCDiagnostic diag) { - return !(diag.isFlagSet(DiagnosticFlag.NON_DEFERRABLE) && passOnNonDeferrable) && filter.test(diag); + return !(diag.isFlagSet(NON_DEFERRABLE) && passOnNonDeferrable) && filter.test(diag); } @Override @@ -160,6 +239,15 @@ public void report(JCDiagnostic diag) { } } + @Override + public void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diag) { + if (deferrable(diag)) { + super.addLintWaiter(sourceFile, diag); + } else { + prev.addLintWaiter(sourceFile, diag); + } + } + public List getDiagnostics() { return deferred; } @@ -177,6 +265,13 @@ public void reportDeferredDiagnostics(Predicate accepter) { .filter(accepter) .forEach(prev::report); deferred = null; // prevent accidental ongoing use + + // Flush matching Lint waiters to the previous handler + lintWaitersMap.forEach( + (sourceFile, diagnostics) -> diagnostics.stream() + .filter(accepter) + .forEach(diagnostic -> prev.addLintWaiter(sourceFile, diagnostic))); + lintWaitersMap = null; // prevent accidental ongoing use } /** Report all deferred diagnostics in the specified order. */ @@ -237,6 +332,26 @@ public enum WriterKind { NOTICE, WARNING, ERROR, STDOUT, STDERR } */ private JavacMessages messages; + /** + * The compilation context. + */ + private final Context context; + + /** + * The {@link Options} singleton. + */ + private final Options options; + + /** + * The lint positions table. + */ + private final LintMapper lintMapper; + + /** + * The root {@link Lint} singleton. + */ + private Lint rootLint; + /** * Handler for initial dispatch of diagnostics. */ @@ -334,6 +449,9 @@ private static Map initWriters(PrintWriter out, PrintWr private Log(Context context, Map writers) { super(JCDiagnostic.Factory.instance(context)); context.put(logKey, this); + this.context = context; + this.options = Options.instance(context); + this.lintMapper = LintMapper.instance(context); this.writers = writers; @SuppressWarnings("unchecked") // FIXME @@ -353,7 +471,6 @@ private Log(Context context, Map writers) { this.diagFormatter = new BasicDiagnosticFormatter(messages); // Once Options is ready, complete the initialization - final Options options = Options.instance(context); options.whenReady(this::initOptions); } // where @@ -517,7 +634,7 @@ private boolean shouldReport(JCDiagnostic d) { if (!shouldReport(file, d.getIntPosition())) return false; - if (!d.isFlagSet(DiagnosticFlag.SOURCE_LEVEL)) + if (!d.isFlagSet(SOURCE_LEVEL)) return true; Pair> coords = new Pair<>(file, getCode(d)); @@ -680,8 +797,22 @@ public void strictWarning(DiagnosticPosition pos, String key, Object ... args) { */ @Override public void report(JCDiagnostic diagnostic) { - diagnosticHandler.report(diagnostic); - } + LintCategory category = diagnostic.getLintCategory(); + if (category != null) { + if (category.annotationSuppression && diagnostic.getDiagnosticPosition() != null) + diagnosticHandler.addLintWaiter(currentSourceFile(), diagnostic); // subject to @SuppressWarnings + else + applyLint(rootLint(), diagnostic, diagnosticHandler::report); + } else + diagnosticHandler.report(diagnostic); + } + + /** + * Report unreported lint warnings for which the applicable {@link Lint} configuration is now known. + */ + public void reportOutstandingWarnings() { + diagnosticHandler.flushLintWaiters(); + } /** * Reset the state of this instance. @@ -695,8 +826,83 @@ public void clear() { nsuppressedwarns = 0; while (diagnosticHandler.prev != null) popDiagnosticHandler(diagnosticHandler); + aggregators.values().forEach(MandatoryWarningAggregator::clear); + suppressedDeferredMandatory.clear(); + } + + // Apply the given Lint configuration to the diagnostic and, if it survives, pass on downstream + private void applyLint(Lint lint, JCDiagnostic diag, Consumer downstream) { + LintCategory category = diag.getLintCategory(); + + // Fallback hackery for REQUIRES_TRANSITIVE_AUTOMATIC (see also Check.checkModuleRequires()) + if (diag.getCode().equals("compiler.warn.requires.transitive.automatic") && + !lint.isEnabled(REQUIRES_TRANSITIVE_AUTOMATIC)) { + diag = diags.warning(diag.getDiagnosticSource(), diag.getDiagnosticPosition(), LintWarnings.RequiresAutomatic); + category = diag.getLintCategory(); + } + + // Determine whether this diagnostic should be emitted at all + if (diag.isFlagSet(DEFAULT_ENABLED) ? isExplicitlySuppressed(lint, category) : !lint.isEnabled(category)) + return; + + // Configure verbose logging (or not) for diagnostics going through a mandatory warning aggregator + if (diag.isFlagSet(AGGREGATE) && lint.isEnabled(category)) + diag.setFlag(AGGREGATE_VERBOSE); + + // Emit the warning + downstream.accept(diag); + } + + private boolean isExplicitlySuppressed(Lint lint, LintCategory category) { + return category.annotationSuppression ? + lint.isSuppressed(category) : // suppression happens via @SuppressWarnings + options.isSet(XLINT_CUSTOM, "-" + category.option); // suppression happens via -Xlint:-category + } + + // Obtain root Lint singleton lazily to avoid init loops + private Lint rootLint() { + if (rootLint == null) + rootLint = Lint.instance(context); + return rootLint; + } + +// Mandatory Warnings + + private final EnumMap aggregators = new EnumMap<>(LintCategory.class); + + private final EnumSet suppressedDeferredMandatory = EnumSet.noneOf(LintCategory.class); + + /** + * Suppress aggregated mandatory warning notes for the specified category. + */ + public void suppressAggregatedWarningNotes(LintCategory category) { + suppressedDeferredMandatory.add(category); + } + + /** + * Report any remaining unreported aggregated mandatory warning notes. + */ + public void reportOutstandingNotes() { + aggregators.entrySet().stream() + .filter(entry -> !suppressedDeferredMandatory.contains(entry.getKey())) + .map(Map.Entry::getValue) + .map(MandatoryWarningAggregator::aggregationNotes) + .flatMap(List::stream) + .forEach(this::report); + aggregators.clear(); + } + + private MandatoryWarningAggregator aggregatorFor(LintCategory lc) { + return switch (lc) { + case PREVIEW -> aggregators.computeIfAbsent(lc, c -> new MandatoryWarningAggregator(this, Source.instance(context), c)); + case DEPRECATION -> aggregators.computeIfAbsent(lc, c -> new MandatoryWarningAggregator(this, null, c, "deprecated")); + case REMOVAL, UNCHECKED -> aggregators.computeIfAbsent(lc, c -> new MandatoryWarningAggregator(this, null, c)); + case null, default -> null; + }; } +// DefaultDiagnosticHandler + /** * Common diagnostic handling. * The diagnostic is counted, and depending on the options and how many diagnostics have been @@ -727,6 +933,15 @@ public void report(JCDiagnostic diagnostic) { break; case WARNING: + + // Apply the appropriate mandatory warning aggregator, if needed + if (diagnostic.isFlagSet(AGGREGATE)) { + boolean verbose = diagnostic.isFlagSet(AGGREGATE_VERBOSE); + if (!aggregatorFor(diagnostic.getLintCategory()).aggregate(diagnostic, verbose)) + break; + } + + // Emit warning unless not mandatory and warnings are disabled if (emitWarnings || diagnostic.isMandatory()) { if (nwarnings < MaxWarnings) { writeDiagnostic(diagnostic); @@ -738,8 +953,7 @@ public void report(JCDiagnostic diagnostic) { break; case ERROR: - if (diagnostic.isFlagSet(DiagnosticFlag.API) || - shouldReport(diagnostic)) { + if (diagnostic.isFlagSet(API) || shouldReport(diagnostic)) { if (nerrors < MaxErrors) { writeDiagnostic(diagnostic); nerrors++; @@ -749,7 +963,7 @@ public void report(JCDiagnostic diagnostic) { } break; } - if (diagnostic.isFlagSet(JCDiagnostic.DiagnosticFlag.COMPRESSED)) { + if (diagnostic.isFlagSet(COMPRESSED)) { compressedOutput = true; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningHandler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningAggregator.java similarity index 72% rename from src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningHandler.java rename to src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningAggregator.java index 046740ddc3028..b6130d384aa5d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningHandler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningAggregator.java @@ -25,11 +25,14 @@ package com.sun.tools.javac.util; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Set; import javax.tools.JavaFileObject; +import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @@ -39,9 +42,10 @@ /** - * A handler to process mandatory warnings, setting up a deferred diagnostic + * An aggregator for mandatory warnings, setting up a deferred diagnostic * to be printed at the end of the compilation if some warnings get suppressed - * because too many warnings have already been generated. + * because the lint category is not enabled or too many warnings have already + * been generated. * *

* Instances evolve through these states: *

    - *
  • Before the file has been completely parsed, {@code #parsed} is false and {@link #lintSpanMap} is empty. - *
  • Immediately after the file has been parsed, {@code #parsed} is true and {@link #lintSpanMap} contains - * zero or more entries corresponding to the top-level declarations in the file, but whose values are null. - *
  • As each top-level declaration is attributed, the entries in {@link #lintSpanMap} are updated to non-null. + *
  • Before the file has been completely parsed, {@link #topSpans} is null. + *
  • Immediately after the file has been parsed, {@link #topSpans} contains zero or + * more {@link Span}s corresponding to the top-level declarations in the file. + *
  • When a top-level declaration is attributed, a corresponding {@link DeclNode} child + * matching one of the {@link Span}s in {@link #topSpans} is added to {@link #rootNode}. *
*/ - private static class FileInfo { + private class FileInfo { + + Set topSpans; // the spans of all top level declarations + final DeclNode rootNode = new DeclNode(rootLint); // tree of file's "interesting" declaration nodes + + // Add an entry to topSpans corresponding to each top-level declaration + void afterParse(JCCompilationUnit tree) { + Assert.check(topSpans == null, "source already parsed"); + topSpans = tree.defs.stream() + .filter(this::isTopLevelDecl) + .map(decl -> new Span(decl, tree.endPositions)) + .collect(Collectors.toSet()); + } + + // Mark the given top-level declaration as attributed and build its DeclNode subtree + void afterAttr(JCTree tree, EndPosTable endPositions) { + Assert.check(topSpans != null, "source not parsed"); + Assert.check(topSpans.contains(new Span(tree, endPositions)), "unknown declaration"); + Assert.check(findTopNode(tree.pos()) == null, "duplicate call"); + new DeclNodeTreeBuilder(this, rootNode, endPositions).scan(tree); + } - final Map> lintSpanMap = new HashMap<>(); - boolean parsed; + // Find the Lint that applies to the given position, if known + Optional lintAt(DiagnosticPosition pos) { + if (topSpans == null) // has the file been parsed yet? + return Optional.empty(); // -> no, we don't know yet + if (!findTopSpan(pos).isPresent()) // is the position within some top-level declaration? + return Optional.of(rootLint); // -> no, use the root lint + DeclNode topNode = findTopNode(pos); + if (topNode == null) // has that declaration been attributed yet? + return Optional.empty(); // -> no, we don't know yet + DeclNode node = topNode.find(pos); // find the best matching node + Assert.check(node != null); // (it must exist) + return Optional.of(node.lint); // use its Lint + } - // Find the top-level declaration containing the given position - Span findDeclSpan(DiagnosticPosition pos) { - return lintSpanMap.keySet().stream() + Optional findTopSpan(DiagnosticPosition pos) { + return topSpans.stream() .filter(span -> span.contains(pos)) + .findFirst(); + } + + DeclNode findTopNode(DiagnosticPosition pos) { + return rootNode.children.stream() + .filter(node -> node.contains(pos)) .findFirst() .orElse(null); } - // Find the narrowest span in the given list that contains the given position - static Optional bestMatch(List lintSpans, DiagnosticPosition pos) { - int position = pos.getLintPosition(); - if (position == Position.NOPOS) - return Optional.empty(); - LintSpan bestSpan = null; - for (LintSpan lintSpan : lintSpans) { - if (lintSpan.contains(position) && (bestSpan == null || bestSpan.contains(lintSpan))) { - bestSpan = lintSpan; - } - } - return Optional.ofNullable(bestSpan); + void removeTopNode(DeclNode topNode) { + boolean removed = rootNode.children.remove(topNode); + Assert.check(removed); + removed = topSpans.remove(topNode.toSpan()); + Assert.check(removed); + } + + boolean isTopLevelDecl(JCTree tree) { + return tree.getTag() == Tag.MODULEDEF + || tree.getTag() == Tag.PACKAGEDEF + || tree.getTag() == Tag.CLASSDEF; } } @@ -304,51 +293,84 @@ public String toString() { } } -// LintSpan +// DeclNode /** - * Represents a lexical range and the {@link Lint} configuration that applies therein. + * Represents a declaration and the {@link Lint} configuration that applies within its lexical range. + * + *

+ * For each file, a root node represents the entire source file. At the next level down are nodes that + * represent the top-level declarations in the file, and so on. */ - private static class LintSpan extends Span { - - final Lint lint; - - LintSpan(int startPos, int endPos, Lint lint) { - super(startPos, endPos); - this.lint = lint; + private static class DeclNode extends Span { + + final DeclNode parent; // immediate containing declaration (null for root) + final List children = new ArrayList<>(); // next level down declarations contained by this node + final Symbol symbol; // the symbol declared by this declaration (null for root) + final Lint lint; // the Lint configuration that applies within this declaration + + // Create a root node representing the entire file + DeclNode(Lint rootLint) { + super(Integer.MIN_VALUE, Integer.MAX_VALUE); + this.parent = null; + this.symbol = null; + this.lint = rootLint; } - LintSpan(JCTree tree, EndPosTable endPositions, Lint lint) { + // Create a normal declaration node + DeclNode(DeclNode parent, Symbol symbol, JCTree tree, EndPosTable endPositions, Lint lint) { super(tree, endPositions); + this.parent = parent; + this.symbol = symbol; this.lint = lint; + parent.children.add(this); + } + + // Find the narrowest node in this tree that contains the given position, if any + DeclNode find(DiagnosticPosition pos) { + return children.stream() + .map(child -> child.find(pos)) + .filter(Objects::nonNull) + .reduce((a, b) -> a.contains(b) ? b : a) + .orElseGet(() -> contains(pos) ? this : null); + } + + // Stream this node and all descendents via pre-order recursive descent + Stream stream() { + return Stream.concat(Stream.of(this), children.stream().flatMap(DeclNode::stream)); + } + + Span toSpan() { + return new Span(startPos, endPos); } // Note: no need for equals() or hashCode() here @Override public String toString() { - return String.format("LintSpan[%d-%d, lint=%s]", startPos, endPos, lint); + String label = symbol != null ? "sym=" + symbol : "ROOT"; + return String.format("DeclNode[%s,lint=%s]", label, lint); } } -// LintSpanCalculator +// DeclNodeTreeBuilder + + /** + * Builds a tree of {@link DeclNode}s, starting from a top-level declaration. + */ + private class DeclNodeTreeBuilder extends TreeScanner { - private class LintSpanCalculator extends TreeScanner { + private final FileInfo fileInfo; + private final EndPosTable endPositions; - private EndPosTable endPositions; - private Lint currentLint; - private List lintSpans; + private DeclNode parent; + private Lint lint; - List calculate(EndPosTable endPositions, JCTree tree) { + DeclNodeTreeBuilder(FileInfo fileInfo, DeclNode rootNode, EndPosTable endPositions) { + this.fileInfo = fileInfo; this.endPositions = endPositions; - currentLint = rootLint(); - lintSpans = new ArrayList<>(); - try { - scan(tree); - return lintSpans; - } finally { - lintSpans = null; - } + this.parent = rootNode; + this.lint = rootNode.lint; // i.e, rootLint } @Override @@ -377,14 +399,31 @@ public void visitVarDef(JCVariableDecl tree) { } private void scanDecl(T tree, Symbol symbol, Consumer recursion) { - Lint previousLint = currentLint; - currentLint = Optional.ofNullable(symbol) // symbol can be null if there were earlier errors - .map(currentLint::augment) - .orElse(currentLint); - recursion.accept(tree); - if (currentLint != previousLint) { // Lint.augment() returns the same object if no change - lintSpans.add(new LintSpan(tree, endPositions, currentLint)); - currentLint = previousLint; + + // Symbol can be null if there were earlier errors; skip this declaration if so + if (symbol == null) { + recursion.accept(tree); + return; + } + + // Update the current Lint in effect. Note lint.augment() returns the same instance if there's no change. + Lint previousLint = lint; + lint = lint.augment(symbol); + + // If this declaration is not interesting, we don't need to create a DeclNode for it + if (lint == previousLint && parent.parent != null) { + recursion.accept(tree); + return; + } + + // Add a DeclNode here + DeclNode node = new DeclNode(parent, symbol, tree, endPositions, lint); + parent = node; + try { + recursion.accept(tree); + } finally { + parent = node.parent; + lint = previousLint; } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index 1b313aed77bc8..9b1906209d74c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -182,7 +182,7 @@ public final void reportWithLint(JCDiagnostic diag, Lint lint) { /** * Step 3: Handle a diagnostic to which the applicable Lint instance (if any) has been applied. */ - public abstract void reportReady(JCDiagnostic diag); + protected abstract void reportReady(JCDiagnostic diag); protected void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { lintWaitersMap.computeIfAbsent(sourceFile, s -> new LinkedList<>()).add(diagnostic); @@ -230,7 +230,7 @@ public class DiscardDiagnosticHandler extends DiagnosticHandler { protected void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { } @Override - public void reportReady(JCDiagnostic diag) { } + protected void reportReady(JCDiagnostic diag) { } } /** @@ -263,7 +263,7 @@ private boolean deferrable(JCDiagnostic diag) { } @Override - public void reportReady(JCDiagnostic diag) { + protected void reportReady(JCDiagnostic diag) { if (deferrable(diag)) { deferred.add(diag); } else { @@ -844,7 +844,7 @@ public void reportOutstandingWarnings() { // Get the Lint config for the given warning (if known) private Lint lintFor(JCDiagnostic diag) { Assert.check(diag.getLintCategory() != null); - return lintMapper.lintAt(currentSourceFile(), diag.getDiagnosticPosition()).orElse(null); + return lintMapper.lintAt(diag.getSource(), diag.getDiagnosticPosition()).orElse(null); } // Obtain root Lint singleton lazily to avoid init loops @@ -913,8 +913,9 @@ public void clear() { * reported so far, the diagnostic may be handed off to writeDiagnostic. */ private class DefaultDiagnosticHandler extends DiagnosticHandler { + @Override - public void reportReady(JCDiagnostic diagnostic) { + protected void reportReady(JCDiagnostic diagnostic) { if (expectDiagKeys != null) expectDiagKeys.remove(diagnostic.getCode()); From d19ad6ffc59757dd02714254ab4391dcf7c1232a Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Tue, 22 Apr 2025 21:08:30 -0500 Subject: [PATCH 28/44] Move LintMapper from "util" to "code" sub-package. --- .../classes/com/sun/tools/javac/api/JavacTaskPool.java | 2 +- .../com/sun/tools/javac/{util => code}/LintMapper.java | 6 +++--- .../share/classes/com/sun/tools/javac/comp/Attr.java | 2 +- .../com/sun/tools/javac/comp/ThisEscapeAnalyzer.java | 2 +- .../classes/com/sun/tools/javac/main/JavaCompiler.java | 1 - .../share/classes/com/sun/tools/javac/util/Log.java | 1 + 6 files changed, 7 insertions(+), 7 deletions(-) rename src/jdk.compiler/share/classes/com/sun/tools/javac/{util => code}/LintMapper.java (99%) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java index fc91bc13a6bc5..65772cdccb3f0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java @@ -55,6 +55,7 @@ import com.sun.source.util.TaskListener; import com.sun.source.util.TreeScanner; import com.sun.tools.javac.code.Kinds; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symtab; @@ -76,7 +77,6 @@ import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; -import com.sun.tools.javac.util.LintMapper; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Options; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java similarity index 99% rename from src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java rename to src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java index cb3377f396e24..a71178b31c7be 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java @@ -23,7 +23,7 @@ * questions. */ -package com.sun.tools.javac.util; +package com.sun.tools.javac.code; import java.util.ArrayList; import java.util.Comparator; @@ -40,13 +40,13 @@ import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; 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.DiagnosticPosition; /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index caa002bc41147..24ee2ac587ad4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -41,6 +41,7 @@ import com.sun.source.util.SimpleTreeVisitor; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.code.Scope.WriteableScope; import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Symbol.*; @@ -69,7 +70,6 @@ import com.sun.tools.javac.util.JCDiagnostic.Error; import com.sun.tools.javac.util.JCDiagnostic.Fragment; import com.sun.tools.javac.util.JCDiagnostic.Warning; -import com.sun.tools.javac.util.LintMapper; import com.sun.tools.javac.util.List; import static com.sun.tools.javac.code.Flags.*; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index 23c20320c6649..5e5313bbf2f90 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -48,6 +48,7 @@ import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Symtab; @@ -64,7 +65,6 @@ import com.sun.tools.javac.util.JCDiagnostic; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.LintWarning; -import com.sun.tools.javac.util.LintMapper; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Names; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 81dc219bf50ef..927c62e6e21eb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -79,7 +79,6 @@ import com.sun.tools.javac.util.Context.Key; import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.JCDiagnostic.Factory; -import com.sun.tools.javac.util.LintMapper; import com.sun.tools.javac.util.Log.DiagnosticHandler; import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler; import com.sun.tools.javac.util.Log.WriterKind; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index 9b1906209d74c..fff623e55a112 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -49,6 +49,7 @@ import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.LintMapper; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.comp.AttrContext; From 5f003d1ca8affc030e58d70c77836773529e3902 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Wed, 23 Apr 2025 10:03:08 -0500 Subject: [PATCH 29/44] Update copyrights. --- .../share/classes/com/sun/tools/javac/comp/Lower.java | 2 +- .../share/classes/com/sun/tools/javac/comp/TransPatterns.java | 2 +- src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java | 2 +- .../share/classes/com/sun/tools/javac/tree/EndPosTable.java | 2 +- src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java | 2 +- .../tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java | 2 +- test/langtools/tools/javac/parser/JavacParserTest.java | 2 +- test/langtools/tools/javac/parser/extend/TrialParser.java | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index 7c325f5afdfdb..d991af3c82ce4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -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 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java index 462b60214ac67..7440ff8b66766 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index 03d1c4e21fd4e..34b1b9dd2a37c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -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 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java index c81e7e8d6d618..650d99b222dab 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 diff --git a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java index ce16eb2e699c7..0219fa0eaf88e 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 diff --git a/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java b/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java index eae937fb03fa3..95a9ef2dd5376 100644 --- a/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java +++ b/test/langtools/tools/javac/T8180660/MissingLNTEntryForFinalizerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 diff --git a/test/langtools/tools/javac/parser/JavacParserTest.java b/test/langtools/tools/javac/parser/JavacParserTest.java index ee80553ba74d4..191c01cbcf858 100644 --- a/test/langtools/tools/javac/parser/JavacParserTest.java +++ b/test/langtools/tools/javac/parser/JavacParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 diff --git a/test/langtools/tools/javac/parser/extend/TrialParser.java b/test/langtools/tools/javac/parser/extend/TrialParser.java index f508b23f4033b..c9a858fbc47ef 100644 --- a/test/langtools/tools/javac/parser/extend/TrialParser.java +++ b/test/langtools/tools/javac/parser/extend/TrialParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 From 3cd938a2bf9e4d6c9bb191b48d508f81ee064757 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Thu, 24 Apr 2025 17:42:53 -0500 Subject: [PATCH 30/44] Cleanups. --- .../com/sun/tools/javac/code/LintMapper.java | 126 +++++++----------- .../tools/javac/comp/ThisEscapeAnalyzer.java | 73 +++++----- .../com/sun/tools/javac/util/AbstractLog.java | 9 +- .../classes/com/sun/tools/javac/util/Log.java | 2 +- 4 files changed, 87 insertions(+), 123 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java index a71178b31c7be..8b35cc197e717 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java @@ -32,7 +32,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -50,13 +49,17 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; /** - * Maps source code positions to the applicable {@link Lint} instance based on {@link -Xlint} - * command line flags and {@code @SuppressWarnings} annotations on containing declarations. + * Maps source code positions to the applicable {@link Lint} instance. * *

- * This mapping can't be cannot be calculated until after attribution. As each top-level - * declaration (class, package, or module) is attributed, this singleton is notified by - * Attr and the {@link Lint}s contained in that declaration are calculated. + * Because {@code @SuppressWarnings} is a Java symbol, in general this mapping can't be be + * calculated until after attribution. As each top-level declaration (class, package, or module) + * is attributed, this singleton is notified and the {@link Lint}s that apply to every source + * position within that top-level declaration are calculated. + * + *

+ * The method {@link #lintAt} returns the {@link Lint} instance applicable to source position; + * if it can't be determined yet, an empty {@link Optional} is returned. * *

This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. @@ -135,7 +138,7 @@ public Optional lintAt(JavaFileObject sourceFile, DiagnosticPosition pos) * @param tree top-level declaration (class, package, or module) */ public void calculateLints(JavaFileObject sourceFile, JCTree tree, EndPosTable endPositions) { - initializeIfNeeded(); + Assert.check(rootLint != null); fileInfoMap.get(sourceFile).afterAttr(tree, endPositions); } @@ -160,45 +163,29 @@ public void startParsingFile(JavaFileObject sourceFile) { * Invoked when file parsing completes to identify the top-level declarations. */ public void finishParsingFile(JCCompilationUnit tree) { + Assert.check(rootLint != null); fileInfoMap.get(tree.sourcefile).afterParse(tree); } // FileInfo /** - * Holds {@link Lint} related information for one source file. + * Holds {@link Lint} information for one source file. * *

- * Instances evolve through these states: + * Instances evolve through three states: *

    *
  • Before the file has been completely parsed, {@link #topSpans} is null. - *
  • Immediately after the file has been parsed, {@link #topSpans} contains zero or - * more {@link Span}s corresponding to the top-level declarations in the file. - *
  • When a top-level declaration is attributed, a corresponding {@link DeclNode} child - * matching one of the {@link Span}s in {@link #topSpans} is added to {@link #rootNode}. + *
  • Immediately after the file has been parsed, {@link #topSpans} contains zero or more {@link Span}s + * corresponding to the top-level declarations in the file, and {@code rootNode} has no children. + *
  • When a top-level declaration is attributed, a corresponding {@link DeclNode} child matching one + * of the {@link Span}s in {@link #topSpans} is created and added to {@link #rootNode}. *
*/ private class FileInfo { - Set topSpans; // the spans of all top level declarations - final DeclNode rootNode = new DeclNode(rootLint); // tree of file's "interesting" declaration nodes - - // Add an entry to topSpans corresponding to each top-level declaration - void afterParse(JCCompilationUnit tree) { - Assert.check(topSpans == null, "source already parsed"); - topSpans = tree.defs.stream() - .filter(this::isTopLevelDecl) - .map(decl -> new Span(decl, tree.endPositions)) - .collect(Collectors.toSet()); - } - - // Mark the given top-level declaration as attributed and build its DeclNode subtree - void afterAttr(JCTree tree, EndPosTable endPositions) { - Assert.check(topSpans != null, "source not parsed"); - Assert.check(topSpans.contains(new Span(tree, endPositions)), "unknown declaration"); - Assert.check(findTopNode(tree.pos()) == null, "duplicate call"); - new DeclNodeTreeBuilder(this, rootNode, endPositions).scan(tree); - } + List topSpans; // the spans of all top level declarations + final DeclNode rootNode = new DeclNode(rootLint); // tree of file's "interesting" declaration nodes // Find the Lint that applies to the given position, if known Optional lintAt(DiagnosticPosition pos) { @@ -209,11 +196,24 @@ Optional lintAt(DiagnosticPosition pos) { DeclNode topNode = findTopNode(pos); if (topNode == null) // has that declaration been attributed yet? return Optional.empty(); // -> no, we don't know yet - DeclNode node = topNode.find(pos); // find the best matching node - Assert.check(node != null); // (it must exist) + DeclNode node = topNode.find(pos); // find the best matching node (it must exist) return Optional.of(node.lint); // use its Lint } + void afterParse(JCCompilationUnit tree) { + Assert.check(topSpans == null, "source already parsed"); + topSpans = tree.defs.stream() + .filter(this::isTopLevelDecl) + .map(decl -> new Span(decl, tree.endPositions)) + .collect(Collectors.toList()); + } + + void afterAttr(JCTree tree, EndPosTable endPositions) { + Assert.check(topSpans != null, "source not parsed"); + Assert.check(findTopNode(tree.pos()) == null, "duplicate call"); + new DeclNodeTreeBuilder(rootNode, endPositions).scan(tree); + } + Optional findTopSpan(DiagnosticPosition pos) { return topSpans.stream() .filter(span -> span.contains(pos)) @@ -227,13 +227,6 @@ DeclNode findTopNode(DiagnosticPosition pos) { .orElse(null); } - void removeTopNode(DeclNode topNode) { - boolean removed = rootNode.children.remove(topNode); - Assert.check(removed); - removed = topSpans.remove(topNode.toSpan()); - Assert.check(removed); - } - boolean isTopLevelDecl(JCTree tree) { return tree.getTag() == Tag.MODULEDEF || tree.getTag() == Tag.PACKAGEDEF @@ -272,21 +265,6 @@ boolean contains(Span that) { return this.startPos <= that.startPos && this.endPos >= that.endPos; } - @Override - public int hashCode() { - return Integer.hashCode(startPos) ^ Integer.hashCode(endPos); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) - return true; - if (obj == null || obj.getClass() != this.getClass()) - return false; - final Span that = (Span)obj; - return this.startPos == that.startPos && this.endPos == that.endPos; - } - @Override public String toString() { return String.format("Span[%d-%d]", startPos, endPos); @@ -299,29 +277,29 @@ public String toString() { * Represents a declaration and the {@link Lint} configuration that applies within its lexical range. * *

- * For each file, a root node represents the entire source file. At the next level down are nodes that - * represent the top-level declarations in the file, and so on. + * For each file, there is a root node represents the entire source file. At the next level down are + * nodes representing the top-level declarations in the file, and so on. */ private static class DeclNode extends Span { - final DeclNode parent; // immediate containing declaration (null for root) - final List children = new ArrayList<>(); // next level down declarations contained by this node final Symbol symbol; // the symbol declared by this declaration (null for root) - final Lint lint; // the Lint configuration that applies within this declaration + final DeclNode parent; // the immediately containing declaration (null for root) + final List children = new ArrayList<>(); // the immediately next level down declarations under this node + final Lint lint; // the Lint configuration that applies at this declaration // Create a root node representing the entire file DeclNode(Lint rootLint) { super(Integer.MIN_VALUE, Integer.MAX_VALUE); - this.parent = null; this.symbol = null; + this.parent = null; this.lint = rootLint; } // Create a normal declaration node - DeclNode(DeclNode parent, Symbol symbol, JCTree tree, EndPosTable endPositions, Lint lint) { + DeclNode(Symbol symbol, DeclNode parent, JCTree tree, EndPosTable endPositions, Lint lint) { super(tree, endPositions); - this.parent = parent; this.symbol = symbol; + this.parent = parent; this.lint = lint; parent.children.add(this); } @@ -340,12 +318,6 @@ Stream stream() { return Stream.concat(Stream.of(this), children.stream().flatMap(DeclNode::stream)); } - Span toSpan() { - return new Span(startPos, endPos); - } - - // Note: no need for equals() or hashCode() here - @Override public String toString() { String label = symbol != null ? "sym=" + symbol : "ROOT"; @@ -356,18 +328,16 @@ public String toString() { // DeclNodeTreeBuilder /** - * Builds a tree of {@link DeclNode}s, starting from a top-level declaration. + * Builds a tree of {@link DeclNode}s starting from a top-level declaration. */ private class DeclNodeTreeBuilder extends TreeScanner { - private final FileInfo fileInfo; private final EndPosTable endPositions; private DeclNode parent; private Lint lint; - DeclNodeTreeBuilder(FileInfo fileInfo, DeclNode rootNode, EndPosTable endPositions) { - this.fileInfo = fileInfo; + DeclNodeTreeBuilder(DeclNode rootNode, EndPosTable endPositions) { this.endPositions = endPositions; this.parent = rootNode; this.lint = rootNode.lint; // i.e, rootLint @@ -400,24 +370,24 @@ public void visitVarDef(JCVariableDecl tree) { private void scanDecl(T tree, Symbol symbol, Consumer recursion) { - // Symbol can be null if there were earlier errors; skip this declaration if so + // "symbol" can be null if there were earlier errors; skip this declaration if so if (symbol == null) { recursion.accept(tree); return; } - // Update the current Lint in effect. Note lint.augment() returns the same instance if there's no change. + // Update the current Lint in effect; note lint.augment() returns the same instance if there's no change Lint previousLint = lint; lint = lint.augment(symbol); - // If this declaration is not interesting, we don't need to create a DeclNode for it + // If this declaration is not "interesting", we don't need to create a DeclNode for it if (lint == previousLint && parent.parent != null) { recursion.accept(tree); return; } // Add a DeclNode here - DeclNode node = new DeclNode(parent, symbol, tree, endPositions, lint); + DeclNode node = new DeclNode(symbol, parent, tree, endPositions, lint); parent = node; try { recursion.accept(tree); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index 5e5313bbf2f90..df1e6aba5ae6a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -170,7 +170,7 @@ public class ThisEscapeAnalyzer extends TreeScanner { /** Environment for symbol lookup. */ - private Env attrEnv; + private Env topLevelEnv; /** Maps symbols of all methods to their corresponding declarations. */ @@ -187,7 +187,7 @@ public class ThisEscapeAnalyzer extends TreeScanner { /** Snapshots of {@link #callStack} where possible 'this' escapes occur. */ - private final ArrayList warningList = new ArrayList<>(); + private final java.util.List warningList = new ArrayList<>(); // These fields are scoped to the constructor being analyzed @@ -200,7 +200,7 @@ public class ThisEscapeAnalyzer extends TreeScanner { * constructor we started with, and subsequent entries correspond to invoked methods. * If we're still in the initial constructor, the list will be empty. */ - private final ArrayList callStack = new ArrayList<>(); + private final java.util.List callStack = new ArrayList<>(); /** Used to terminate recursion in {@link #invokeInvokable invokeInvokable()}. */ @@ -243,11 +243,11 @@ protected ThisEscapeAnalyzer(Context context) { // public void analyzeTree(Env env) { - attrEnv = env; + topLevelEnv = env; try { doAnalyzeTree(env); } finally { - attrEnv = null; + topLevelEnv = null; methodMap.clear(); nonPublicOuters.clear(); targetClass = null; @@ -282,7 +282,6 @@ private void doAnalyzeTree(Env env) { // Build a mapping from symbols of methods to their declarations. // Classify all ctors and methods as analyzable and/or invokable. - // Track which constructors and fields don't need to be analyzed. // Record classes whose outer instance (if any) is non-public. new TreeScanner() { @@ -763,13 +762,13 @@ record ForeachMethods(MethodSymbol iterator, MethodSymbol hasNext, MethodSymbol MethodSymbol hasNext = null; MethodSymbol next = null; if (elemType == null) { - Symbol iteratorSym = rs.resolveQualifiedMethod(tree.expr.pos(), attrEnv, + Symbol iteratorSym = rs.resolveQualifiedMethod(tree.expr.pos(), topLevelEnv, tree.expr.type, names.iterator, List.nil(), List.nil()); if (iteratorSym instanceof MethodSymbol) { iterator = (MethodSymbol)iteratorSym; - Symbol hasNextSym = rs.resolveQualifiedMethod(tree.expr.pos(), attrEnv, + Symbol hasNextSym = rs.resolveQualifiedMethod(tree.expr.pos(), topLevelEnv, iterator.getReturnType(), names.hasNext, List.nil(), List.nil()); - Symbol nextSym = rs.resolveQualifiedMethod(tree.expr.pos(), attrEnv, + Symbol nextSym = rs.resolveQualifiedMethod(tree.expr.pos(), topLevelEnv, iterator.getReturnType(), names.next, List.nil(), List.nil()); if (hasNextSym instanceof MethodSymbol) hasNext = (MethodSymbol)hasNextSym; @@ -1397,7 +1396,7 @@ public final String toString() { + "[" + properties.stream().collect(Collectors.joining(",")) + "]"; } - protected void addProperties(ArrayList properties) { + protected void addProperties(java.util.List properties) { } // Return a modified copy of this Ref's Indirections. The modified set must not be empty. @@ -1527,7 +1526,7 @@ public boolean equals(Object obj) { } @Override - protected void addProperties(ArrayList properties) { + protected void addProperties(java.util.List properties) { super.addProperties(properties); properties.add("depth=" + depth); } @@ -1607,7 +1606,7 @@ public boolean equals(Object obj) { } @Override - protected void addProperties(ArrayList properties) { + protected void addProperties(java.util.List properties) { super.addProperties(properties); properties.add("sym=" + sym); } @@ -1710,18 +1709,14 @@ private class StackFrame { final MethodInfo method; // the method containing the statement final JCTree site; // the call site within the method - final JCTree initializer; // originating field or init block, else null + final JCTree initializer; // originating field or initialization block, else null final boolean suppressible; // whether warning can be suppressed at this frame StackFrame(MethodInfo method, JCTree initializer, JCTree site) { this.method = method; this.initializer = initializer; - this.suppressible = initializer != null || (method.constructor && method.declaringClass == targetClass); this.site = site; - } - - int pos() { - return site.pos().getPreferredPosition(); + this.suppressible = initializer != null || (method.constructor && method.declaringClass == targetClass); } DiagnosticPosition warningPos() { @@ -1729,15 +1724,22 @@ DiagnosticPosition warningPos() { } Lint lint() { - return lintMapper.lintAt(attrEnv.toplevel.sourcefile, site.pos()).get(); + return lintMapper.lintAt(topLevelEnv.toplevel.sourcefile, site.pos()).get(); + } + + boolean isSuppressed() { + return suppressible && !lint().isEnabled(THIS_ESCAPE); + } + + int comparePos(StackFrame that) { + return Integer.compare(this.site.pos().getPreferredPosition(), that.site.pos().getPreferredPosition()); } @Override public String toString() { return "StackFrame" - + "[" + method.declaration.sym + "@" + pos() + + "[" + method.declaration.sym + "@" + site.pos().getPreferredPosition() + (initializer != null ? ",init@" + initializer.pos().getPreferredPosition() : "") - + (suppressible ? ",suppressible" : "") + "]"; } } @@ -1748,36 +1750,31 @@ public String toString() { private class Warning { final JCClassDecl declaringClass; // the class whose instance is leaked - final ArrayList stack; // the call stack where the leak happens + final java.util.List stack; // the call stack where the leak happens final JCTree origin; // the originating ctor, field, or init block - Warning(JCClassDecl declaringClass, ArrayList stack) { + Warning(JCClassDecl declaringClass, java.util.List stack) { this.declaringClass = declaringClass; this.stack = stack; this.origin = stack.stream() .map(frame -> frame.initializer) .filter(Objects::nonNull) .findFirst() - .orElseGet(() -> initialConstructor().declaration); - } - - // Get the initial constructor that generated this warning, which is found at the bottom of the call stack - MethodInfo initialConstructor() { - return stack.get(0).method; + .orElseGet(() -> stack.get(0).method.declaration); // default to the initial constructor } // Used to eliminate redundant warnings. Warning A is redundant with warning B if the call stack of A includes - // the call stack of B plus additional outer frame(s). For example, if constructor B = Foo(int x) generates a - // warning, then generating another warning for some constructor A where it invokes this(123) would be redundant. + // the call stack of B plus additional initial frame(s). For example, if constructor B = Foo(int x) generates a + // warning, then generating warning for some other constructor A when it invokes this(123) would be redundant. boolean isRedundantWith(Warning that) { int numExtra = this.stack.size() - that.stack.size(); return numExtra >= 0 && IntStream.range(0, that.stack.size()) - .allMatch(i -> this.stack.get(numExtra + i).pos() == that.stack.get(i).pos()); + .allMatch(index -> this.stack.get(numExtra + index).comparePos(that.stack.get(index)) == 0); }; - // Order warnings by stack frame lexicographically from top to bottom, which will cause all - // warnings that are isRedundantWith() some other warning to immediately follow that warning. + // Order warnings by their stack frames, lexicographically in reverse calling order, which will cause + // all warnings that are isRedundantWith() some other warning to immediately follow that warning. static int sortByStackFrames(Warning warning1, Warning warning2) { int index1 = warning1.stack.size(); int index2 = warning2.stack.size(); @@ -1790,7 +1787,7 @@ static int sortByStackFrames(Warning warning1, Warning warning2) { return -1; if (end2) return 1; - int diff = Integer.compare(warning1.stack.get(index1).pos(), warning2.stack.get(index2).pos()); + int diff = warning1.stack.get(index1).comparePos(warning2.stack.get(index2)); if (diff != 0) return diff; } @@ -1803,10 +1800,8 @@ static int sortByStackFrames(Warning warning1, Warning warning2) { // on regular methods are ignored. We work our way back up the call stack from the point of the leak until we // encounter a suppressible stack frame. boolean isSuppressed() { - int index = stack.size(); - while (--index >= 0) { - StackFrame frame = stack.get(index); - if (frame.suppressible && !frame.lint().isEnabled(THIS_ESCAPE)) + for (int index = stack.size() - 1; index >= 0; index--) { + if (stack.get(index).isSuppressed()) return true; } return false; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java index 8cef7c059618d..2e9da31b0204b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java @@ -195,11 +195,10 @@ public void warning(DiagnosticPosition pos, Warning warningKey, DiagnosticFlag.. * @param flags Any additional flags required */ public void mandatoryWarning(DiagnosticPosition pos, Warning warningKey, DiagnosticFlag... flags) { - DiagnosticFlag[] flags2 = new DiagnosticFlag[flags.length + 2]; - System.arraycopy(flags, 0, flags2, 0, flags.length); - flags2[flags.length + 0] = DiagnosticFlag.MANDATORY; - flags2[flags.length + 1] = DiagnosticFlag.DEFAULT_ENABLED; - warning(pos, warningKey, flags2); + EnumSet flagSet = EnumSet.of(DiagnosticFlag.MANDATORY, DiagnosticFlag.DEFAULT_ENABLED); + for (DiagnosticFlag flag : flags) + flagSet.add(flag); + report(diags.create(flagSet, source, pos, warningKey)); } /** Provide a non-fatal notification, unless suppressed by the -nowarn option. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index fff623e55a112..66b8ead65e794 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -167,7 +167,7 @@ public final void reportWithLint(JCDiagnostic diag, Lint lint) { // Apply the lint configuration (if any) and discard the warning if it gets filtered out if (lint != null) { LintCategory category = diag.getLintCategory(); - boolean emit = !diag.isFlagSet(DEFAULT_ENABLED) ? // is the warning not enabled by default? + boolean emit = !diag.isFlagSet(DEFAULT_ENABLED) ? // is the warning not enabled by default? lint.isEnabled(category) : // then emit if the category is enabled category.annotationSuppression ? // else emit if the category is not suppressed, where !lint.isSuppressed(category) : // ...suppression happens via @SuppressWarnings From 5ef9606fc837e30d4463943e871349c2bf59b8a8 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Mon, 28 Apr 2025 11:48:57 -0500 Subject: [PATCH 31/44] Refactor ThisEscapeAnalyzer to correct bug in suppression logic. --- .../tools/javac/comp/ThisEscapeAnalyzer.java | 446 ++++++++++-------- .../tools/javac/warnings/ThisEscape.java | 20 +- 2 files changed, 272 insertions(+), 194 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index 77ddd5c65833e..8710e22ab5a57 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -25,9 +25,7 @@ package com.sun.tools.javac.comp; -import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Comparator; import java.util.LinkedHashMap; import java.util.EnumSet; import java.util.HashSet; @@ -35,12 +33,13 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.function.BiPredicate; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collector; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import com.sun.tools.javac.code.Directive; @@ -52,21 +51,20 @@ import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; -import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.tree.JCTree; 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.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Pair; import static com.sun.tools.javac.code.Kinds.Kind.*; +import static com.sun.tools.javac.code.Lint.LintCategory.THIS_ESCAPE; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.tree.JCTree.Tag.*; @@ -164,7 +162,7 @@ public class ThisEscapeAnalyzer extends TreeScanner { /** Environment for symbol lookup. */ - private Env attrEnv; + private Env topLevelEnv; /** Maps symbols of all methods to their corresponding declarations. */ @@ -185,30 +183,25 @@ public class ThisEscapeAnalyzer extends TreeScanner { /** Snapshots of {@link #callStack} where possible 'this' escapes occur. */ - private final ArrayList warningList = new ArrayList<>(); + private final ArrayList warningList = new ArrayList<>(); // These fields are scoped to the constructor being analyzed - /** The declaring class of the "invoked" method we're currently analyzing. + /** The method we're currently analyzing. * This is either the analyzed constructor or some method it invokes. */ - private JCClassDecl methodClass; + private MethodInfo currentMethod; - /** The current "call stack" during our analysis. The first entry is some method - * invoked from the target constructor; if empty, we're still in the constructor. + /** The current "call stack" during our analysis. The first entry is the initial + * constructor we started with, and subsequent entries correspond to invoked methods. + * If we're still in the initial constructor, the list will be empty. */ - private final ArrayDeque callStack = new ArrayDeque<>(); + private final ArrayList callStack = new ArrayList<>(); /** Used to terminate recursion in {@link #invokeInvokable invokeInvokable()}. */ private final Set>> invocations = new HashSet<>(); - /** Snapshot of {@link #callStack} where a possible 'this' escape occurs. - * If non-null, a 'this' escape warning has been found in the current - * constructor statement, initialization block statement, or field initializer. - */ - private DiagnosticPosition[] pendingWarning; - // These fields are scoped to the constructor or invoked method being analyzed /** Current lexical scope depth in the constructor or method we're currently analyzing. @@ -246,18 +239,18 @@ protected ThisEscapeAnalyzer(Context context) { // public void analyzeTree(Env env) { + topLevelEnv = env; try { doAnalyzeTree(env); } finally { - attrEnv = null; + topLevelEnv = null; methodMap.clear(); nonPublicOuters.clear(); targetClass = null; warningList.clear(); - methodClass = null; + currentMethod = null; callStack.clear(); invocations.clear(); - pendingWarning = null; depth = -1; refs = null; } @@ -270,7 +263,7 @@ private void doAnalyzeTree(Env env) { Assert.check(methodMap.isEmpty()); // we are not prepared to be used more than once // Short circuit if warnings are totally disabled - if (!lint.isEnabled(Lint.LintCategory.THIS_ESCAPE)) + if (!lint.isEnabled(THIS_ESCAPE)) return; // Determine which packages are exported by the containing module, if any. @@ -324,7 +317,7 @@ public void visitVarDef(JCVariableDecl tree) { try { // Track warning suppression of fields - if (tree.sym.owner.kind == TYP && !lint.isEnabled(Lint.LintCategory.THIS_ESCAPE)) + if (tree.sym.owner.kind == TYP && !lint.isEnabled(THIS_ESCAPE)) suppressed.add(tree.sym); // Recurse @@ -341,23 +334,23 @@ public void visitMethodDef(JCMethodDecl tree) { try { // Track warning suppression of constructors - if (TreeInfo.isConstructor(tree) && !lint.isEnabled(Lint.LintCategory.THIS_ESCAPE)) + if (TreeInfo.isConstructor(tree) && !lint.isEnabled(THIS_ESCAPE)) suppressed.add(tree.sym); + // Gather some useful info + boolean constructor = TreeInfo.isConstructor(tree); + boolean extendableClass = currentClassIsExternallyExtendable(); + boolean nonPrivate = (tree.sym.flags() & (Flags.PUBLIC | Flags.PROTECTED)) != 0; + boolean finalish = (tree.mods.flags & (Flags.STATIC | Flags.PRIVATE | Flags.FINAL)) != 0; + // Determine if this is a constructor we should analyze - boolean extendable = currentClassIsExternallyExtendable(); - boolean analyzable = extendable && - TreeInfo.isConstructor(tree) && - (tree.sym.flags() & (Flags.PUBLIC | Flags.PROTECTED)) != 0 && - !suppressed.contains(tree.sym); + boolean analyzable = extendableClass && constructor && nonPrivate; - // Determine if this method is "invokable" in an analysis (can't be overridden) - boolean invokable = !extendable || - TreeInfo.isConstructor(tree) || - (tree.mods.flags & (Flags.STATIC | Flags.PRIVATE | Flags.FINAL)) != 0; + // Determine if it's safe to "invoke" the method in an analysis (i.e., it can't be overridden) + boolean invokable = !extendableClass || constructor || finalish; - // Add method or constructor to map - methodMap.put(tree.sym, new MethodInfo(currentClass, tree, analyzable, invokable)); + // Add this method or constructor to our map + methodMap.put(tree.sym, new MethodInfo(currentClass, tree, constructor, analyzable, invokable)); // Recurse super.visitMethodDef(tree); @@ -377,106 +370,54 @@ private boolean currentClassIsExternallyExtendable() { } }.scan(env.tree); - // Analyze non-static field initializers and initialization blocks, - // but only for classes having at least one analyzable constructor. - methodMap.values().stream() - .filter(MethodInfo::analyzable) - .map(MethodInfo::declaringClass) - .distinct() - .forEach(klass -> { - for (List defs = klass.defs; defs.nonEmpty(); defs = defs.tail) { - - // Ignore static stuff - if ((TreeInfo.flags(defs.head) & Flags.STATIC) != 0) - continue; - - // Handle field initializers - if (defs.head instanceof JCVariableDecl vardef) { - visitTopLevel(env, klass, () -> { - scan(vardef); - copyPendingWarning(); - }); - continue; - } - - // Handle initialization blocks - if (defs.head instanceof JCBlock block) { - visitTopLevel(env, klass, () -> analyzeStatements(block.stats)); - continue; - } - } - }); - - // Analyze all of the analyzable constructors we found + // Analyze the analyzable constructors we found methodMap.values().stream() - .filter(MethodInfo::analyzable) - .forEach(methodInfo -> { - visitTopLevel(env, methodInfo.declaringClass(), - () -> analyzeStatements(methodInfo.declaration().body.stats)); - }); - - // Eliminate duplicate warnings. Warning B duplicates warning A if the stack trace of A is a prefix - // of the stack trace of B. For example, if constructor Foo(int x) has a leak, and constructor - // Foo() invokes this(0), then emitting a warning for Foo() would be redundant. - BiPredicate extendsAsPrefix = (warning1, warning2) -> { - if (warning2.length < warning1.length) + .filter(MethodInfo::analyzable) + .forEach(this::analyzeConstructor); + + // Manually apply any Lint suppression + filterWarnings(warning -> !warning.isSuppressed()); + + // Field intitializers and initialization blocks will generate a separate warning for each primary constructor. + // Trim off stack frames up through the super() call so these will have identical stacks and get de-duplicated below. + warningList.forEach(Warning::trimInitializerFrames); + + // Sort warnings so redundant warnings immediately follow whatever they are redundant for, then remove them + warningList.sort(Warning::sortByStackFrames); + AtomicReference previousRef = new AtomicReference<>(); + filterWarnings(warning -> { + Warning previous = previousRef.get(); + if (previous != null && warning.isRedundantWith(previous)) return false; - for (int index = 0; index < warning1.length; index++) { - if (warning2[index].getPreferredPosition() != warning1[index].getPreferredPosition()) - return false; - } + previousRef.set(warning); return true; - }; + }); - // Stack traces are ordered top to bottom, and so duplicates always have the same first element(s). - // Sort the stack traces lexicographically, so that duplicates immediately follow what they duplicate. - Comparator ordering = (warning1, warning2) -> { - for (int index1 = 0, index2 = 0; true; index1++, index2++) { - boolean end1 = index1 >= warning1.length; - boolean end2 = index2 >= warning2.length; - if (end1 && end2) - return 0; - if (end1) - return -1; - if (end2) - return 1; - int posn1 = warning1[index1].getPreferredPosition(); - int posn2 = warning2[index2].getPreferredPosition(); - int diff = Integer.compare(posn1, posn2); - if (diff != 0) - return diff; - } - }; - warningList.sort(ordering); - - // Now emit the warnings, but skipping over duplicates as we go through the list - DiagnosticPosition[] previous = null; - for (DiagnosticPosition[] warning : warningList) { - - // Skip duplicates - if (previous != null && extendsAsPrefix.test(previous, warning)) - continue; - previous = warning; - - // Emit warnings showing the entire stack trace - JCDiagnostic.Warning key = LintWarnings.PossibleThisEscape; - int remain = warning.length; - do { - DiagnosticPosition pos = warning[--remain]; - log.warning(pos, key); + // Limit output to one warning per constructor, field initializer, or initializer block + Set thingsWarnedAbout = new HashSet<>(); + filterWarnings(warning -> thingsWarnedAbout.add(warning.origin)); + + // Emit warnings + for (Warning warning : warningList) { + LintWarning key = LintWarnings.PossibleThisEscape; + for (StackFrame frame : warning.stack) { + log.warning(frame.site.pos(), key); key = LintWarnings.PossibleThisEscapeLocation; - } while (remain > 0); + } } + + // Done warningList.clear(); } - // Analyze statements, but stop at (and record) the first warning generated - private void analyzeStatements(List stats) { - for (JCStatement stat : stats) { - scan(stat); - if (copyPendingWarning()) - break; + // Warning list editor (this is slightly more efficient than removeIf()) + private void filterWarnings(Predicate filter) { + int numRetained = 0; + for (Warning warning : warningList) { + if (filter.test(warning)) + warningList.set(numRetained++, warning); } + warningList.subList(numRetained, warningList.size()).clear(); } @Override @@ -542,10 +483,6 @@ public void visitVarDef(JCVariableDecl tree) { private void visitVarDef(VarSymbol sym, JCExpression expr) { - // Skip if ignoring warnings for this field - if (suppressed.contains(sym)) - return; - // Scan initializer, if any scan(expr); if (isParamOrVar(sym)) @@ -579,19 +516,43 @@ public void visitApply(JCMethodInvocation invoke) { } else refs.discardExprs(depth); - // If "super()": ignore - we don't try to track into superclasses - if (TreeInfo.name(invoke.meth) == names._super) + // If "super()": we don't invoke it (we don't track into superclasses) but we do execute any + // non-static field initializers and initialization blocks because this is when they happen. + if (TreeInfo.name(invoke.meth) == names._super) { + currentMethod.declaringClass.defs.stream() + .filter(def -> (TreeInfo.flags(def) & Flags.STATIC) == 0) + .forEach(def -> { + switch (def) { + case JCBlock block -> analyzeInitializer(invoke, block, receiverRefs, () -> visitBlock(block)); + case JCVariableDecl varDecl -> analyzeInitializer(invoke, varDecl, receiverRefs, () -> scan(varDecl)); + default -> { } + } + }); return; + } // "Invoke" the method invoke(invoke, sym, invoke.args, receiverRefs); } - private void invoke(JCTree site, Symbol sym, List args, RefSet receiverRefs) { + // Analyze a field initializer or initialization block after encountering a super() invocation + private void analyzeInitializer(JCMethodInvocation site, JCTree initializer, RefSet receiverRefs, Runnable action) { + RefSet refsPrev = refs; + refs = RefSet.newEmpty(); + int depthPrev = depth; + depth = 0; + callStack.add(new StackFrame(currentMethod, initializer, site)); + try { + refs.addAll(receiverRefs); + action.run(); + } finally { + callStack.remove(callStack.size() - 1); + depth = depthPrev; + refs = refsPrev; + } + } - // Skip if ignoring warnings for a constructor invoked via 'this()' - if (suppressed.contains(sym)) - return; + private void invoke(JCTree site, Symbol sym, List args, RefSet receiverRefs) { // Ignore final methods in java.lang.Object (getClass(), notify(), etc.) if (sym != null && @@ -627,7 +588,7 @@ private void invoke(JCTree site, Symbol sym, List args, RefSet args, - RefSet receiverRefs, MethodInfo methodInfo) { - Assert.check(methodInfo.invokable()); + private void invokeInvokable(JCTree site, List args, RefSet receiverRefs, MethodInfo methodInfo) { + Assert.check(methodInfo.invokable); // Collect 'this' references found in method parameters - JCMethodDecl method = methodInfo.declaration(); + JCMethodDecl method = methodInfo.declaration; RefSet paramRefs = RefSet.newEmpty(); List params = method.params; while (args.nonEmpty() && params.nonEmpty()) { @@ -663,13 +623,13 @@ private void invokeInvokable(JCTree site, List args, } // "Invoke" the method - JCClassDecl methodClassPrev = methodClass; - methodClass = methodInfo.declaringClass(); + MethodInfo currentMethodPrev = currentMethod; + currentMethod = methodInfo; RefSet refsPrev = refs; refs = RefSet.newEmpty(); int depthPrev = depth; depth = 0; - callStack.push(site); + callStack.add(new StackFrame(currentMethodPrev, null, site)); try { // Add initial references from method receiver @@ -706,10 +666,10 @@ private void invokeInvokable(JCTree site, List args, .map(ref -> new ExprRef(depthPrev, ref)) .forEach(refsPrev::add); } finally { - callStack.pop(); + callStack.remove(callStack.size() - 1); depth = depthPrev; refs = refsPrev; - methodClass = methodClassPrev; + currentMethod = currentMethodPrev; } } @@ -755,7 +715,7 @@ public void visitNewClass(JCNewClass tree) { RefSet receiverRefs = receiverRefsForConstructor(tree.encl, tsym); // "Invoke" the constructor - if (methodInfo != null && methodInfo.invokable()) + if (methodInfo != null && methodInfo.invokable) invokeInvokable(tree, tree.args, receiverRefs, methodInfo); else invokeUnknown(tree, tree.args, receiverRefs); @@ -787,9 +747,10 @@ private RefSet receiverRefsForConstructor(JCExpression explicitOuterThi // Determine if an unqualified "new Foo()" constructor gets 'this' as an implicit outer instance private boolean hasImplicitOuterInstance(TypeSymbol tsym) { - return tsym != methodClass.sym + ClassSymbol currentClassSym = currentMethod.declaringClass.sym; + return tsym != currentClassSym && tsym.hasOuterInstance() - && tsym.isEnclosedBy(methodClass.sym); + && tsym.isEnclosedBy(currentClassSym); } // @@ -829,13 +790,13 @@ record ForeachMethods(MethodSymbol iterator, MethodSymbol hasNext, MethodSymbol MethodSymbol hasNext = null; MethodSymbol next = null; if (elemType == null) { - Symbol iteratorSym = rs.resolveQualifiedMethod(tree.expr.pos(), attrEnv, + Symbol iteratorSym = rs.resolveQualifiedMethod(tree.expr.pos(), topLevelEnv, tree.expr.type, names.iterator, List.nil(), List.nil()); if (iteratorSym instanceof MethodSymbol) { iterator = (MethodSymbol)iteratorSym; - Symbol hasNextSym = rs.resolveQualifiedMethod(tree.expr.pos(), attrEnv, + Symbol hasNextSym = rs.resolveQualifiedMethod(tree.expr.pos(), topLevelEnv, iterator.getReturnType(), names.hasNext, List.nil(), List.nil()); - Symbol nextSym = rs.resolveQualifiedMethod(tree.expr.pos(), attrEnv, + Symbol nextSym = rs.resolveQualifiedMethod(tree.expr.pos(), topLevelEnv, iterator.getReturnType(), names.next, List.nil(), List.nil()); if (hasNextSym instanceof MethodSymbol) hasNext = (MethodSymbol)hasNextSym; @@ -974,7 +935,7 @@ public void visitSelect(JCFieldAccess tree) { Stream methodRefs = refs.removeExprs(depth); // Explicit 'this' reference? The expression references whatever 'this' references - Type.ClassType currentClassType = (Type.ClassType)methodClass.sym.type; + Type.ClassType currentClassType = (Type.ClassType)currentMethod.declaringClass.sym.type; if (TreeInfo.isExplicitThisReference(types, currentClassType, tree)) { refs.find(ThisRef.class) .map(ref -> new ExprRef(depth, ref)) @@ -1059,7 +1020,7 @@ public void visitIdent(JCIdent tree) { MethodSymbol sym = (MethodSymbol)tree.sym; // Check for implicit 'this' reference - ClassSymbol methodClassSym = methodClass.sym; + ClassSymbol methodClassSym = currentMethod.declaringClass.sym; if (methodClassSym.isSubClass(sym.owner, types)) { refs.find(ThisRef.class) .map(ref -> new ExprRef(depth, ref)) @@ -1243,53 +1204,49 @@ public void visitBinary(JCBinary tree) { // Helper methods - private void visitTopLevel(Env env, JCClassDecl klass, Runnable action) { - Assert.check(attrEnv == null); + private void analyzeConstructor(MethodInfo constructor) { Assert.check(targetClass == null); - Assert.check(methodClass == null); + Assert.check(currentMethod == null); Assert.check(depth == -1); Assert.check(refs == null); - attrEnv = env; - targetClass = klass; - methodClass = klass; + targetClass = constructor.declaringClass; + currentMethod = constructor; try { // Add the initial 'this' reference refs = RefSet.newEmpty(); refs.add(new ThisRef(targetClass.sym, EnumSet.of(Indirection.DIRECT))); - // Perform action - this.visitScoped(false, action); + // Analyze constructor + visitScoped(false, () -> scan(constructor.declaration.body)); } finally { Assert.check(depth == -1); - attrEnv = null; - methodClass = null; + currentMethod = null; targetClass = null; refs = null; } } // Recurse through indirect code that might get executed later, e.g., a lambda. - // We stash any pending warning and the current RefSet, then recurse into the deferred - // code (still using the current RefSet) to see if it would leak. Then we restore the - // pending warning and the current RefSet. Finally, if the deferred code would have - // leaked, we create an indirect ExprRef because it must be holding a 'this' reference. - // If the deferred code would not leak, then obviously no leak is possible, period. + // We record the current number of (real) warnings, then recurse into the deferred + // code (still using the current RefSet) to see if that number increases, i.e., to + // see if it would leak. Then we discard any new warnings and the lambda's RefSet. + // Finally, if the deferred code would have leaked, we create an indirect ExprRef + // because the lambda must be holding a 'this' reference. If not, no leak is possible. private void visitDeferred(Runnable deferredCode) { - DiagnosticPosition[] pendingWarningPrev = pendingWarning; - pendingWarning = null; + int numWarningsPrev = warningList.size(); RefSet refsPrev = refs.clone(); boolean deferredCodeLeaks; try { deferredCode.run(); - deferredCodeLeaks = pendingWarning != null; + deferredCodeLeaks = warningList.size() > numWarningsPrev; // There can be ExprRef's if the deferred code returns something. // Don't let them escape unnoticed. deferredCodeLeaks |= refs.discardExprs(depth); } finally { refs = refsPrev; - pendingWarning = pendingWarningPrev; + warningList.subList(numWarningsPrev, warningList.size()).clear(); } if (deferredCodeLeaks) refs.add(new ExprRef(depth, syms.objectType.tsym, EnumSet.of(Indirection.INDIRECT))); @@ -1341,24 +1298,9 @@ private void popScope() { // Note a possible 'this' reference leak at the specified location private void leakAt(JCTree tree) { - - // Generate at most one warning per statement - if (pendingWarning != null) - return; - - // Snapshot the current stack trace - callStack.push(tree.pos()); - pendingWarning = callStack.toArray(new DiagnosticPosition[0]); - callStack.pop(); - } - - // Copy pending warning, if any, to the warning list and reset - private boolean copyPendingWarning() { - if (pendingWarning == null) - return false; - warningList.add(pendingWarning); - pendingWarning = null; - return true; + callStack.add(new StackFrame(currentMethod, null, tree)); // include the point of leakage in the stack + warningList.add(new Warning(targetClass, new ArrayList<>(callStack))); + callStack.remove(callStack.size() - 1); } // Does the symbol correspond to a parameter or local variable (not a field)? @@ -1398,7 +1340,7 @@ private boolean isAnalyzing() { private boolean checkInvariants(boolean analyzing, boolean allowExpr) { Assert.check(analyzing == isAnalyzing()); if (isAnalyzing()) { - Assert.check(methodClass != null); + Assert.check(currentMethod != null); Assert.check(targetClass != null); Assert.check(refs != null); Assert.check(depth >= 0); @@ -1409,7 +1351,6 @@ private boolean checkInvariants(boolean analyzing, boolean allowExpr) { Assert.check(refs == null); Assert.check(depth == -1); Assert.check(callStack.isEmpty()); - Assert.check(pendingWarning == null); Assert.check(invocations.isEmpty()); } return true; @@ -1788,12 +1729,130 @@ public RefSet clone() { } } +// StackFrame + + // Information about one frame on the call stack + private class StackFrame { + + final MethodInfo method; // the method containing the statement + final JCTree site; // the call site within the method + final JCTree initializer; // originating field or initialization block, else null + final boolean suppressible; // whether warning can be suppressed at this frame + + StackFrame(MethodInfo method, JCTree initializer, JCTree site) { + this.method = method; + this.initializer = initializer; + this.site = site; + this.suppressible = initializer != null || (method.constructor && method.declaringClass == targetClass); + } + + boolean isSuppressed() { + return suppressible && + suppressed.contains(initializer instanceof JCVariableDecl v ? v.sym : method.declaration.sym); + } + + int comparePos(StackFrame that) { + return Integer.compare(this.site.pos().getPreferredPosition(), that.site.pos().getPreferredPosition()); + } + + @Override + public String toString() { + return "StackFrame" + + "[" + method.declaration.sym + "@" + site.pos().getPreferredPosition() + + (initializer != null ? ",init@" + initializer.pos().getPreferredPosition() : "") + + "]"; + } + } + +// Warning + + // Information about one warning we have generated + private class Warning { + + final JCClassDecl declaringClass; // the class whose instance is leaked + final ArrayList stack; // the call stack where the leak happens + final JCTree origin; // the originating ctor, field, or init block + + Warning(JCClassDecl declaringClass, ArrayList stack) { + this.declaringClass = declaringClass; + this.stack = stack; + this.origin = stack.stream() + .map(frame -> frame.initializer) + .filter(Objects::nonNull) + .findFirst() + .orElseGet(() -> stack.get(0).method.declaration); // default to the initial constructor + } + + // Used to eliminate redundant warnings. Warning A is redundant with warning B if the call stack of A includes + // the call stack of B plus additional initial frame(s). For example, if constructor B = Foo(int x) generates a + // warning, then generating warning for some other constructor A when it invokes this(123) would be redundant. + boolean isRedundantWith(Warning that) { + int numExtra = this.stack.size() - that.stack.size(); + return numExtra >= 0 && + IntStream.range(0, that.stack.size()) + .allMatch(index -> this.stack.get(numExtra + index).comparePos(that.stack.get(index)) == 0); + }; + + // Order warnings by their stack frames, lexicographically in reverse calling order, which will cause + // all warnings that are isRedundantWith() some other warning to immediately follow that warning. + static int sortByStackFrames(Warning warning1, Warning warning2) { + int index1 = warning1.stack.size(); + int index2 = warning2.stack.size(); + while (true) { + boolean end1 = --index1 < 0; + boolean end2 = --index2 < 0; + if (end1 && end2) + return 0; + if (end1) + return -1; + if (end2) + return 1; + int diff = warning1.stack.get(index1).comparePos(warning2.stack.get(index2)); + if (diff != 0) + return diff; + } + }; + + // Determine whether this warning is suppressed. A single "this-escape" warning involves multiple source code + // positions, so we must determine suppression manually. We do this as follows: A warning is suppressed if + // "this-escape" is disabled at any position in the stack where that stack frame corresponds to a constructor + // or field initializer in the target class. That means, for example, @SuppressWarnings("this-escape") annotations + // on regular methods are ignored. Here we work our way back up the call stack from the point of the leak until + // we encounter a suppressible stack frame. + boolean isSuppressed() { + for (int index = stack.size() - 1; index >= 0; index--) { + if (stack.get(index).isSuppressed()) + return true; + } + return false; + } + + // If this is a field or initializer warning, trim the initial stack frame(s) up through the super() call + void trimInitializerFrames() { + for (int i = 0; i < stack.size(); i++) { + if (stack.get(i).initializer != null) { + stack.subList(0, i + 1).clear(); + break; + } + } + } + + @Override + public String toString() { + return "Warning" + + "[class=" + declaringClass.sym.flatname + + ",stack=[\n " + stack.stream().map(StackFrame::toString).collect(Collectors.joining("\n ")) + "]" + + "]"; + } + } + // MethodInfo // Information about a constructor or method in the compilation unit private record MethodInfo( JCClassDecl declaringClass, // the class declaring "declaration" JCMethodDecl declaration, // the method or constructor itself + boolean constructor, // the method is a constructor boolean analyzable, // it's a constructor that we should analyze boolean invokable) { // it may be safely "invoked" during analysis @@ -1801,6 +1860,7 @@ private record MethodInfo( public String toString() { return "MethodInfo" + "[method=" + declaringClass.sym.flatname + "." + declaration.sym + + ",constructor=" + constructor + ",analyzable=" + analyzable + ",invokable=" + invokable + "]"; diff --git a/test/langtools/tools/javac/warnings/ThisEscape.java b/test/langtools/tools/javac/warnings/ThisEscape.java index 93ccb6e0830f2..070f371d161a5 100644 --- a/test/langtools/tools/javac/warnings/ThisEscape.java +++ b/test/langtools/tools/javac/warnings/ThisEscape.java @@ -1,6 +1,6 @@ /* * @test /nodynamiccopyright/ - * @bug 8015831 + * @bug 8015831 8355753 * @compile/ref=ThisEscape.out -Xlint:this-escape -XDrawDiagnostics ThisEscape.java * @summary Verify 'this' escape detection */ @@ -765,4 +765,22 @@ private Object getObject() { return this.obj; } } + + // JDK-8355753 - @SuppressWarnings("this-escape") not respected for indirect leak via field + public static class SuppressedIndirectLeakViaField { + + private final int x = this.mightLeak(); // this leak should be suppressed + + public SuppressedIndirectLeakViaField() { + this(""); + } + + @SuppressWarnings("this-escape") + private SuppressedIndirectLeakViaField(String s) { + } + + public int mightLeak() { + return 0; + } + } } From ddb0d4ec04cffdfc87624659a1ecb42a7bceac77 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Mon, 28 Apr 2025 13:58:07 -0500 Subject: [PATCH 32/44] Update copyrights. --- test/langtools/tools/javac/ImplicitClass/ErrorRecovery.java | 2 +- .../tools/javac/diags/examples/ImplicitClassBad-Filename.java | 2 +- .../tools/javac/diags/examples/ImplicitClassHasPackage.java | 2 +- test/langtools/tools/javac/modules/AnnotationsOnModules.java | 2 +- .../tools/javac/warnings/UnneededStrictfpWarningToolBox.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/langtools/tools/javac/ImplicitClass/ErrorRecovery.java b/test/langtools/tools/javac/ImplicitClass/ErrorRecovery.java index cb562c6d0d521..17fc38ea316ab 100644 --- a/test/langtools/tools/javac/ImplicitClass/ErrorRecovery.java +++ b/test/langtools/tools/javac/ImplicitClass/ErrorRecovery.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 diff --git a/test/langtools/tools/javac/diags/examples/ImplicitClassBad-Filename.java b/test/langtools/tools/javac/diags/examples/ImplicitClassBad-Filename.java index 90b4d8b41f4b8..fbcf8b264924e 100644 --- a/test/langtools/tools/javac/diags/examples/ImplicitClassBad-Filename.java +++ b/test/langtools/tools/javac/diags/examples/ImplicitClassBad-Filename.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 diff --git a/test/langtools/tools/javac/diags/examples/ImplicitClassHasPackage.java b/test/langtools/tools/javac/diags/examples/ImplicitClassHasPackage.java index 8a7ed564a75f1..229d9adcff99c 100644 --- a/test/langtools/tools/javac/diags/examples/ImplicitClassHasPackage.java +++ b/test/langtools/tools/javac/diags/examples/ImplicitClassHasPackage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 diff --git a/test/langtools/tools/javac/modules/AnnotationsOnModules.java b/test/langtools/tools/javac/modules/AnnotationsOnModules.java index 3f13bc5e913dc..df1ce6135833f 100644 --- a/test/langtools/tools/javac/modules/AnnotationsOnModules.java +++ b/test/langtools/tools/javac/modules/AnnotationsOnModules.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 diff --git a/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java b/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java index cdf3ca07a9798..a9101b47e42b3 100644 --- a/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java +++ b/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 From 65e2679e0ed26bfe58d9c45c40d68556dbe352c1 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Thu, 29 May 2025 17:16:40 -0400 Subject: [PATCH 33/44] Add additional tests for warnings generated from fields. --- .../tools/javac/warnings/ThisEscape.java | 31 +++++++++++++++++++ .../tools/javac/warnings/ThisEscape.out | 3 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/test/langtools/tools/javac/warnings/ThisEscape.java b/test/langtools/tools/javac/warnings/ThisEscape.java index 070f371d161a5..f9280b5b1aecb 100644 --- a/test/langtools/tools/javac/warnings/ThisEscape.java +++ b/test/langtools/tools/javac/warnings/ThisEscape.java @@ -770,6 +770,11 @@ private Object getObject() { public static class SuppressedIndirectLeakViaField { private final int x = this.mightLeak(); // this leak should be suppressed + private int y; + + { + y = this.mightLeak(); // this leak should be suppressed + } public SuppressedIndirectLeakViaField() { this(""); @@ -783,4 +788,30 @@ public int mightLeak() { return 0; } } + + public static class UnsuppressedIndirectLeakViaField { + + private final int x = this.mightLeak(); // this leak should not be suppressed + + public UnsuppressedIndirectLeakViaField() { + this(""); // this constructor does not trigger the warning + } + + @SuppressWarnings("this-escape") + private UnsuppressedIndirectLeakViaField(String s) { + // this constructor does not trigger the warning (obviously; it's directly suppressed) + } + + public UnsuppressedIndirectLeakViaField(int z) { + // this constructor triggers the warning + } + + public UnsuppressedIndirectLeakViaField(float z) { + // this constructor also triggers the warning, but should not create a duplicate + } + + public int mightLeak() { + return 0; + } + } } diff --git a/test/langtools/tools/javac/warnings/ThisEscape.out b/test/langtools/tools/javac/warnings/ThisEscape.out index a444c2828b7c8..b866c4a7731a1 100644 --- a/test/langtools/tools/javac/warnings/ThisEscape.out +++ b/test/langtools/tools/javac/warnings/ThisEscape.out @@ -35,4 +35,5 @@ ThisEscape.java:652:55: compiler.warn.possible.this.escape ThisEscape.java:672:18: compiler.warn.possible.this.escape ThisEscape.java:669:48: compiler.warn.possible.this.escape.location ThisEscape.java:726:32: compiler.warn.possible.this.escape -37 warnings +ThisEscape.java:794:45: compiler.warn.possible.this.escape +38 warnings From 0898c557f780ab4987b27d29eae076b96bd48187 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Thu, 29 May 2025 21:47:30 -0400 Subject: [PATCH 34/44] Revert TreeInfo.endPos() refactoring except for "bracePos" renaming. --- .../com/sun/tools/javac/comp/Flow.java | 28 +++------ .../com/sun/tools/javac/comp/Lower.java | 10 ++- .../sun/tools/javac/comp/TransPatterns.java | 6 +- .../classes/com/sun/tools/javac/jvm/Gen.java | 24 +++---- .../sun/tools/javac/parser/JavacParser.java | 4 +- .../com/sun/tools/javac/tree/EndPosTable.java | 3 - .../com/sun/tools/javac/tree/JCTree.java | 2 + .../com/sun/tools/javac/tree/TreeInfo.java | 63 +++++++++---------- .../javac/parser/DeclarationEndPositions.java | 2 +- 9 files changed, 60 insertions(+), 82 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 1b31ea8a78cef..e685f139b680c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -379,10 +379,6 @@ private JumpKind(Tag treeTag) { abstract JCTree getTarget(JCTree tree); } - /** Table of ending positions. - */ - EndPosTable endPositions; - /** The currently pending exits that go from current inner blocks * to an enclosing block, in source order. */ @@ -618,7 +614,7 @@ public void visitMethodDef(JCMethodDecl tree) { tree.completesNormally = alive != Liveness.DEAD; if (alive == Liveness.ALIVE && !tree.sym.type.getReturnType().hasTag(VOID)) - log.error(TreeInfo.diagEndPos(endPositions, tree.body), Errors.MissingRetStmt); + log.error(TreeInfo.diagEndPos(tree.body), Errors.MissingRetStmt); clearPendingExits(true); } finally { @@ -760,10 +756,10 @@ public void visitSwitchExpression(JCSwitchExpression tree) { scanStats(c.stats); if (alive == Liveness.ALIVE) { if (c.caseKind == JCCase.RULE) { - log.error(TreeInfo.diagEndPos(endPositions, c.body), + log.error(TreeInfo.diagEndPos(c.body), Errors.RuleCompletesNormally); } else if (l.tail.isEmpty()) { - log.error(TreeInfo.diagEndPos(endPositions, tree), + log.error(TreeInfo.diagEndPos(tree), Errors.SwitchExpressionCompletesNormally); } } @@ -1236,7 +1232,7 @@ public void visitTry(JCTry tree) { scanStat(tree.finalizer); tree.finallyCanCompleteNormally = alive != Liveness.DEAD; if (alive == Liveness.DEAD) { - lint.logIfEnabled(TreeInfo.diagEndPos(endPositions, tree.finalizer), + lint.logIfEnabled(TreeInfo.diagEndPos(tree.finalizer), LintWarnings.FinallyCannotComplete); } else { while (exits.nonEmpty()) { @@ -1342,11 +1338,9 @@ public void analyzeTree(Env env, JCTree tree, TreeMaker make) { attrEnv = env; Flow.this.make = make; pendingExits = new ListBuffer<>(); - endPositions = env.toplevel.endPositions; alive = Liveness.ALIVE; scan(tree); } finally { - endPositions = null; pendingExits = null; Flow.this.make = null; } @@ -1928,13 +1922,11 @@ public void analyzeTree(Env env, JCTree tree, TreeMaker make) { attrEnv = env; Flow.this.make = make; pendingExits = new ListBuffer<>(); - endPositions = env.toplevel.endPositions; preciseRethrowTypes = new HashMap<>(); this.thrown = this.caught = null; this.classDef = null; scan(tree); } finally { - endPositions = null; pendingExits = null; Flow.this.make = null; this.thrown = this.caught = null; @@ -2544,13 +2536,13 @@ public void visitMethodDef(JCMethodDecl tree) { */ var.flags_field |= UNINITIALIZED_FIELD; } else { - checkInit(TreeInfo.diagEndPos(endPositions, tree.body), var); + checkInit(TreeInfo.diagEndPos(tree.body), var); } } else { checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), var); } } else { - checkInit(TreeInfo.diagEndPos(endPositions, tree.body), var); + checkInit(TreeInfo.diagEndPos(tree.body), var); } } } @@ -3231,11 +3223,11 @@ public void visitModuleDef(JCModuleDecl tree) { /** Perform definite assignment/unassignment analysis on a tree. */ - public void analyzeTree(Env env, TreeMaker make) { + public void analyzeTree(Env env, TreeMaker make) { analyzeTree(env, env.tree, make); } - public void analyzeTree(Env env, JCTree tree, TreeMaker make) { + public void analyzeTree(Env env, JCTree tree, TreeMaker make) { try { startPos = tree.pos().getStartPosition(); @@ -3248,7 +3240,6 @@ public void analyzeTree(Env env, JCTree tree, TreeMaker make) { nextadr = 0; Flow.this.make = make; pendingExits = new ListBuffer<>(); - endPositions = env.toplevel.endPositions; this.classDef = null; unrefdResources = WriteableScope.create(env.enclClass.sym); scan(tree); @@ -3264,7 +3255,6 @@ public void analyzeTree(Env env, JCTree tree, TreeMaker make) { firstadr = 0; nextadr = 0; Flow.this.make = null; - endPositions = null; pendingExits = null; this.classDef = null; unrefdResources = null; @@ -3477,10 +3467,8 @@ public void analyzeTree(Env env, JCTree tree, TreeMaker make) { attrEnv = env; Flow.this.make = make; pendingExits = new ListBuffer<>(); - endPositions = env.toplevel.endPositions; scan(tree); } finally { - endPositions = null; pendingExits = null; Flow.this.make = null; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index a8823324708fd..68589e52a6649 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -1608,7 +1608,7 @@ private JCBlock makeTwrBlock(List resources, JCBlock block, int depth) { //create (semi-) finally block that will be copied into the main try body: int oldPos = make.pos; - make.at(TreeInfo.endPos(endPosTable, block)); + make.at(TreeInfo.endPos(block)); // if (#resource != null) { #resource.close(); } JCStatement bodyCloseStatement = makeResourceCloseInvocation(resourceUse); @@ -3697,7 +3697,7 @@ private void visitIterableForeachLoop(JCEnhancedForLoop tree) { vardefinit).setType(tree.var.type); indexDef.sym = tree.var.sym; JCBlock body = make.Block(0, List.of(indexDef, tree.body)); - body.bracePos = TreeInfo.endPos(endPosTable, tree.body); + body.bracePos = TreeInfo.endPos(tree.body); result = translate(make. ForLoop(List.of(init), cond, @@ -3855,7 +3855,7 @@ private void handleSwitch(JCTree tree, JCExpression selector, List cases for (JCCase c : convertedCases) { if (c.caseKind == JCCase.RULE && c.completesNormally) { - JCBreak b = make.at(TreeInfo.endPos(endPosTable, c.stats.last())).Break(null); + JCBreak b = make.at(TreeInfo.endPos(c.stats.last())).Break(null); b.target = tree; c.stats = c.stats.append(b); } @@ -4158,7 +4158,7 @@ else if (oneCase == nullCase) { stmtList.append(switch2); JCBlock res = make.Block(0L, stmtList.toList()); - res.bracePos = TreeInfo.endPos(endPosTable, tree); + res.bracePos = TreeInfo.endPos(tree); return res; } else { JCSwitchExpression switch2 = make.SwitchExpression(make.Ident(dollar_tmp), lb.toList()); @@ -4402,14 +4402,12 @@ public List translateTopLevelClass(Env env, JCTree cdef, Tr public JCMethodDecl translateMethod(Env env, JCMethodDecl methodDecl, TreeMaker make) { try { this.attrEnv = env; - this.endPosTable = env.toplevel.endPositions; this.make = make; this.currentClass = methodDecl.sym.enclClass(); proxies = new HashMap<>(); return translate(methodDecl); } finally { this.attrEnv = null; - this.endPosTable = null; this.make = null; this.currentClass = null; // the two fields below are set when visiting the method diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java index 55fd1815b655d..58c36a5cf7c27 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java @@ -82,7 +82,6 @@ import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant; import com.sun.tools.javac.jvm.Target; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCBlock.PatternMatchingCatch; @@ -136,7 +135,6 @@ public static TransPatterns instance(Context context) { private final Preview preview; private TreeMaker make; private Env env; - private EndPosTable endPositions; BindingContext bindingContext = new BindingContext() { @Override @@ -753,7 +751,7 @@ void appendBreakIfNeeded(JCTree switchTree, List cases, JCCase c) { if (c.caseKind == CaseTree.CaseKind.RULE || (cases.last() == c && c.completesNormally)) { JCTree pos = c.stats.nonEmpty() ? c.stats.last() : c; - JCBreak brk = make.at(TreeInfo.endPos(endPositions, pos)).Break(null); + JCBreak brk = make.at(TreeInfo.endPos(pos)).Break(null); brk.target = switchTree; c.stats = c.stats.append(brk); } @@ -1398,13 +1396,11 @@ public JCTree translateTopLevelClass(Env env, JCTree cdef, TreeMake try { this.make = make; this.env = env; - this.endPositions = env.toplevel.endPositions; translate(cdef); } finally { // note that recursive invocations of this method fail hard this.make = null; this.env = null; - this.endPositions = null; } return cdef; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index b527c3c315743..14c5420c7c0fd 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -500,7 +500,7 @@ else if ((block.flags & SYNTHETIC) == 0) c.members().enter(clinit); List clinitStats = clinitCode.toList(); JCBlock block = make.at(clinitStats.head.pos()).Block(0, clinitStats); - block.bracePos = TreeInfo.endPos(endPosTable, clinitStats.last()); + block.bracePos = TreeInfo.endPos(clinitStats.last()); methodDefs.append(make.MethodDef(clinit, block)); if (!clinitTAs.isEmpty()) @@ -554,7 +554,7 @@ void normalizeMethod(JCMethodDecl md, List initCode, List make.Block(0, initCode.prepend(supercall))); if (md.body.bracePos == Position.NOPOS) - md.body.bracePos = TreeInfo.endPos(endPosTable, md.body.stats.last()); + md.body.bracePos = TreeInfo.endPos(md.body.stats.last()); md.sym.appendUniqueTypeAttributes(initTAs); } @@ -961,7 +961,7 @@ else if (tree.body != null) { // If last statement could complete normally, insert a // return at the end. if (code.isAlive()) { - code.statBegin(TreeInfo.endPos(endPosTable, tree.body)); + code.statBegin(TreeInfo.endPos(tree.body)); if (env.enclMethod == null || env.enclMethod.sym.type.getReturnType().hasTag(VOID)) { code.emitop0(return_); @@ -1015,7 +1015,7 @@ private int initCode(JCMethodDecl tree, Env env, boolean fatcode) { varDebugInfo, stackMap, debugCode, - genCrt ? new CRTable(tree, endPosTable) + genCrt ? new CRTable(tree, env.toplevel.endPositions) : null, syms, types, @@ -1438,7 +1438,7 @@ private void handleSwitch(JCTree swtch, JCExpression selector, List case if (swtch instanceof JCSwitchExpression) { // Emit line position for the end of a switch expression - code.statBegin(TreeInfo.endPos(endPosTable, swtch)); + code.statBegin(TreeInfo.endPos(swtch)); } } code.endScopes(limit); @@ -1547,9 +1547,9 @@ void genTry(JCTree body, List catchers, Env env) { genStat(body, env, CRT_BLOCK); int endpc = code.curCP(); List gaps = env.info.gaps.toList(); - code.statBegin(TreeInfo.endPos(endPosTable, body)); + code.statBegin(TreeInfo.endPos(body)); genFinalizer(env); - code.statBegin(TreeInfo.endPos(endPosTable, env.tree)); + code.statBegin(TreeInfo.endPos(env.tree)); Chain exitChain; boolean actualTry = env.tree.hasTag(TRY); if (startpc == endpc && actualTry) { @@ -1568,7 +1568,7 @@ void genTry(JCTree body, List catchers, Env env) { genCatch(l.head, env, startpc, endpc, gaps); genFinalizer(env); if (hasFinalizer || l.tail.nonEmpty()) { - code.statBegin(TreeInfo.endPos(endPosTable, env.tree)); + code.statBegin(TreeInfo.endPos(env.tree)); exitChain = Code.mergeChains(exitChain, code.branch(goto_)); } @@ -1596,14 +1596,14 @@ void genTry(JCTree body, List catchers, Env env) { catchallpc, 0); startseg = env.info.gaps.next().intValue(); } - code.statBegin(TreeInfo.finalizerPos(endPosTable, env.tree, PosKind.FIRST_STAT_POS)); + code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS)); code.markStatBegin(); Item excVar = makeTemp(syms.throwableType); excVar.store(); genFinalizer(env); code.resolvePending(); - code.statBegin(TreeInfo.finalizerPos(endPosTable, env.tree, PosKind.END_POS)); + code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.END_POS)); code.markStatBegin(); excVar.load(); @@ -1619,7 +1619,7 @@ void genTry(JCTree body, List catchers, Env env) { code.resolve(env.info.cont); // Mark statement line number - code.statBegin(TreeInfo.finalizerPos(endPosTable, env.tree, PosKind.FIRST_STAT_POS)); + code.statBegin(TreeInfo.finalizerPos(env.tree, PosKind.FIRST_STAT_POS)); code.markStatBegin(); // Save return address. @@ -1706,7 +1706,7 @@ void genCatchBlock(JCCatch tree, Env env) { code.statBegin(TreeInfo.firstStatPos(tree.body)); genStat(tree.body, env, CRT_BLOCK); code.endScopes(limit); - code.statBegin(TreeInfo.endPos(endPosTable, tree.body)); + code.statBegin(TreeInfo.endPos(tree.body)); } // where List, JCExpression>> catchTypesWithAnnotations(JCCatch tree) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index 3a36d09de1fd1..35f7912040d4d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -3163,6 +3163,7 @@ public JCStatement parseSimpleStatement() { accept(LBRACE); List cases = switchBlockStatementGroups(); JCSwitch t = to(F.at(pos).Switch(selector, cases)); + t.bracePos = token.endPos; accept(RBRACE); return t; } @@ -5677,9 +5678,6 @@ public T storeEnd(T tree, int endpos) { case CLASSDEF: case METHODDEF: case VARDEF: - case BLOCK: - case SWITCH: - case SWITCH_EXPRESSION: break; default: return tree; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java index 650d99b222dab..7cbd7a52d95d1 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java @@ -38,9 +38,6 @@ *

  • {@link JCTree.JCClassDecl} *
  • {@link JCTree.JCMethodDecl} *
  • {@link JCTree.JCVariableDecl} - *
  • {@link JCTree.JCBlock} - *
  • {@link JCTree.JCSwitch} - *
  • {@link JCTree.JCSwitchExpression} * * *

    This is NOT part of any supported API. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index b4d357e7b0823..debc10c9ff87e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -1329,6 +1329,8 @@ public Tag getTag() { public static class JCSwitch extends JCStatement implements SwitchTree { public JCExpression selector; public List cases; + /** Position of closing brace, optional. */ + public int bracePos = Position.NOPOS; public boolean hasUnconditionalPattern; public boolean isExhaustive; public boolean patternSwitch; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index d42c9b0031218..ae946c5b6d64d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -52,7 +52,7 @@ import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.ToIntBiFunction; +import java.util.function.ToIntFunction; import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT; import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.RIGHT; @@ -500,27 +500,26 @@ public static int firstStatPos(JCTree tree) { return tree.pos; } - /** The end position of the given tree, if defined. + /** The closing brace position of given tree, if it is a block with + * defined bracePos. */ - public static int endPos(EndPosTable endPosTable, JCTree tree) { - switch (tree.getTag()) { - case BLOCK: - return ((JCBlock)tree).bracePos; - case SWITCH_EXPRESSION: - return ((JCSwitchExpression)tree).bracePos; - case SYNCHRONIZED: - return endPos(endPosTable, ((JCSynchronized)tree).body); - case TRY: + public static int endPos(JCTree tree) { + if (tree.hasTag(BLOCK) && ((JCBlock) tree).bracePos != Position.NOPOS) + return ((JCBlock) tree).bracePos; + else if (tree.hasTag(SYNCHRONIZED)) + return endPos(((JCSynchronized) tree).body); + else if (tree.hasTag(TRY)) { JCTry t = (JCTry) tree; - return endPos(endPosTable, (t.finalizer != null) ? t.finalizer + return endPos((t.finalizer != null) ? t.finalizer : (t.catchers.nonEmpty() ? t.catchers.last().body : t.body)); - default: - break; - } - int endPos = endPosTable.getEndPos(tree); - if (endPos != Position.NOPOS) - return endPos; - return tree.pos; + } else if (tree.hasTag(SWITCH) && + ((JCSwitch) tree).bracePos != Position.NOPOS) { + return ((JCSwitch) tree).bracePos; + } else if (tree.hasTag(SWITCH_EXPRESSION) && + ((JCSwitchExpression) tree).bracePos != Position.NOPOS) { + return ((JCSwitchExpression) tree).bracePos; + } else + return tree.pos; } @@ -727,11 +726,11 @@ public static int getEndPos(JCTree tree, EndPosTable endPosTable) { /** A DiagnosticPosition with the preferred position set to the - * end position of given tree, if it is a block with - * defined endpos. + * closing brace position of given tree, if it is a block with + * defined closing brace position. */ - public static DiagnosticPosition diagEndPos(EndPosTable endPosTable, final JCTree tree) { - final int endPos = TreeInfo.endPos(endPosTable, tree); + public static DiagnosticPosition diagEndPos(final JCTree tree) { + final int endPos = TreeInfo.endPos(tree); return new DiagnosticPosition() { public JCTree getTree() { return tree; } public int getStartPosition() { return TreeInfo.getStartPos(tree); } @@ -743,30 +742,30 @@ public int getEndPosition(EndPosTable endPosTable) { } public enum PosKind { - START_POS((table, tree) -> TreeInfo.getStartPos(tree)), - FIRST_STAT_POS((table, tree) -> TreeInfo.firstStatPos(tree)), + START_POS(TreeInfo::getStartPos), + FIRST_STAT_POS(TreeInfo::firstStatPos), END_POS(TreeInfo::endPos); - final ToIntBiFunction posFunc; + final ToIntFunction posFunc; - PosKind(ToIntBiFunction posFunc) { + PosKind(ToIntFunction posFunc) { this.posFunc = posFunc; } - int toPos(EndPosTable endPosTable, JCTree tree) { - return posFunc.applyAsInt(endPosTable, tree); + int toPos(JCTree tree) { + return posFunc.applyAsInt(tree); } } /** The position of the finalizer of given try/synchronized statement. */ - public static int finalizerPos(EndPosTable endPosTable, JCTree tree, PosKind posKind) { + public static int finalizerPos(JCTree tree, PosKind posKind) { if (tree.hasTag(TRY)) { JCTry t = (JCTry) tree; Assert.checkNonNull(t.finalizer); - return posKind.toPos(endPosTable, t.finalizer); + return posKind.toPos(t.finalizer); } else if (tree.hasTag(SYNCHRONIZED)) { - return endPos(endPosTable, ((JCSynchronized)tree).body); + return endPos(((JCSynchronized) tree).body); } else { throw new AssertionError(); } diff --git a/test/langtools/tools/javac/parser/DeclarationEndPositions.java b/test/langtools/tools/javac/parser/DeclarationEndPositions.java index 6ab9d6f6f4cd4..473d6bc2712f1 100644 --- a/test/langtools/tools/javac/parser/DeclarationEndPositions.java +++ b/test/langtools/tools/javac/parser/DeclarationEndPositions.java @@ -71,7 +71,7 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOExcept public Void scan(Tree node, Void aVoid) { if (nodeType.isInstance(node)) { JCTree tree = (JCTree)node; - int actual = TreeInfo.endPos(unit.endPositions, tree); + int actual = TreeInfo.getEndPos(tree, unit.endPositions); int expected = marker.indexOf('^') + 1; if (actual != expected) { throw new AssertionError(String.format( From f79e35f8ee6fc3f394ec023f66dba78407b3389f Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 30 May 2025 09:23:14 -0400 Subject: [PATCH 35/44] Remove extraneous semicolons. --- .../com/sun/tools/javac/comp/ThisEscapeAnalyzer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index 8710e22ab5a57..9119278660f32 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -785,7 +785,7 @@ public void visitForeachLoop(JCEnhancedForLoop tree) { Type elemType = types.elemtype(tree.expr.type); // If not array, resolve the Iterable and Iterator methods - record ForeachMethods(MethodSymbol iterator, MethodSymbol hasNext, MethodSymbol next) { }; + record ForeachMethods(MethodSymbol iterator, MethodSymbol hasNext, MethodSymbol next) { } MethodSymbol iterator = null; MethodSymbol hasNext = null; MethodSymbol next = null; @@ -1791,7 +1791,7 @@ boolean isRedundantWith(Warning that) { return numExtra >= 0 && IntStream.range(0, that.stack.size()) .allMatch(index -> this.stack.get(numExtra + index).comparePos(that.stack.get(index)) == 0); - }; + } // Order warnings by their stack frames, lexicographically in reverse calling order, which will cause // all warnings that are isRedundantWith() some other warning to immediately follow that warning. @@ -1811,7 +1811,7 @@ static int sortByStackFrames(Warning warning1, Warning warning2) { if (diff != 0) return diff; } - }; + } // Determine whether this warning is suppressed. A single "this-escape" warning involves multiple source code // positions, so we must determine suppression manually. We do this as follows: A warning is suppressed if From 205f77e9d532e2a03bb6a862093dab8e671e58f8 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 30 May 2025 15:55:18 -0400 Subject: [PATCH 36/44] Update EndPosTable.replaceTree() to handle a new node equal to null. --- .../share/classes/com/sun/tools/javac/comp/Lower.java | 2 +- .../classes/com/sun/tools/javac/parser/JavacParser.java | 5 ++--- .../share/classes/com/sun/tools/javac/tree/EndPosTable.java | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index 68589e52a6649..54226155cf816 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -2060,7 +2060,7 @@ public T translate(T tree) { } else { make_at(tree.pos()); T result = super.translate(tree); - if (endPosTable != null && result != null && result != tree) { + if (endPosTable != null && result != tree) { endPosTable.replaceTree(tree, result); } return result; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index 35f7912040d4d..7ebcd0c844b2a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -5657,11 +5657,10 @@ public int getEndPos(JCTree tree) { @Override public int replaceTree(JCTree oldTree, JCTree newTree) { int pos = endPosMap.remove(oldTree); - if (pos != -1) { + if (pos != -1 && newTree != null) { storeEnd(newTree, pos); - return pos; } - return Position.NOPOS; + return pos; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java index 7cbd7a52d95d1..83fe402c0a78d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java @@ -76,7 +76,7 @@ public interface EndPosTable { * the new tree, the position of the new tree will be that of the old * tree. * @param oldtree a JCTree to be replaced - * @param newtree a JCTree to be replaced with + * @param newtree a JCTree to be replaced with, or null to just remove {@code oldtree} * @return position of the old tree or Positions.NOPOS for non-existent mapping */ int replaceTree(JCTree oldtree, JCTree newtree); From 16debaa4c3609cfe9830ee6a441a361703b45c99 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Tue, 10 Jun 2025 14:32:42 -0500 Subject: [PATCH 37/44] Small refactoring to utilize new method Options.isExplicitlyDisabled(). --- .../share/classes/com/sun/tools/javac/util/Log.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index c7627d6e45bb4..6b0bea7feab37 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -171,7 +171,7 @@ public final void reportWithLint(JCDiagnostic diag, Lint lint) { lint.isEnabled(category) : // then emit if the category is enabled category.annotationSuppression ? // else emit if the category is not suppressed, where !lint.isSuppressed(category) : // ...suppression happens via @SuppressWarnings - !options.isSet(XLINT_CUSTOM, "-" + category.option); // ...suppression happens via -Xlint:-category + !options.isExplicitlyDisabled(Option.XLINT, category); // ...suppression happens via -Xlint:-category if (!emit) return; } From 75d9edf5160486744d39fe85226bf571987fea13 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 13 Jun 2025 14:25:48 -0500 Subject: [PATCH 38/44] Refactor MandatoryWarningHandler to just be an aggregator. --- .../sun/tools/javac/api/JavacTaskPool.java | 2 - .../com/sun/tools/javac/code/Preview.java | 32 ++---- .../com/sun/tools/javac/comp/Check.java | 57 ++------- .../tools/javac/launcher/MemoryContext.java | 23 +--- .../sun/tools/javac/main/JavaCompiler.java | 14 +-- .../com/sun/tools/javac/util/AbstractLog.java | 31 +++-- .../sun/tools/javac/util/JCDiagnostic.java | 30 +---- .../classes/com/sun/tools/javac/util/Log.java | 85 ++++++++++++-- ...r.java => MandatoryWarningAggregator.java} | 108 +++++++----------- 9 files changed, 166 insertions(+), 216 deletions(-) rename src/jdk.compiler/share/classes/com/sun/tools/javac/util/{MandatoryWarningHandler.java => MandatoryWarningAggregator.java} (72%) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java index 87bc64c4e1976..6214c3775d382 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskPool.java @@ -272,8 +272,6 @@ void clear() { ((ReusableJavaCompiler)ReusableJavaCompiler.instance(this)).clear(); Types.instance(this).newRound(); Check.instance(this).newRound(); - Check.instance(this).clear(); //clear mandatory warning handlers - Preview.instance(this).clear(); //clear mandatory warning handlers Modules.instance(this).newRound(); Annotate.instance(this).newRound(); CompileStates.instance(this).clear(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java index d1e64e90a07cc..0b06863cd962f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java @@ -34,13 +34,13 @@ import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Error; import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.Warning; import com.sun.tools.javac.util.Log; -import com.sun.tools.javac.util.MandatoryWarningHandler; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Options; @@ -71,9 +71,6 @@ public class Preview { /** flag: is the "preview" lint category enabled? */ private final boolean verbose; - /** the diag handler to manage preview feature usage diagnostics */ - private final MandatoryWarningHandler previewHandler; - /** test flag: should all features be considered as preview features? */ private final boolean forcePreview; @@ -105,7 +102,6 @@ protected Preview(Context context) { log = Log.instance(context); source = Source.instance(context); verbose = Lint.instance(context).isEnabled(LintCategory.PREVIEW); - previewHandler = new MandatoryWarningHandler(log, source, verbose, true, LintCategory.PREVIEW); forcePreview = options.isSet("forcePreview"); majorVersionToSource = initMajorVersionToSourceMap(); } @@ -176,9 +172,11 @@ public void warnPreview(DiagnosticPosition pos, Feature feature) { Assert.check(isEnabled()); Assert.check(isPreview(feature)); markUsesPreview(pos); - previewHandler.report(pos, feature.isPlural() ? + log.mandatoryWarning(pos, + feature.isPlural() ? LintWarnings.PreviewFeatureUsePlural(feature.nameFragment()) : - LintWarnings.PreviewFeatureUse(feature.nameFragment())); + LintWarnings.PreviewFeatureUse(feature.nameFragment()), + DiagnosticFlag.AGGREGATE); } /** @@ -203,10 +201,6 @@ public void markUsesPreview(DiagnosticPosition pos) { sourcesWithPreviewFeatures.add(log.currentSourceFile()); } - public void reportPreviewWarning(DiagnosticPosition pos, LintWarning warnKey) { - previewHandler.report(pos, warnKey); - } - public boolean usesPreview(JavaFileObject file) { return sourcesWithPreviewFeatures.contains(file); } @@ -269,25 +263,13 @@ public boolean declaredUsingPreviewFeature(Symbol sym) { return false; } - /** - * Report any deferred diagnostics. - */ - public void reportDeferredDiagnostics() { - previewHandler.reportDeferredDiagnostic(); - } - - public void clear() { - previewHandler.clear(); - } - public void checkSourceLevel(DiagnosticPosition pos, Feature feature) { if (isPreview(feature) && !isEnabled()) { //preview feature without --preview flag, error - log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, disabledError(feature)); + log.error(DiagnosticFlag.SOURCE_LEVEL, pos, disabledError(feature)); } else { if (!feature.allowedInSource(source)) { - log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, - feature.error(source.name)); + log.error(DiagnosticFlag.SOURCE_LEVEL, pos, feature.error(source.name)); } if (isEnabled() && isPreview(feature)) { warnPreview(pos, feature); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 89ab5b13dd145..68d1a1125ded0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -164,18 +164,6 @@ protected Check(Context context) { profile = Profile.instance(context); preview = Preview.instance(context); - boolean verboseDeprecated = lint.isEnabled(LintCategory.DEPRECATION); - boolean verboseRemoval = lint.isEnabled(LintCategory.REMOVAL); - boolean verboseUnchecked = lint.isEnabled(LintCategory.UNCHECKED); - boolean enforceMandatoryWarnings = true; - - deprecationHandler = new MandatoryWarningHandler(log, null, verboseDeprecated, - enforceMandatoryWarnings, LintCategory.DEPRECATION, "deprecated"); - removalHandler = new MandatoryWarningHandler(log, null, verboseRemoval, - enforceMandatoryWarnings, LintCategory.REMOVAL); - uncheckedHandler = new MandatoryWarningHandler(log, null, verboseUnchecked, - enforceMandatoryWarnings, LintCategory.UNCHECKED); - deferredLintHandler = DeferredLintHandler.instance(context); allowModules = Feature.MODULES.allowedInSource(source); @@ -192,18 +180,6 @@ protected Check(Context context) { */ private Map,ClassSymbol> compiled = new HashMap<>(); - /** A handler for messages about deprecated usage. - */ - private MandatoryWarningHandler deprecationHandler; - - /** A handler for messages about deprecated-for-removal usage. - */ - private MandatoryWarningHandler removalHandler; - - /** A handler for messages about unchecked or unsafe usage. - */ - private MandatoryWarningHandler uncheckedHandler; - /** A handler for deferred lint warnings. */ private DeferredLintHandler deferredLintHandler; @@ -253,21 +229,24 @@ MethodSymbol setMethod(MethodSymbol newMethod) { * @param sym The deprecated symbol. */ void warnDeprecated(DiagnosticPosition pos, Symbol sym) { + LintWarning warningKey = null; if (sym.isDeprecatedForRemoval()) { if (!lint.isSuppressed(LintCategory.REMOVAL)) { if (sym.kind == MDL) { - removalHandler.report(pos, LintWarnings.HasBeenDeprecatedForRemovalModule(sym)); + warningKey = LintWarnings.HasBeenDeprecatedForRemovalModule(sym); } else { - removalHandler.report(pos, LintWarnings.HasBeenDeprecatedForRemoval(sym, sym.location())); + warningKey = LintWarnings.HasBeenDeprecatedForRemoval(sym, sym.location()); } } } else if (!lint.isSuppressed(LintCategory.DEPRECATION)) { if (sym.kind == MDL) { - deprecationHandler.report(pos, LintWarnings.HasBeenDeprecatedModule(sym)); + warningKey = LintWarnings.HasBeenDeprecatedModule(sym); } else { - deprecationHandler.report(pos, LintWarnings.HasBeenDeprecated(sym, sym.location())); + warningKey = LintWarnings.HasBeenDeprecated(sym, sym.location()); } } + Optional.ofNullable(warningKey) + .ifPresent(key -> log.mandatoryWarning(pos, key, DiagnosticFlag.AGGREGATE)); } /** Log a preview warning. @@ -276,7 +255,7 @@ void warnDeprecated(DiagnosticPosition pos, Symbol sym) { */ public void warnPreviewAPI(DiagnosticPosition pos, LintWarning warnKey) { if (!importSuppression && !lint.isSuppressed(LintCategory.PREVIEW)) - preview.reportPreviewWarning(pos, warnKey); + log.mandatoryWarning(pos, warnKey, DiagnosticFlag.AGGREGATE); } /** Log a preview warning. @@ -293,25 +272,15 @@ public void warnRestrictedAPI(DiagnosticPosition pos, Symbol sym) { */ public void warnUnchecked(DiagnosticPosition pos, LintWarning warnKey) { if (!lint.isSuppressed(LintCategory.UNCHECKED)) - uncheckedHandler.report(pos, warnKey); + log.mandatoryWarning(pos, warnKey, DiagnosticFlag.AGGREGATE); } - /** - * Report any deferred diagnostics. - */ - public void reportDeferredDiagnostics() { - deprecationHandler.reportDeferredDiagnostic(); - removalHandler.reportDeferredDiagnostic(); - uncheckedHandler.reportDeferredDiagnostic(); - } - - /** Report a failure to complete a class. * @param pos Position to be used for error reporting. * @param ex The failure to report. */ public Type completionError(DiagnosticPosition pos, CompletionFailure ex) { - log.error(JCDiagnostic.DiagnosticFlag.NON_DEFERRABLE, pos, Errors.CantAccess(ex.sym, ex.getDetailValue())); + log.error(DiagnosticFlag.NON_DEFERRABLE, pos, Errors.CantAccess(ex.sym, ex.getDetailValue())); return syms.errType; } @@ -474,12 +443,6 @@ public void newRound() { localClassNameIndexes.clear(); } - public void clear() { - deprecationHandler.clear(); - removalHandler.clear(); - uncheckedHandler.clear(); - } - public void putCompiled(ClassSymbol csym) { compiled.put(Pair.of(csym.packge().modle, csym.flatname), csym); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java index 5fa76c14d3f8f..3f3f5e2f27b67 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryContext.java @@ -28,11 +28,12 @@ import com.sun.source.util.TaskEvent; import com.sun.source.util.TaskListener; import com.sun.tools.javac.api.JavacTool; -import com.sun.tools.javac.code.Preview; +import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.resources.LauncherProperties.Errors; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Context.Factory; +import com.sun.tools.javac.util.Log; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; @@ -120,8 +121,11 @@ void compileProgram() throws Fault { } var opts = options.forProgramCompilation(); var context = new Context(); - MemoryPreview.registerInstance(context); var task = compiler.getTask(out, memoryFileManager, null, opts, null, units, context); + + // This suppresses diagnostics like "Note: Recompile with -Xlint:preview for details." + Log.instance(context).suppressAggregatedWarningNotes(LintCategory.PREVIEW); + var ok = task.call(); if (!ok) { throw new Fault(Errors.CompilationFailed); @@ -269,19 +273,4 @@ private void enableNativeAccess(ModuleLayer.Controller controller, boolean shoul controller.enableNativeAccess(module); } } - - static class MemoryPreview extends Preview { - static void registerInstance(Context context) { - context.put(previewKey, (Factory)MemoryPreview::new); - } - - MemoryPreview(Context context) { - super(context); - } - - @Override - public void reportDeferredDiagnostics() { - // suppress diagnostics like "Note: Recompile with -Xlint:preview for details." - } - } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 52979ab16b412..214f72984e508 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -55,10 +55,10 @@ import com.sun.source.util.TaskEvent; import com.sun.tools.javac.api.MultiTaskListener; import com.sun.tools.javac.code.*; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.CompletionFailure; +import com.sun.tools.javac.code.Symbol.ModuleSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.comp.*; import com.sun.tools.javac.comp.CompileStates.CompileState; @@ -85,10 +85,6 @@ import static com.sun.tools.javac.code.Kinds.Kind.*; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.code.Lint.LintCategory; -import com.sun.tools.javac.code.Symbol.ModuleSymbol; - import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.resources.CompilerProperties.Notes; @@ -274,10 +270,6 @@ else if (option.equals("class")) */ protected Source source; - /** The preview language version. - */ - protected Preview preview; - /** The module for code generation. */ protected Gen gen; @@ -413,7 +405,6 @@ public JavaCompiler(Context context) { log.error(Errors.CantAccess(ex.sym, ex.getDetailValue())); } source = Source.instance(context); - preview = Preview.instance(context); attr = Attr.instance(context); analyzer = Analyzer.instance(context); chk = Check.instance(context); @@ -1852,8 +1843,7 @@ public void reportDeferredDiagnostics() { else log.warning(Warnings.ProcUseProcOrImplicit); } - chk.reportDeferredDiagnostics(); - preview.reportDeferredDiagnostics(); + log.reportOutstandingNotes(); if (log.compressedOutput) { log.mandatoryNote(null, Notes.CompressedDiags); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java index 59730448bf53a..d7d328ddae4a3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, 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 @@ -25,6 +25,7 @@ package com.sun.tools.javac.util; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import javax.tools.JavaFileObject; @@ -159,35 +160,45 @@ public void error(DiagnosticFlag flag, int pos, Error errorKey) { * maximum number of warnings has been reached. * * @param warningKey The key for the localized warning message. + * @param flags Any additional flags required */ - public void warning(Warning warningKey) { - report(diags.warning(source, null, warningKey)); + public void warning(Warning warningKey, DiagnosticFlag... flags) { + warning(null, warningKey, flags); } /** Report a warning, unless suppressed by the -nowarn option or the * maximum number of warnings has been reached. * @param pos The source position at which to report the warning. * @param warningKey The key for the localized warning message. + * @param flags Any additional flags required */ - public void warning(DiagnosticPosition pos, Warning warningKey) { - report(diags.warning(source, pos, warningKey)); + public void warning(int pos, Warning warningKey, DiagnosticFlag... flags) { + warning(wrap(pos), warningKey, flags); } /** Report a warning, unless suppressed by the -nowarn option or the * maximum number of warnings has been reached. * @param pos The source position at which to report the warning. * @param warningKey The key for the localized warning message. + * @param flags Any additional flags required */ - public void warning(int pos, Warning warningKey) { - report(diags.warning(source, wrap(pos), warningKey)); + public void warning(DiagnosticPosition pos, Warning warningKey, DiagnosticFlag... flags) { + EnumSet flagSet = EnumSet.noneOf(DiagnosticFlag.class); + for (DiagnosticFlag flag : flags) + flagSet.add(flag); + report(diags.create(flagSet, source, pos, warningKey)); } - /** Report a warning. + /** Report a mandatory warning. * @param pos The source position at which to report the warning. * @param warningKey The key for the localized warning message. + * @param flags Any additional flags required */ - public void mandatoryWarning(DiagnosticPosition pos, Warning warningKey) { - report(diags.mandatoryWarning(source, pos, warningKey)); + public void mandatoryWarning(DiagnosticPosition pos, Warning warningKey, DiagnosticFlag... flags) { + EnumSet flagSet = EnumSet.of(DiagnosticFlag.MANDATORY); + for (DiagnosticFlag flag : flags) + flagSet.add(flag); + report(diags.create(flagSet, source, pos, warningKey)); } /** Provide a non-fatal notification, unless suppressed by the -nowarn option. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java index 220ec856d62fd..56e8f227d40c4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java @@ -115,33 +115,6 @@ public JCDiagnostic error( return diag; } - /** - * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options. - * @param lc The lint category for the diagnostic - * @param source The source of the compilation unit, if any, in which to report the warning. - * @param pos The source position at which to report the warning. - * @param key The key for the localized warning message. - * @param args Fields of the warning message. - * @see MandatoryWarningHandler - */ - public JCDiagnostic mandatoryWarning( - LintCategory lc, - DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) { - return mandatoryWarning(source, pos, warningKey(lc, key, args)); - } - - /** - * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options. - * @param source The source of the compilation unit, if any, in which to report the warning. - * @param pos The source position at which to report the warning. - * @param warningKey The key for the localized warning message. - * @see MandatoryWarningHandler - */ - public JCDiagnostic mandatoryWarning( - DiagnosticSource source, DiagnosticPosition pos, Warning warningKey) { - return create(EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, warningKey); - } - /** * Create a warning diagnostic. * @param lc The lint category for the diagnostic @@ -447,6 +420,9 @@ public enum DiagnosticFlag { RECOVERABLE, NON_DEFERRABLE, COMPRESSED, + /** Flags mandatory warnings that should pass through a mandatory warning aggregator. + */ + AGGREGATE, /** Flag for diagnostics that were reported through API methods. */ API, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index fd51729bf2139..44d893d1fb0e2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -30,6 +30,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.EnumMap; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -41,15 +42,18 @@ import javax.tools.JavaFileObject; import com.sun.tools.javac.api.DiagnosticFormatter; +import com.sun.tools.javac.code.Lint; +import com.sun.tools.javac.code.Lint.LintCategory; +import com.sun.tools.javac.code.Source; import com.sun.tools.javac.main.Main; import com.sun.tools.javac.main.Option; import com.sun.tools.javac.tree.EndPosTable; -import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticInfo; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; import static com.sun.tools.javac.main.Option.*; +import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*; /** A class for error logs. Reports errors and warnings, and * keeps track of error numbers and positions. @@ -148,7 +152,7 @@ public DeferredDiagnosticHandler(Predicate filter, boolean passOnN } private boolean deferrable(JCDiagnostic diag) { - return !(diag.isFlagSet(DiagnosticFlag.NON_DEFERRABLE) && passOnNonDeferrable) && filter.test(diag); + return !(diag.isFlagSet(NON_DEFERRABLE) && passOnNonDeferrable) && filter.test(diag); } @Override @@ -237,6 +241,16 @@ public enum WriterKind { NOTICE, WARNING, ERROR, STDOUT, STDERR } */ private JavacMessages messages; + /** + * The compilation context. + */ + private final Context context; + + /** + * The root {@link Lint} singleton. + */ + private Lint rootLint; + /** * Handler for initial dispatch of diagnostics. */ @@ -334,6 +348,7 @@ private static Map initWriters(PrintWriter out, PrintWr private Log(Context context, Map writers) { super(JCDiagnostic.Factory.instance(context)); context.put(logKey, this); + this.context = context; this.writers = writers; @SuppressWarnings("unchecked") // FIXME @@ -517,7 +532,7 @@ private boolean shouldReport(JCDiagnostic d) { if (!shouldReport(file, d.getIntPosition())) return false; - if (!d.isFlagSet(DiagnosticFlag.SOURCE_LEVEL)) + if (!d.isFlagSet(SOURCE_LEVEL)) return true; Pair> coords = new Pair<>(file, getCode(d)); @@ -681,7 +696,49 @@ public void strictWarning(DiagnosticPosition pos, String key, Object ... args) { @Override public void report(JCDiagnostic diagnostic) { diagnosticHandler.report(diagnostic); - } + } + + // Obtain root Lint singleton lazily to avoid init loops + private Lint rootLint() { + if (rootLint == null) + rootLint = Lint.instance(context); + return rootLint; + } + +// Mandatory Warnings + + private final EnumMap aggregators = new EnumMap<>(LintCategory.class); + + private final EnumSet suppressedDeferredMandatory = EnumSet.noneOf(LintCategory.class); + + /** + * Suppress aggregated mandatory warning notes for the specified category. + */ + public void suppressAggregatedWarningNotes(LintCategory category) { + suppressedDeferredMandatory.add(category); + } + + /** + * Report any remaining unreported aggregated mandatory warning notes. + */ + public void reportOutstandingNotes() { + aggregators.entrySet().stream() + .filter(entry -> !suppressedDeferredMandatory.contains(entry.getKey())) + .map(Map.Entry::getValue) + .map(MandatoryWarningAggregator::aggregationNotes) + .flatMap(List::stream) + .forEach(this::report); + aggregators.clear(); + } + + private MandatoryWarningAggregator aggregatorFor(LintCategory lc) { + return switch (lc) { + case PREVIEW -> aggregators.computeIfAbsent(lc, c -> new MandatoryWarningAggregator(this, Source.instance(context), c)); + case DEPRECATION -> aggregators.computeIfAbsent(lc, c -> new MandatoryWarningAggregator(this, null, c, "deprecated")); + case REMOVAL, UNCHECKED -> aggregators.computeIfAbsent(lc, c -> new MandatoryWarningAggregator(this, null, c)); + case null, default -> null; + }; + } /** * Reset the state of this instance. @@ -695,14 +752,19 @@ public void clear() { nsuppressedwarns = 0; while (diagnosticHandler.prev != null) popDiagnosticHandler(diagnosticHandler); + aggregators.values().forEach(MandatoryWarningAggregator::clear); + suppressedDeferredMandatory.clear(); } +// DefaultDiagnosticHandler + /** * Common diagnostic handling. * The diagnostic is counted, and depending on the options and how many diagnostics have been * reported so far, the diagnostic may be handed off to writeDiagnostic. */ private class DefaultDiagnosticHandler extends DiagnosticHandler { + @Override public void report(JCDiagnostic diagnostic) { if (expectDiagKeys != null) @@ -727,6 +789,16 @@ public void report(JCDiagnostic diagnostic) { break; case WARNING: + + // Apply the appropriate mandatory warning aggregator, if needed + if (diagnostic.isFlagSet(AGGREGATE)) { + LintCategory category = diagnostic.getLintCategory(); + boolean verbose = rootLint().isEnabled(category); + if (!aggregatorFor(category).aggregate(diagnostic, verbose)) + return; + } + + // Emit warning unless not mandatory and warnings are disabled if (emitWarnings || diagnostic.isMandatory()) { if (nwarnings < MaxWarnings) { writeDiagnostic(diagnostic); @@ -738,8 +810,7 @@ public void report(JCDiagnostic diagnostic) { break; case ERROR: - if (diagnostic.isFlagSet(DiagnosticFlag.API) || - shouldReport(diagnostic)) { + if (diagnostic.isFlagSet(API) || shouldReport(diagnostic)) { if (nerrors < MaxErrors) { writeDiagnostic(diagnostic); nerrors++; @@ -749,7 +820,7 @@ public void report(JCDiagnostic diagnostic) { } break; } - if (diagnostic.isFlagSet(JCDiagnostic.DiagnosticFlag.COMPRESSED)) { + if (diagnostic.isFlagSet(COMPRESSED)) { compressedOutput = true; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningHandler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningAggregator.java similarity index 72% rename from src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningHandler.java rename to src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningAggregator.java index 046740ddc3028..b6130d384aa5d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningHandler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/MandatoryWarningAggregator.java @@ -25,11 +25,14 @@ package com.sun.tools.javac.util; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Set; import javax.tools.JavaFileObject; +import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @@ -39,9 +42,10 @@ /** - * A handler to process mandatory warnings, setting up a deferred diagnostic + * An aggregator for mandatory warnings, setting up a deferred diagnostic * to be printed at the end of the compilation if some warnings get suppressed - * because too many warnings have already been generated. + * because the lint category is not enabled or too many warnings have already + * been generated. * *

    * Note that the SuppressWarnings annotation can be used to suppress warnings @@ -58,7 +62,7 @@ * This code and its internal interfaces are subject to change or * deletion without notice. */ -public class MandatoryWarningHandler { +class MandatoryWarningAggregator { /** * The kinds of different deferred diagnostics that might be generated @@ -105,64 +109,51 @@ private enum DeferredDiagnosticKind { /** - * Create a handler for mandatory warnings. + * Create an aggregator for mandatory warnings. * * @param log The log on which to generate any diagnostics * @param source Associated source file, or null for none - * @param verbose Specify whether or not detailed messages about - * individual instances should be given, or whether an aggregate - * message should be generated at the end of the compilation. - * Typically set via -Xlint:option. - * @param enforceMandatory - * True if mandatory warnings and notes are being enforced. * @param lc The lint category for all warnings */ - public MandatoryWarningHandler(Log log, Source source, boolean verbose, boolean enforceMandatory, LintCategory lc) { - this(log, source, verbose, enforceMandatory, lc, null); + public MandatoryWarningAggregator(Log log, Source source, LintCategory lc) { + this(log, source, lc, null); } /** - * Create a handler for mandatory warnings. + * Create an aggregator for mandatory warnings. * * @param log The log on which to generate any diagnostics * @param source Associated source file, or null for none - * @param verbose Specify whether or not detailed messages about - * individual instances should be given, or whether an aggregate - * message should be generated at the end of the compilation. - * Typically set via -Xlint:option. - * @param enforceMandatory - * True if mandatory warnings and notes are being enforced. * @param lc The lint category for all warnings * @param prefix A common prefix for the set of message keys for the messages * that may be generated, or null to infer from the lint category. */ - public MandatoryWarningHandler(Log log, Source source, boolean verbose, boolean enforceMandatory, LintCategory lc, String prefix) { + public MandatoryWarningAggregator(Log log, Source source, LintCategory lc, String prefix) { this.log = log; this.source = source; - this.verbose = verbose; this.prefix = prefix != null ? prefix : lc.option; - this.enforceMandatory = enforceMandatory; this.lintCategory = lc; } /** - * Report a mandatory warning. + * Aggregate a mandatory warning and determine whether to emit it. * - * @param pos source code position - * @param warnKey lint warning + * @param diagnostic the mandatory warning + * @param verbose whether the warning's lint category is enabled + * @return true if diagnostic should be emitted, otherwise false */ - public void report(DiagnosticPosition pos, LintWarning warnKey) { + public boolean aggregate(JCDiagnostic diagnostic, boolean verbose) { + Assert.check(diagnostic.isMandatory()); + Assert.check(diagnostic.getLintCategory() == lintCategory); JavaFileObject currentSource = log.currentSourceFile(); - Assert.check(warnKey.getLintCategory() == lintCategory); - if (verbose) { if (sourcesWithReportedWarnings == null) sourcesWithReportedWarnings = new HashSet<>(); - if (log.nwarnings < log.MaxWarnings) { // generate message and remember the source file - logMandatoryWarning(pos, warnKey); sourcesWithReportedWarnings.add(currentSource); + anyWarningEmitted = true; + return true; } else if (deferredDiagnosticKind == null) { // set up deferred message if (sourcesWithReportedWarnings.contains(currentSource)) { @@ -194,30 +185,36 @@ public void report(DiagnosticPosition pos, LintWarning warnKey) { deferredDiagnosticArg = null; } } + return false; } /** - * Report any diagnostic that might have been deferred by previous calls of report(). + * Build and return any accumulated aggregation notes. */ - public void reportDeferredDiagnostic() { + public List aggregationNotes() { + List list = new ArrayList<>(2); if (deferredDiagnosticKind != null) { if (deferredDiagnosticArg == null) { if (source != null) { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), source); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), source); } else { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix)); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix)); } } else { if (source != null) { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg, source); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg, source); } else { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg); } } - - if (!verbose) - logMandatoryNote(deferredDiagnosticSource, prefix + ".recompile"); + if (!anyWarningEmitted) + addNote(list, deferredDiagnosticSource, prefix + ".recompile"); } + return list; + } + + private void addNote(List list, JavaFileObject file, String msg, Object... args) { + list.add(log.diags.mandatoryNote(log.getSource(file), new Note("compiler", msg, args))); } /** @@ -226,12 +223,6 @@ public void reportDeferredDiagnostic() { private final Log log; private final Source source; - /** - * Whether or not to report individual warnings, or simply to report a - * single aggregate warning at the end of the compilation. - */ - private final boolean verbose; - /** * The common prefix for all I18N message keys generated by this handler. */ @@ -268,9 +259,10 @@ public void reportDeferredDiagnostic() { private Object deferredDiagnosticArg; /** - * True if mandatory warnings and notes are being enforced. + * Whether we have actually emitted a warning or just deferred everything. + * In the latter case, the "recompile" notice is included in the summary. */ - private final boolean enforceMandatory; + private boolean anyWarningEmitted; /** * A LintCategory to be included in point-of-use diagnostics to indicate @@ -278,28 +270,6 @@ public void reportDeferredDiagnostic() { */ private final LintCategory lintCategory; - /** - * Reports a mandatory warning to the log. If mandatory warnings - * are not being enforced, treat this as an ordinary warning. - */ - private void logMandatoryWarning(DiagnosticPosition pos, LintWarning warnKey) { - if (enforceMandatory) - log.mandatoryWarning(pos, warnKey); - else - log.warning(pos, warnKey); - } - - /** - * Reports a mandatory note to the log. If mandatory notes are - * not being enforced, treat this as an ordinary note. - */ - private void logMandatoryNote(JavaFileObject file, String msg, Object... args) { - if (enforceMandatory) - log.mandatoryNote(file, new Note("compiler", msg, args)); - else - log.note(file, new Note("compiler", msg, args)); - } - public void clear() { sourcesWithReportedWarnings = null; deferredDiagnosticKind = null; From cde1f5ef56dd247cb6049172d9cdb4dc37cd897d Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 13 Jun 2025 16:35:08 -0500 Subject: [PATCH 39/44] Revert PR #24600 (for JDK-8354447) from this branch so it's not a dependency. --- .../danglingDocComments/DanglingDocCommentsClass.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.java b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.java index 9d2adbc656f51..3f6553d191f57 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.java +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.java @@ -45,13 +45,4 @@ public void m4b() { } /** Good comment. */ int i = 0; } - - /** Dangling comment X */ - - /** - * The {@code @SuppressWarnings} annotation below retroactively - * silences the warning about "Dangling comment X". - */ - @SuppressWarnings("dangling-doc-comments") - public void m5() { } -} +} \ No newline at end of file From 24df3a2e50b414c2f634dcff840462d8b4e72e99 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 13 Jun 2025 17:18:09 -0500 Subject: [PATCH 40/44] Add comment to regression test. --- test/langtools/tools/javac/6304921/TestLog.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/langtools/tools/javac/6304921/TestLog.java b/test/langtools/tools/javac/6304921/TestLog.java index 2a40c6ec4fee5..8f00a14b9e237 100644 --- a/test/langtools/tools/javac/6304921/TestLog.java +++ b/test/langtools/tools/javac/6304921/TestLog.java @@ -130,6 +130,7 @@ public void visitIf(JCTree.JCIf tree) { log.error(tree.pos(), Errors.NotStmt); log.error(nil, Errors.NotStmt); + // some warnings that will be emitted during parsing log.warning(Warnings.ExtraneousSemicolon); log.warning(tree.pos, Warnings.ExtraneousSemicolon); log.warning(tree.pos(), Warnings.ExtraneousSemicolon); From b7adf7c439297604158fcf9909dd61c3f84e5130 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 13 Jun 2025 17:18:21 -0500 Subject: [PATCH 41/44] Revert regression test change that is no longer necessary. --- .../javac/processing/model/util/printing/module-info.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/langtools/tools/javac/processing/model/util/printing/module-info.java b/test/langtools/tools/javac/processing/model/util/printing/module-info.java index 3cf7d8425d98f..5e02bd4cf4213 100644 --- a/test/langtools/tools/javac/processing/model/util/printing/module-info.java +++ b/test/langtools/tools/javac/processing/model/util/printing/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, 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 @@ -25,7 +25,7 @@ * @test * @bug 8173609 * @summary printing of modules - * @compile/ref=module-info.out -Xprint p/P.java -Xlint:-module module-info.java + * @compile/ref=module-info.out -Xprint p/P.java module-info.java */ /** From 5069bf2d66625fd220d693e49a923e0b383ced3c Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Mon, 16 Jun 2025 20:52:10 -0500 Subject: [PATCH 42/44] Restore behavior when both -Xlint:options and -Xlint:-options are given. --- .../com/sun/tools/javac/util/Options.java | 48 ++++++------------- .../tools/javac/lint/LintOptions.java | 11 +++++ .../tools/javac/lint/LintOptions.out | 4 ++ 3 files changed, 29 insertions(+), 34 deletions(-) create mode 100644 test/langtools/tools/javac/lint/LintOptions.java create mode 100644 test/langtools/tools/javac/lint/LintOptions.out diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java index 63f5b0ca75abb..c882fb4cf8a20 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java @@ -172,55 +172,35 @@ public boolean isUnset(Option option, String value) { } /** - * Check whether the given lint category is explicitly enabled or disabled. + * Determine if a specific {@link LintCategory} is explicitly enabled via a custom + * option flag of the form {@code -Flag:key}. * *

    - * If the category is neither enabled nor disabled, return the given default value. + * Note: It's possible the category was also explicitly disabled; this method does not check that. * - * @param option the plain (non-custom) option - * @param lc the {@link LintCategory} in question - * @param defaultValue presumed default value - * @return true if {@code lc} would be included - */ - public boolean isSet(Option option, LintCategory lc, boolean defaultValue) { - Option customOption = option.getCustom(); - if (lc.optionList.stream().anyMatch(alias -> isSet(customOption, alias))) { - return true; - } - if (lc.optionList.stream().anyMatch(alias -> isSet(customOption, "-" + alias))) { - return false; - } - if (isSet(option) || isSet(customOption, Option.LINT_CUSTOM_ALL)) { - return true; - } - if (isSet(customOption, Option.LINT_CUSTOM_NONE)) { - return false; - } - return defaultValue; - } - - /** - * Determine if a specific {@link LintCategory} was explicitly enabled via a custom option flag - * of the form {@code -Flag:all} or {@code -Flag:key}. - * - * @param option the option + * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT}) * @param lc the {@link LintCategory} in question * @return true if {@code lc} has been explicitly enabled */ public boolean isExplicitlyEnabled(Option option, LintCategory lc) { - return isSet(option, lc, false); + Option customOption = option.getCustom(); + return lc.optionList.stream().anyMatch(alias -> isSet(customOption, alias)); } /** - * Determine if a specific {@link LintCategory} was explicitly disabled via a custom option flag - * of the form {@code -Flag:none} or {@code -Flag:-key}. + * Determine if a specific {@link LintCategory} is explicitly disabled via a custom + * option flag of the form {@code -Flag:-key}. * - * @param option the option + *

    + * Note: It's possible the category was also explicitly enabled; this method does not check that. + * + * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT}) * @param lc the {@link LintCategory} in question * @return true if {@code lc} has been explicitly disabled */ public boolean isExplicitlyDisabled(Option option, LintCategory lc) { - return !isSet(option, lc, true); + Option customOption = option.getCustom(); + return lc.optionList.stream().anyMatch(alias -> isSet(customOption, "-" + alias)); } public void put(String name, String value) { diff --git a/test/langtools/tools/javac/lint/LintOptions.java b/test/langtools/tools/javac/lint/LintOptions.java new file mode 100644 index 0000000000000..81b0a0dc3250b --- /dev/null +++ b/test/langtools/tools/javac/lint/LintOptions.java @@ -0,0 +1,11 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8359596 + * @summary Verify behavior when both "-Xlint:options" and "-Xlint:-options" are given + * @compile/fail/ref=LintOptions.out -Werror -XDrawDiagnostics -source 21 -target 21 LintOptions.java + * @compile/fail/ref=LintOptions.out -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:options LintOptions.java + * @compile -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:-options LintOptions.java + * @compile -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:options -Xlint:-options LintOptions.java + */ +class LintOptions { +} diff --git a/test/langtools/tools/javac/lint/LintOptions.out b/test/langtools/tools/javac/lint/LintOptions.out new file mode 100644 index 0000000000000..020c626ee5c20 --- /dev/null +++ b/test/langtools/tools/javac/lint/LintOptions.out @@ -0,0 +1,4 @@ +- compiler.warn.source.no.system.modules.path: 21, (compiler.misc.source.no.system.modules.path.with.target: 21, 21) +- compiler.err.warnings.and.werror +1 error +1 warning From 8dcea7004f9ad1f3c8ee5e6aeb934ed0518a2a37 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Tue, 17 Jun 2025 08:59:59 -0500 Subject: [PATCH 43/44] No need for /nodynamiccopyright/ with this test. --- .../tools/javac/lint/LintOptions.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/langtools/tools/javac/lint/LintOptions.java b/test/langtools/tools/javac/lint/LintOptions.java index 81b0a0dc3250b..b5825fce72841 100644 --- a/test/langtools/tools/javac/lint/LintOptions.java +++ b/test/langtools/tools/javac/lint/LintOptions.java @@ -1,5 +1,30 @@ /* - * @test /nodynamiccopyright/ + * 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. + */ + +/* + * @test * @bug 8359596 * @summary Verify behavior when both "-Xlint:options" and "-Xlint:-options" are given * @compile/fail/ref=LintOptions.out -Werror -XDrawDiagnostics -source 21 -target 21 LintOptions.java From 9e788319c5109a86e1bc1ec3417b3301aa1d0153 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Wed, 18 Jun 2025 16:06:32 -0500 Subject: [PATCH 44/44] Ensure that "-Xlint:none" still works for the affected warnings. The extra checks for "-Xlint:none" are needed now because of JDK-8352612, which changed the behavior of "-Xlint:none" to no longer imply "-nowarn", which allowed the affected warnings to get away with skipping that check. --- .../com/sun/tools/javac/comp/Modules.java | 2 +- .../com/sun/tools/javac/main/Arguments.java | 4 +- .../com/sun/tools/javac/util/Options.java | 41 ++++++++++++++++++- .../tools/javac/lint/LintOptions.java | 2 + 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java index a159793fe327f..0015b73631a99 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java @@ -205,7 +205,7 @@ protected Modules(Context context) { allowAccessIntoSystem = options.isUnset(Option.RELEASE); - lintOptions = !options.isExplicitlyDisabled(Option.XLINT, LintCategory.OPTIONS); + lintOptions = !options.isDisabled(Option.XLINT, LintCategory.OPTIONS); multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH); ClassWriter classWriter = ClassWriter.instance(context); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java index 9e3a978c3bc9a..b1dfb0378bde5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java @@ -503,7 +503,7 @@ public boolean validate() { } } else { // single-module or legacy mode - boolean lintPaths = !options.isExplicitlyDisabled(Option.XLINT, LintCategory.PATH); + boolean lintPaths = !options.isDisabled(Option.XLINT, LintCategory.PATH); if (lintPaths) { Path outDirParent = outDir.getParent(); if (outDirParent != null && Files.exists(outDirParent.resolve("module-info.class"))) { @@ -576,7 +576,7 @@ public boolean validate() { reportDiag(Errors.SourcepathModulesourcepathConflict); } - boolean lintOptions = !options.isExplicitlyDisabled(Option.XLINT, LintCategory.OPTIONS); + boolean lintOptions = !options.isDisabled(Option.XLINT, LintCategory.OPTIONS); if (lintOptions && source.compareTo(Source.DEFAULT) < 0 && !options.isSet(Option.RELEASE)) { if (fm instanceof BaseFileManager baseFileManager) { if (source.compareTo(Source.JDK8) <= 0) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java index c882fb4cf8a20..cbc69feb421ab 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java @@ -171,12 +171,46 @@ public boolean isUnset(Option option, String value) { return !isSet(option, value); } + /** + * Determine if a specific {@link LintCategory} is enabled via a custom + * option flag of the form {@code -Flag}, {@code -Flag:all}, or {@code -Flag:key}. + * + *

    + * Note: It's possible the category was also disabled; this method does not check that. + * + * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT}) + * @param lc the {@link LintCategory} in question + * @return true if {@code lc} has been enabled + */ + public boolean isEnabled(Option option, LintCategory lc) { + Option custom = option.getCustom(); + return isExplicitlyEnabled(option, lc) || isSet(custom) || isSet(custom, Option.LINT_CUSTOM_ALL); + } + + /** + * Determine if a specific {@link LintCategory} is disabled via a custom + * option flag of the form {@code -Flag:none} or {@code -Flag:-key}. + * + *

    + * Note: It's possible the category was also enabled; this method does not check that. + * + * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT}) + * @param lc the {@link LintCategory} in question + * @return true if {@code lc} has been disabled + */ + public boolean isDisabled(Option option, LintCategory lc) { + return isExplicitlyDisabled(option, lc) || isSet(option.getCustom(), Option.LINT_CUSTOM_NONE); + } + /** * Determine if a specific {@link LintCategory} is explicitly enabled via a custom * option flag of the form {@code -Flag:key}. * *

    - * Note: It's possible the category was also explicitly disabled; this method does not check that. + * Note: This does not check for option flags of the form {@code -Flag} or {@code -Flag:all}. + * + *

    + * Note: It's possible the category was also disabled; this method does not check that. * * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT}) * @param lc the {@link LintCategory} in question @@ -192,7 +226,10 @@ public boolean isExplicitlyEnabled(Option option, LintCategory lc) { * option flag of the form {@code -Flag:-key}. * *

    - * Note: It's possible the category was also explicitly enabled; this method does not check that. + * Note: This does not check for an option flag of the form {@code -Flag:none}. + * + *

    + * Note: It's possible the category was also enabled; this method does not check that. * * @param option the plain (non-custom) version of the option (e.g., {@link Option#XLINT}) * @param lc the {@link LintCategory} in question diff --git a/test/langtools/tools/javac/lint/LintOptions.java b/test/langtools/tools/javac/lint/LintOptions.java index b5825fce72841..10def72a31a5e 100644 --- a/test/langtools/tools/javac/lint/LintOptions.java +++ b/test/langtools/tools/javac/lint/LintOptions.java @@ -31,6 +31,8 @@ * @compile/fail/ref=LintOptions.out -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:options LintOptions.java * @compile -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:-options LintOptions.java * @compile -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:options -Xlint:-options LintOptions.java + * @compile -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:none LintOptions.java + * @compile -Werror -XDrawDiagnostics -source 21 -target 21 -Xlint:options -Xlint:none LintOptions.java */ class LintOptions { }

  • * Note that the SuppressWarnings annotation can be used to suppress warnings @@ -58,7 +62,7 @@ * This code and its internal interfaces are subject to change or * deletion without notice. */ -public class MandatoryWarningHandler { +class MandatoryWarningAggregator { /** * The kinds of different deferred diagnostics that might be generated @@ -105,64 +109,51 @@ private enum DeferredDiagnosticKind { /** - * Create a handler for mandatory warnings. + * Create an aggregator for mandatory warnings. * * @param log The log on which to generate any diagnostics * @param source Associated source file, or null for none - * @param verbose Specify whether or not detailed messages about - * individual instances should be given, or whether an aggregate - * message should be generated at the end of the compilation. - * Typically set via -Xlint:option. - * @param enforceMandatory - * True if mandatory warnings and notes are being enforced. * @param lc The lint category for all warnings */ - public MandatoryWarningHandler(Log log, Source source, boolean verbose, boolean enforceMandatory, LintCategory lc) { - this(log, source, verbose, enforceMandatory, lc, null); + public MandatoryWarningAggregator(Log log, Source source, LintCategory lc) { + this(log, source, lc, null); } /** - * Create a handler for mandatory warnings. + * Create an aggregator for mandatory warnings. * * @param log The log on which to generate any diagnostics * @param source Associated source file, or null for none - * @param verbose Specify whether or not detailed messages about - * individual instances should be given, or whether an aggregate - * message should be generated at the end of the compilation. - * Typically set via -Xlint:option. - * @param enforceMandatory - * True if mandatory warnings and notes are being enforced. * @param lc The lint category for all warnings * @param prefix A common prefix for the set of message keys for the messages * that may be generated, or null to infer from the lint category. */ - public MandatoryWarningHandler(Log log, Source source, boolean verbose, boolean enforceMandatory, LintCategory lc, String prefix) { + public MandatoryWarningAggregator(Log log, Source source, LintCategory lc, String prefix) { this.log = log; this.source = source; - this.verbose = verbose; this.prefix = prefix != null ? prefix : lc.option; - this.enforceMandatory = enforceMandatory; this.lintCategory = lc; } /** - * Report a mandatory warning. + * Aggregate a mandatory warning and determine whether to emit it. * - * @param pos source code position - * @param warnKey lint warning + * @param diagnostic the mandatory warning + * @param verbose whether the warning's lint category is enabled + * @return true if diagnostic should be emitted, otherwise false */ - public void report(DiagnosticPosition pos, LintWarning warnKey) { + public boolean aggregate(JCDiagnostic diagnostic, boolean verbose) { + Assert.check(diagnostic.isMandatory()); + Assert.check(diagnostic.getLintCategory() == lintCategory); JavaFileObject currentSource = log.currentSourceFile(); - Assert.check(warnKey.getLintCategory() == lintCategory); - if (verbose) { if (sourcesWithReportedWarnings == null) sourcesWithReportedWarnings = new HashSet<>(); - if (log.nwarnings < log.MaxWarnings) { // generate message and remember the source file - logMandatoryWarning(pos, warnKey); sourcesWithReportedWarnings.add(currentSource); + anyWarningEmitted = true; + return true; } else if (deferredDiagnosticKind == null) { // set up deferred message if (sourcesWithReportedWarnings.contains(currentSource)) { @@ -194,30 +185,36 @@ public void report(DiagnosticPosition pos, LintWarning warnKey) { deferredDiagnosticArg = null; } } + return false; } /** - * Report any diagnostic that might have been deferred by previous calls of report(). + * Build and return any accumulated aggregation notes. */ - public void reportDeferredDiagnostic() { + public List aggregationNotes() { + List list = new ArrayList<>(2); if (deferredDiagnosticKind != null) { if (deferredDiagnosticArg == null) { if (source != null) { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), source); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), source); } else { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix)); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix)); } } else { if (source != null) { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg, source); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg, source); } else { - logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg); + addNote(list, deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg); } } - - if (!verbose) - logMandatoryNote(deferredDiagnosticSource, prefix + ".recompile"); + if (!anyWarningEmitted) + addNote(list, deferredDiagnosticSource, prefix + ".recompile"); } + return list; + } + + private void addNote(List list, JavaFileObject file, String msg, Object... args) { + list.add(log.diags.mandatoryNote(log.getSource(file), new Note("compiler", msg, args))); } /** @@ -226,12 +223,6 @@ public void reportDeferredDiagnostic() { private final Log log; private final Source source; - /** - * Whether or not to report individual warnings, or simply to report a - * single aggregate warning at the end of the compilation. - */ - private final boolean verbose; - /** * The common prefix for all I18N message keys generated by this handler. */ @@ -268,9 +259,10 @@ public void reportDeferredDiagnostic() { private Object deferredDiagnosticArg; /** - * True if mandatory warnings and notes are being enforced. + * Whether we have actually emitted a warning or just deferred everything. + * In the latter case, the "recompile" notice is included in the summary. */ - private final boolean enforceMandatory; + private boolean anyWarningEmitted; /** * A LintCategory to be included in point-of-use diagnostics to indicate @@ -278,28 +270,6 @@ public void reportDeferredDiagnostic() { */ private final LintCategory lintCategory; - /** - * Reports a mandatory warning to the log. If mandatory warnings - * are not being enforced, treat this as an ordinary warning. - */ - private void logMandatoryWarning(DiagnosticPosition pos, LintWarning warnKey) { - if (enforceMandatory) - log.mandatoryWarning(pos, warnKey); - else - log.warning(pos, warnKey); - } - - /** - * Reports a mandatory note to the log. If mandatory notes are - * not being enforced, treat this as an ordinary note. - */ - private void logMandatoryNote(JavaFileObject file, String msg, Object... args) { - if (enforceMandatory) - log.mandatoryNote(file, new Note("compiler", msg, args)); - else - log.note(file, new Note("compiler", msg, args)); - } - public void clear() { sourcesWithReportedWarnings = null; deferredDiagnosticKind = null; diff --git a/test/langtools/tools/javac/6304921/TestLog.java b/test/langtools/tools/javac/6304921/TestLog.java index 41695554a8800..2a40c6ec4fee5 100644 --- a/test/langtools/tools/javac/6304921/TestLog.java +++ b/test/langtools/tools/javac/6304921/TestLog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -41,7 +41,7 @@ import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.parser.Parser; import com.sun.tools.javac.parser.ParserFactory; -import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; +import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeScanner; @@ -130,10 +130,10 @@ public void visitIf(JCTree.JCIf tree) { log.error(tree.pos(), Errors.NotStmt); log.error(nil, Errors.NotStmt); - log.warning(LintWarnings.DivZero); - log.warning(tree.pos, LintWarnings.DivZero); - log.warning(tree.pos(), LintWarnings.DivZero); - log.warning(nil, LintWarnings.DivZero); + log.warning(Warnings.ExtraneousSemicolon); + log.warning(tree.pos, Warnings.ExtraneousSemicolon); + log.warning(tree.pos(), Warnings.ExtraneousSemicolon); + log.warning(nil, Warnings.ExtraneousSemicolon); } private Log log; diff --git a/test/langtools/tools/javac/ImplicitClass/ErrorRecovery.java b/test/langtools/tools/javac/ImplicitClass/ErrorRecovery.java index 883580893af2a..cb562c6d0d521 100644 --- a/test/langtools/tools/javac/ImplicitClass/ErrorRecovery.java +++ b/test/langtools/tools/javac/ImplicitClass/ErrorRecovery.java @@ -87,8 +87,6 @@ public void testMethodNoReturnType(Path base) throws Exception { .getOutputLines(OutputKind.DIRECT); List expected = List.of( "Test.java:1:1: compiler.err.invalid.meth.decl.ret.type.req", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", "1 error" ); if (!Objects.equals(expected, log)) { diff --git a/test/langtools/tools/javac/ImplicitClass/ImplicitClassRecovery.out b/test/langtools/tools/javac/ImplicitClass/ImplicitClassRecovery.out index 7de6ac6b3ee1b..acdbd1f7cff93 100644 --- a/test/langtools/tools/javac/ImplicitClass/ImplicitClassRecovery.out +++ b/test/langtools/tools/javac/ImplicitClass/ImplicitClassRecovery.out @@ -1,4 +1,2 @@ ImplicitClassRecovery.java:7:33: compiler.err.expected: ';' -- compiler.note.preview.filename: ImplicitClassRecovery.java, DEFAULT -- compiler.note.preview.recompile 1 error diff --git a/test/langtools/tools/javac/ImportModule.java b/test/langtools/tools/javac/ImportModule.java index a53bc92f1b03f..63fd479f53e9a 100644 --- a/test/langtools/tools/javac/ImportModule.java +++ b/test/langtools/tools/javac/ImportModule.java @@ -770,8 +770,8 @@ public class C {} .getOutputLines(Task.OutputKind.DIRECT); List expectedErrors = List.of( - "module-info.java:3:18: compiler.warn.module.not.found: M1", "module-info.java:6:9: compiler.err.cant.resolve: kindname.class, A, , ", + "module-info.java:3:18: compiler.warn.module.not.found: M1", "- compiler.note.preview.filename: module-info.java, DEFAULT", "- compiler.note.preview.recompile", "1 error", diff --git a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out index 2a12111fe4041..23dcddc1e930b 100644 --- a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out +++ b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189a.out @@ -1,4 +1,4 @@ -T6400189a.java:14:35: compiler.warn.unchecked.call.mbr.of.raw.type: getAnnotation(java.lang.Class), java.lang.reflect.Constructor T6400189a.java:14:35: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.annotation.Annotation, java.lang.annotation.Documented) +T6400189a.java:14:35: compiler.warn.unchecked.call.mbr.of.raw.type: getAnnotation(java.lang.Class), java.lang.reflect.Constructor 1 error 1 warning diff --git a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out index 904cd3e677f22..91e34b5becad6 100644 --- a/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out +++ b/test/langtools/tools/javac/OverrideChecks/6400189/T6400189b.out @@ -1,4 +1,4 @@ -T6400189b.java:24:24: compiler.warn.unchecked.call.mbr.of.raw.type: m(T6400189b), T6400189b.B T6400189b.java:24:24: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Object, java.lang.Integer) +T6400189b.java:24:24: compiler.warn.unchecked.call.mbr.of.raw.type: m(T6400189b), T6400189b.B 1 error 1 warning diff --git a/test/langtools/tools/javac/diags/examples/ImplicitClassBad-Filename.java b/test/langtools/tools/javac/diags/examples/ImplicitClassBad-Filename.java index e3bc3d677834c..90b4d8b41f4b8 100644 --- a/test/langtools/tools/javac/diags/examples/ImplicitClassBad-Filename.java +++ b/test/langtools/tools/javac/diags/examples/ImplicitClassBad-Filename.java @@ -22,8 +22,6 @@ */ // key: compiler.err.bad.file.name - // key: compiler.note.preview.filename - // key: compiler.note.preview.recompile // options: -source ${jdk.version} --enable-preview public static void main(String... args) { diff --git a/test/langtools/tools/javac/diags/examples/ImplicitClassHasPackage.java b/test/langtools/tools/javac/diags/examples/ImplicitClassHasPackage.java index c90e0f3dfbcd2..8a7ed564a75f1 100644 --- a/test/langtools/tools/javac/diags/examples/ImplicitClassHasPackage.java +++ b/test/langtools/tools/javac/diags/examples/ImplicitClassHasPackage.java @@ -22,8 +22,6 @@ */ // key: compiler.err.implicit.class.should.not.have.package.declaration - // key: compiler.note.preview.filename - // key: compiler.note.preview.recompile // options: -source ${jdk.version} --enable-preview package implicit.classes; diff --git a/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out b/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out index efceb84c8c7fb..3c93e1711d64c 100644 --- a/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out +++ b/test/langtools/tools/javac/generics/diamond/7188968/T7188968.out @@ -1,11 +1,11 @@ T7188968.java:20:20: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) -T7188968.java:20:9: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo T7188968.java:21:20: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) -T7188968.java:21:29: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo T7188968.java:22:22: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) +T7188968.java:23:24: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) +T7188968.java:20:9: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo +T7188968.java:21:29: compiler.warn.unchecked.call.mbr.of.raw.type: T7188968.Foo(java.util.List,java.lang.Object), T7188968.Foo T7188968.java:22:9: compiler.warn.unchecked.meth.invocation.applied: kindname.constructor, , java.util.List,java.lang.Object, java.util.List,unknown, kindname.class, T7188968.Foo T7188968.java:22:19: compiler.warn.prob.found.req: (compiler.misc.unchecked.assign), java.util.List, java.util.List -T7188968.java:23:24: compiler.err.cant.resolve.location: kindname.variable, unknown, , , (compiler.misc.location: kindname.class, T7188968, null) T7188968.java:23:20: compiler.warn.unchecked.meth.invocation.applied: kindname.method, makeFoo, java.util.List,java.lang.Object, java.util.List,unknown, kindname.class, T7188968.Foo T7188968.java:23:21: compiler.warn.prob.found.req: (compiler.misc.unchecked.assign), java.util.List, java.util.List 4 errors diff --git a/test/langtools/tools/javac/lambda/TargetType22.out b/test/langtools/tools/javac/lambda/TargetType22.out index d94b2cc60b386..c19aef2411fc7 100644 --- a/test/langtools/tools/javac/lambda/TargetType22.out +++ b/test/langtools/tools/javac/lambda/TargetType22.out @@ -1,4 +1,4 @@ -TargetType22.java:29:21: compiler.warn.unchecked.varargs.non.reifiable.type: A TargetType22.java:40:9: compiler.err.ref.ambiguous: call, kindname.method, call(TargetType22.Sam1), TargetType22, kindname.method, call(TargetType22.SamX), TargetType22 +TargetType22.java:29:21: compiler.warn.unchecked.varargs.non.reifiable.type: A 1 error 1 warning diff --git a/test/langtools/tools/javac/lint/LexicalLintNesting.java b/test/langtools/tools/javac/lint/LexicalLintNesting.java new file mode 100644 index 0000000000000..f167921df816c --- /dev/null +++ b/test/langtools/tools/javac/lint/LexicalLintNesting.java @@ -0,0 +1,170 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8224228 + * @summary Verify lexical lint warnings handle nested declarations with SuppressWarnings correctly + * @compile/fail/ref=LexicalLintNesting.out -XDrawDiagnostics -Xlint:text-blocks -Werror LexicalLintNesting.java + */ + +//@SuppressWarnings("text-blocks") +public class LexicalLintNesting { + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s1 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s2 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + public static class Nested1 { + + @SuppressWarnings("text-blocks") + String s3 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s4 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + public static class Nested1A { + + //@SuppressWarnings("text-blocks") + String s5 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s6 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s7 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s8 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + public static class Nested1B { + + @SuppressWarnings("text-blocks") + String s9 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s10 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s11 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s12 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s13 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s14 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + public static class Nested2 { + + @SuppressWarnings("text-blocks") + String s15 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s16 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + public static class Nested2A { + + //@SuppressWarnings("text-blocks") + String s17 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s18 = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + + } + + @SuppressWarnings("text-blocks") + String s19 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s20 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + public static class Nested2B { + + @SuppressWarnings("text-blocks") + String s21 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s22 = """ + trailing space here:\u0020 + """; + + } + + @SuppressWarnings("text-blocks") + String s23 = """ + trailing space here:\u0020 + """; + + //@SuppressWarnings("text-blocks") + String s24 = """ + trailing space here:\u0020 + """; + + } + + //@SuppressWarnings("text-blocks") + /* WARNING HERE */ String s25 = """ + trailing space here:\u0020 + """; + + @SuppressWarnings("text-blocks") + String s26 = """ + trailing space here:\u0020 + """; +} diff --git a/test/langtools/tools/javac/lint/LexicalLintNesting.out b/test/langtools/tools/javac/lint/LexicalLintNesting.out new file mode 100644 index 0000000000000..b16db47cf528e --- /dev/null +++ b/test/langtools/tools/javac/lint/LexicalLintNesting.out @@ -0,0 +1,10 @@ +LexicalLintNesting.java:12:36: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:30:40: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:55:40: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:68:45: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:80:41: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:92:37: compiler.warn.trailing.white.space.will.be.removed +LexicalLintNesting.java:162:37: compiler.warn.trailing.white.space.will.be.removed +- compiler.err.warnings.and.werror +1 error +7 warnings diff --git a/test/langtools/tools/javac/lint/TextBlockSuppress.java b/test/langtools/tools/javac/lint/TextBlockSuppress.java new file mode 100644 index 0000000000000..05019be6bc212 --- /dev/null +++ b/test/langtools/tools/javac/lint/TextBlockSuppress.java @@ -0,0 +1,61 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8224228 + * @summary Verify SuppressWarnings works for LintCategore.TEXT_BLOCKS + * @compile/fail/ref=TextBlockSuppress.out -XDrawDiagnostics -Xlint:text-blocks -Werror TextBlockSuppress.java + */ + +public class TextBlockSuppress { + + public static class Example1 { + public void method() { + String s = """ + trailing space here:\u0020 + """; // SHOULD get a warning here + } + } + + @SuppressWarnings("text-blocks") + public static class Example2 { + public void method() { + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } + + public static class Example3 { + @SuppressWarnings("text-blocks") + public void method() { + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } + + public static class Example4 { + { + String s = """ + trailing space here:\u0020 + """; // SHOULD get a warning here + } + } + + @SuppressWarnings("text-blocks") + public static class Example5 { + { + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } + + public static class Example6 { + public void method() { + @SuppressWarnings("text-blocks") + String s = """ + trailing space here:\u0020 + """; // SHOULD NOT get a warning here + } + } +} diff --git a/test/langtools/tools/javac/lint/TextBlockSuppress.out b/test/langtools/tools/javac/lint/TextBlockSuppress.out new file mode 100644 index 0000000000000..d16f080a13384 --- /dev/null +++ b/test/langtools/tools/javac/lint/TextBlockSuppress.out @@ -0,0 +1,5 @@ +TextBlockSuppress.java:12:24: compiler.warn.trailing.white.space.will.be.removed +TextBlockSuppress.java:38:24: compiler.warn.trailing.white.space.will.be.removed +- compiler.err.warnings.and.werror +1 error +2 warnings diff --git a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out index 6fe57b8979d08..1656d8eeff5d7 100644 --- a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out +++ b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5.out @@ -1,4 +1,4 @@ -Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 P.java:10:18: compiler.warn.has.been.deprecated: foo(), Q +Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 Q.java:17:25: compiler.warn.has.been.deprecated: foo(), Q 3 warnings diff --git a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out index fe4a91e258432..5dee9c1414c29 100644 --- a/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out +++ b/test/langtools/tools/javac/mandatoryWarnings/deprecated/Test5b.out @@ -1,4 +1,4 @@ -Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 P.java:10:18: compiler.warn.has.been.deprecated: foo(), Q +Q.java:7:10: compiler.warn.has.been.deprecated: bar(), Q2 - compiler.note.deprecated.filename.additional: Q.java 2 warnings diff --git a/test/langtools/tools/javac/modules/AnnotationsOnModules.java b/test/langtools/tools/javac/modules/AnnotationsOnModules.java index 291633b4fb240..3f13bc5e913dc 100644 --- a/test/langtools/tools/javac/modules/AnnotationsOnModules.java +++ b/test/langtools/tools/javac/modules/AnnotationsOnModules.java @@ -599,8 +599,8 @@ public void testModuleDeprecation(Path base) throws Exception { "1 warning"); } else if (suppress.equals(DEPRECATED_JAVADOC)) { expected = Arrays.asList( - "module-info.java:1:19: compiler.warn.missing.deprecated.annotation", "module-info.java:2:14: compiler.warn.has.been.deprecated.module: m1x", + "module-info.java:1:19: compiler.warn.missing.deprecated.annotation", "2 warnings"); } else { expected = Arrays.asList(""); diff --git a/test/langtools/tools/javac/preview/PreviewErrors.java b/test/langtools/tools/javac/preview/PreviewErrors.java index eab5b2af9bfae..db17aabbd425f 100644 --- a/test/langtools/tools/javac/preview/PreviewErrors.java +++ b/test/langtools/tools/javac/preview/PreviewErrors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -324,7 +324,9 @@ public void test(Object o) { ok = true; switch (elementType) { case LANGUAGE -> { - if (lint == Lint.ENABLE_PREVIEW) { + if (suppress == Suppress.YES) { + expected = Set.of(); + } else if (lint == Lint.ENABLE_PREVIEW) { expected = Set.of("5:41:compiler.warn.preview.feature.use"); } else { expected = Set.of("-1:-1:compiler.note.preview.filename", diff --git a/test/langtools/tools/javac/preview/PreviewTest.java b/test/langtools/tools/javac/preview/PreviewTest.java index 36f1e70acd07d..e681f3f837e54 100644 --- a/test/langtools/tools/javac/preview/PreviewTest.java +++ b/test/langtools/tools/javac/preview/PreviewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -587,12 +587,12 @@ interface Produce { "Test.java:19:11: compiler.err.is.preview: test()", "Test.java:20:11: compiler.err.is.preview: test()", "Test.java:21:11: compiler.err.is.preview: test()", - "Test.java:24:11: compiler.warn.is.preview.reflective: test()", "Test.java:29:16: compiler.err.is.preview: preview.api.Preview", "Test.java:32:21: compiler.err.is.preview: test()", "Test.java:36:21: compiler.err.is.preview: test()", "Test.java:40:13: compiler.err.is.preview: test()", "Test.java:41:21: compiler.err.is.preview: FIELD", + "Test.java:24:11: compiler.warn.is.preview.reflective: test()", "17 errors", "1 warning"); @@ -792,6 +792,99 @@ public void test(NonPreview np, Preview p) { throw new Exception("expected output not found" + log); } + @Test //JDK-8224228: + public void testSuppressWarnings(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public class Preview { + public static int test() { + return 0; + } + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.Preview; + public class Test { + + public static class Example1 { + public void method() { + Preview.test(); // SHOULD get a warning here + } + } + + @SuppressWarnings("preview") + public static class Example2 { + public void method() { + Preview.test(); // SHOULD NOT get a warning here + } + } + + public static class Example3 { + @SuppressWarnings("preview") + public void method() { + Preview.test(); // SHOULD NOT get a warning here + } + } + + public static class Example4 { + { + Preview.test(); // SHOULD get a warning here + } + } + + @SuppressWarnings("preview") + public static class Example5 { + { + Preview.test(); // SHOULD NOT get a warning here + } + } + + public static class Example6 { + @SuppressWarnings("preview") + int x = Preview.test(); // SHOULD NOT get a warning here + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "--enable-preview", + "-Xlint:preview", + "-source", String.valueOf(Runtime.version().feature()), + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = + List.of("Test.java:7:11: compiler.warn.is.preview: preview.api.Preview", + "Test.java:27:11: compiler.warn.is.preview: preview.api.Preview", + "2 warnings"); + + if (!log.equals(expected)) + throw new Exception("expected output not found: " + log); + } + @Test //JDK-8343540: public void nonPreviewImplementsPreview5(Path base) throws Exception { Path apiSrc = base.resolve("api-src"); diff --git a/test/langtools/tools/javac/processing/model/util/printing/module-info.java b/test/langtools/tools/javac/processing/model/util/printing/module-info.java index 5e02bd4cf4213..3cf7d8425d98f 100644 --- a/test/langtools/tools/javac/processing/model/util/printing/module-info.java +++ b/test/langtools/tools/javac/processing/model/util/printing/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -25,7 +25,7 @@ * @test * @bug 8173609 * @summary printing of modules - * @compile/ref=module-info.out -Xprint p/P.java module-info.java + * @compile/ref=module-info.out -Xprint p/P.java -Xlint:-module module-info.java */ /** diff --git a/test/langtools/tools/javac/varargs/7097436/T7097436.out b/test/langtools/tools/javac/varargs/7097436/T7097436.out index 5e35910d2faca..392869f3b9f38 100644 --- a/test/langtools/tools/javac/varargs/7097436/T7097436.out +++ b/test/langtools/tools/javac/varargs/7097436/T7097436.out @@ -1,6 +1,6 @@ -T7097436.java:13:20: compiler.warn.varargs.unsafe.use.varargs.param: ls -T7097436.java:14:25: compiler.warn.varargs.unsafe.use.varargs.param: ls T7097436.java:15:20: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List[], java.lang.String) T7097436.java:16:26: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List[], java.lang.Integer[]) +T7097436.java:13:20: compiler.warn.varargs.unsafe.use.varargs.param: ls +T7097436.java:14:25: compiler.warn.varargs.unsafe.use.varargs.param: ls 2 errors 2 warnings diff --git a/test/langtools/tools/javac/warnings/6594914/T6594914a.out b/test/langtools/tools/javac/warnings/6594914/T6594914a.out index 62f99072a7ad5..d3d759ca044b9 100644 --- a/test/langtools/tools/javac/warnings/6594914/T6594914a.out +++ b/test/langtools/tools/javac/warnings/6594914/T6594914a.out @@ -1,7 +1,7 @@ T6594914a.java:11:5: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:16:16: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6594914a.java:16:52: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:16:33: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:17:20: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6594914a.java:16:52: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6594914a.java:24:9: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package 6 warnings diff --git a/test/langtools/tools/javac/warnings/7090499/T7090499.out b/test/langtools/tools/javac/warnings/7090499/T7090499.out index 1ff9e164e482e..2241c0a04bb1d 100644 --- a/test/langtools/tools/javac/warnings/7090499/T7090499.out +++ b/test/langtools/tools/javac/warnings/7090499/T7090499.out @@ -1,14 +1,14 @@ +T7090499.java:26:10: compiler.err.improperly.formed.type.inner.raw.param +T7090499.java:27:10: compiler.err.improperly.formed.type.inner.raw.param +T7090499.java:28:17: compiler.err.improperly.formed.type.inner.raw.param +T7090499.java:28:10: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:18:5: compiler.warn.raw.class.use: T7090499, T7090499 T7090499.java:18:22: compiler.warn.raw.class.use: T7090499, T7090499 T7090499.java:20:10: compiler.warn.raw.class.use: T7090499.A.X, T7090499.A.X T7090499.java:21:10: compiler.warn.raw.class.use: T7090499.A.Z, T7090499.A.Z T7090499.java:24:17: compiler.warn.raw.class.use: T7090499.B, T7090499.B -T7090499.java:26:10: compiler.err.improperly.formed.type.inner.raw.param -T7090499.java:27:10: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:28:18: compiler.warn.raw.class.use: T7090499.B, T7090499.B -T7090499.java:28:17: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:28:11: compiler.warn.raw.class.use: T7090499.B, T7090499.B -T7090499.java:28:10: compiler.err.improperly.formed.type.inner.raw.param T7090499.java:30:32: compiler.warn.raw.class.use: T7090499.B, T7090499.B T7090499.java:33:13: compiler.warn.raw.class.use: T7090499.A, T7090499.A T7090499.java:33:24: compiler.warn.raw.class.use: T7090499.A, T7090499.A diff --git a/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java b/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java index b5062a9b63fcd..cdf3ca07a9798 100644 --- a/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java +++ b/test/langtools/tools/javac/warnings/UnneededStrictfpWarningToolBox.java @@ -116,8 +116,8 @@ public void testWithAndWithOutLint(Path base) throws IOException { var expected = List.of("UnneededStrictfpWarning1.java:1:17: compiler.warn.strictfp", "UnneededStrictfpWarning1.java:10:10: compiler.warn.strictfp", "UnneededStrictfpWarning1.java:12:29: compiler.warn.strictfp", - "UnneededStrictfpWarning1.java:16:28: compiler.warn.strictfp", "UnneededStrictfpWarning1.java:18:21: compiler.warn.strictfp", + "UnneededStrictfpWarning1.java:16:28: compiler.warn.strictfp", "5 warnings"); checkLog(log, expected); } diff --git a/test/langtools/tools/javac/warnings/suppress/T6480588.out b/test/langtools/tools/javac/warnings/suppress/T6480588.out index 630ba55523dac..267ef32c96408 100644 --- a/test/langtools/tools/javac/warnings/suppress/T6480588.out +++ b/test/langtools/tools/javac/warnings/suppress/T6480588.out @@ -1,19 +1,19 @@ T6480588.java:16:24: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:16:51: compiler.warn.has.been.deprecated: DeprecatedInterface, compiler.misc.unnamed.package T6480588.java:15:2: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package +T6480588.java:18:35: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:18:12: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:18:65: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:30:5: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:33:25: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:33:52: compiler.warn.has.been.deprecated: DeprecatedInterface, compiler.misc.unnamed.package +T6480588.java:32:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package T6480588.java:17:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package -T6480588.java:18:35: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package +T6480588.java:29:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package T6480588.java:19:9: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:19:34: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:21:9: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package T6480588.java:21:25: compiler.warn.deprecated.annotation.has.no.effect: kindname.variable T6480588.java:21:35: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:30:5: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:29:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package T6480588.java:30:33: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:33:25: compiler.warn.has.been.deprecated: DeprecatedClass, compiler.misc.unnamed.package -T6480588.java:33:52: compiler.warn.has.been.deprecated: DeprecatedInterface, compiler.misc.unnamed.package -T6480588.java:32:6: compiler.warn.has.been.deprecated: DeprecatedAnnotation, compiler.misc.unnamed.package 18 warnings From e67b47be0f79bbcb41ec81b14e7bcd8a76c3932f Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 11 Apr 2025 14:50:27 -0500 Subject: [PATCH 19/44] Add test for retroactive @SuppressWarnings("dangling-doc-comments") behavior. --- .../danglingDocComments/DanglingDocCommentsClass.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.java b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.java index 3f6553d191f57..9d2adbc656f51 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.java +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.java @@ -45,4 +45,13 @@ public void m4b() { } /** Good comment. */ int i = 0; } -} \ No newline at end of file + + /** Dangling comment X */ + + /** + * The {@code @SuppressWarnings} annotation below retroactively + * silences the warning about "Dangling comment X". + */ + @SuppressWarnings("dangling-doc-comments") + public void m5() { } +} From d0954d4c174da17d6634cfe07d30432ebca4b90e Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 11 Apr 2025 15:32:06 -0500 Subject: [PATCH 20/44] Remove another unnecessary call to Check.setLint(). --- .../share/classes/com/sun/tools/javac/comp/Check.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index c1e63f942d835..c176f84edc3c0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -1804,12 +1804,7 @@ else if (unhandledUnerased.nonEmpty()) { // Warn if a deprecated method overridden by a non-deprecated one. if (!isDeprecatedOverrideIgnorable(other, origin)) { - Lint prevLint = setLint(lint.augment(m)); - try { - checkDeprecated(() -> TreeInfo.diagnosticPositionFor(m, tree), m, other); - } finally { - setLint(prevLint); - } + checkDeprecated(() -> TreeInfo.diagnosticPositionFor(m, tree), m, other); } } // where From 4d870e0dd491b21c379dbfeb441671e2a7124a1b Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 11 Apr 2025 16:16:49 -0500 Subject: [PATCH 21/44] Revert unintentional change in the behavior of the "dangling-doc-comments" warning. --- .../sun/tools/javac/parser/JavaTokenizer.java | 3 +- .../sun/tools/javac/parser/JavacParser.java | 41 +++++++++++++++++-- .../sun/tools/javac/util/JCDiagnostic.java | 30 ++++++++++++++ .../com/sun/tools/javac/util/LintMapper.java | 4 +- .../DanglingDocCommentsClass.enabled.out | 4 +- test/lib/jdk/test/lib/security/XMLUtils.java | 2 +- 6 files changed, 73 insertions(+), 11 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java index c5b49af2c09a9..4fdf58959cab4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java @@ -1049,8 +1049,7 @@ public Token readToken() { // If a text block. if (isTextBlock) { // Verify that the incidental indentation is consistent. - Set checks = - TextBlockSupport.checkWhitespace(string); + Set checks = TextBlockSupport.checkWhitespace(string); if (checks.contains(TextBlockSupport.WhitespaceChecks.INCONSISTENT)) { log.warning(pos, LintWarnings.InconsistentWhiteSpaceIndentation); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index 3555fcc5a29e1..916b6e1aa25a6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -113,6 +113,10 @@ public class JavacParser implements Parser { /** End position mappings container */ protected final AbstractEndPosTable endPosTable; + /** A map associating "other nearby documentation comments" + * with the preferred documentation comment for a declaration. */ + protected Map> danglingComments = new HashMap<>(); + // Because of javac's limited lookahead, some contexts are ambiguous in // the presence of type annotations even though they are not ambiguous // in the absence of type annotations. Consider this code: @@ -574,8 +578,16 @@ protected void checkNoMods(int pos, long mods) { * (using {@code token.getDocComment()}. * 3. At the end of the "signature" of the declaration * (that is, before any initialization or body for the - * declaration) any other "recent" comments are - * reported to the log as warnings. + * declaration) any other "recent" comments are saved + * in a map using the primary comment as a key, + * using this method, {@code saveDanglingComments}. + * 4. When the tree node for the declaration is finally + * available, and the primary comment, if any, + * is "attached", (in {@link #attach}) any related + * dangling comments are reported to the log as warnings. + * 5. (Later) Warnings may be generated for the dangling + * comments, subject to the {@code -Xlint} and + * {@code @SuppressWarnings}. * * @param dc the primary documentation comment */ @@ -595,16 +607,21 @@ private void saveDanglingDocComments(Comment dc) { } } + var lb = new ListBuffer(); while (!recentComments.isEmpty()) { var c = recentComments.remove(); if (c != dc) { - reportDanglingDocComment(c); + lb.add(c); } } + danglingComments.put(dc, lb.toList()); } /** Make an entry into docComments hashtable, * provided flag keepDocComments is set and given doc comment is non-null. + * If there are any related "dangling comments", register + * diagnostics to be handled later, when @SuppressWarnings + * can be taken into account. * * @param tree The tree to be used as index in the hashtable * @param dc The doc comment to associate with the tree, or null. @@ -614,9 +631,24 @@ protected T attach(T tree, Comment dc) { if (keepDocComments && dc != null) { docComments.putComment(tree, dc); } + reportDanglingComments(tree, dc); return tree; } + /** Reports all dangling comments associated with the + * primary comment for a declaration against the position + * of the tree node for a declaration. + * + * @param tree the tree node for the declaration + * @param dc the primary comment for the declaration + */ + void reportDanglingComments(JCTree tree, Comment dc) { + var list = danglingComments.remove(dc); + if (list != null) { + list.forEach(c -> reportDanglingDocComment(tree, c)); + } + } + /** * Reports an individual dangling comment as a warning to the log. * The comment may or not may generate an actual diagnostic, depending on @@ -624,9 +656,10 @@ protected T attach(T tree, Comment dc) { * * @param c the comment */ - void reportDanglingDocComment(Comment c) { + void reportDanglingDocComment(JCTree tree, Comment c) { var pos = c.getPos(); if (pos != null && !shebang(c, pos)) { + pos = pos.withLintPosition(tree.getStartPosition()); S.lintWarning(pos, LintWarnings.DanglingDocComment); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java index 19f7104420e6e..7e792772b5fbd 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java @@ -383,6 +383,36 @@ public static interface DiagnosticPosition { * the end position of the tree node. Otherwise, just returns the * same as getPreferredPosition(). */ int getEndPosition(EndPosTable endPosTable); + /** Get the position that determines which Lint configuration applies. */ + default int getLintPosition() { + return getStartPosition(); + } + /** Create a new instance from this instance and the given lint position. */ + default DiagnosticPosition withLintPosition(int lintPos) { + DiagnosticPosition orig = this; + return new DiagnosticPosition() { + @Override + public JCTree getTree() { + return orig.getTree(); + } + @Override + public int getStartPosition() { + return orig.getStartPosition(); + } + @Override + public int getPreferredPosition() { + return orig.getPreferredPosition(); + } + @Override + public int getEndPosition(EndPosTable endPosTable) { + return orig.getEndPosition(endPosTable); + } + @Override + public int getLintPosition() { + return lintPos; + } + }; + } } /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java index 68ff9e28f1dcb..8ed90c5e66e52 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java @@ -239,7 +239,7 @@ Span findDeclSpan(DiagnosticPosition pos) { // Find the narrowest span in the given list that contains the given position static Optional bestMatch(List lintSpans, DiagnosticPosition pos) { - int position = pos.getStartPosition(); + int position = pos.getLintPosition(); Assert.check(position != Position.NOPOS); LintSpan bestSpan = null; for (LintSpan lintSpan : lintSpans) { @@ -275,7 +275,7 @@ boolean contains(int pos) { } boolean contains(DiagnosticPosition pos) { - return contains(pos.getStartPosition()); + return contains(pos.getLintPosition()); } boolean contains(Span that) { diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out index 78285aa45d996..b2dde3ff0ef31 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass.enabled.out @@ -1,7 +1,7 @@ +DanglingDocCommentsClass.java:15:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsClass.java:19:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:10:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:13:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:14:8: compiler.warn.dangling.doc.comment DanglingDocCommentsClass.java:14:69: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass.java:15:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass.java:19:5: compiler.warn.dangling.doc.comment 6 warnings diff --git a/test/lib/jdk/test/lib/security/XMLUtils.java b/test/lib/jdk/test/lib/security/XMLUtils.java index dc52eeb713419..e70a30d9b3d27 100644 --- a/test/lib/jdk/test/lib/security/XMLUtils.java +++ b/test/lib/jdk/test/lib/security/XMLUtils.java @@ -61,7 +61,6 @@ // A collection of test utility methods for parsing, validating and // generating XML Signatures. -@SuppressWarnings("dangling-doc-comments") public class XMLUtils { private static final XMLSignatureFactory FAC = @@ -560,6 +559,7 @@ public KeySelectorResult select(KeyInfo keyInfo, /** * Adds a new rule to "jdk.xml.dsig.secureValidationPolicy" */ + @SuppressWarnings("dangling-doc-comments") public static void addPolicy(String rule) { String value = Security.getProperty("jdk.xml.dsig.secureValidationPolicy"); value = rule + "," + value; From fbca756419b2934bf277d63739251af9f9506bb1 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Fri, 11 Apr 2025 16:32:23 -0500 Subject: [PATCH 22/44] Update regression test expected output after previous commit. --- .../DanglingDocCommentsClass_Line.enabled.out | 4 ++-- .../DanglingDocCommentsClass_Mixed.enabled.out | 2 +- .../danglingDocComments/DanglingDocCommentsEnum.enabled.out | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out index 3ed89c5f5bc23..e97bd630ad8b0 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Line.enabled.out @@ -1,7 +1,7 @@ +DanglingDocCommentsClass_Line.java:21:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsClass_Line.java:26:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:11:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:15:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:17:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Line.java:19:9: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass_Line.java:21:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsClass_Line.java:26:5: compiler.warn.dangling.doc.comment 6 warnings diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out index 1eda729da194d..13e1549357963 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsClass_Mixed.enabled.out @@ -1,4 +1,4 @@ -DanglingDocCommentsClass_Mixed.java:13:1: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Mixed.java:17:5: compiler.warn.dangling.doc.comment DanglingDocCommentsClass_Mixed.java:21:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsClass_Mixed.java:13:1: compiler.warn.dangling.doc.comment 3 warnings diff --git a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out index ddf1b2964de73..33938e86078f8 100644 --- a/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out +++ b/test/langtools/tools/javac/danglingDocComments/DanglingDocCommentsEnum.enabled.out @@ -1,8 +1,8 @@ +DanglingDocCommentsEnum.java:16:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsEnum.java:22:5: compiler.warn.dangling.doc.comment +DanglingDocCommentsEnum.java:28:5: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:10:1: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:13:1: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:14:8: compiler.warn.dangling.doc.comment DanglingDocCommentsEnum.java:14:67: compiler.warn.dangling.doc.comment -DanglingDocCommentsEnum.java:16:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsEnum.java:22:5: compiler.warn.dangling.doc.comment -DanglingDocCommentsEnum.java:28:5: compiler.warn.dangling.doc.comment 7 warnings From d855d512fb8fa6fcce0d1352b357a42aa80f64c3 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Sat, 12 Apr 2025 10:51:48 -0500 Subject: [PATCH 23/44] Remove some obsolete invocations of Check.setLint() and unused imports. --- .../share/classes/com/sun/tools/javac/comp/Modules.java | 7 +------ .../share/classes/com/sun/tools/javac/comp/TypeEnter.java | 5 ----- .../share/classes/com/sun/tools/javac/main/Arguments.java | 1 - .../classes/com/sun/tools/javac/main/JavaCompiler.java | 6 +----- .../classes/com/sun/tools/javac/parser/JavaTokenizer.java | 1 - 5 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java index 895a58014b16c..788b7d08a8a03 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java @@ -787,12 +787,7 @@ public void visitModuleDef(JCModuleDecl tree) { sym.requires = List.nil(); sym.exports = List.nil(); sym.opens = List.nil(); - Lint prevLint = chk.setLint(lint.augment(sym)); - try { - tree.directives.forEach(t -> t.accept(this)); - } finally { - chk.setLint(prevLint); - } + tree.directives.forEach(t -> t.accept(this)); sym.requires = sym.requires.reverse(); sym.exports = sym.exports.reverse(); sym.opens = sym.opens.reverse(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java index 14fd937ecf0aa..13f8b82de96bf 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java @@ -34,7 +34,6 @@ import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Directive.ExportsDirective; import com.sun.tools.javac.code.Directive.RequiresDirective; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Scope.ImportFilter; import com.sun.tools.javac.code.Scope.NamedImportScope; import com.sun.tools.javac.code.Scope.StarImportScope; @@ -108,7 +107,6 @@ public class TypeEnter implements Completer { private final Annotate annotate; private final TypeAnnotations typeAnnotations; private final Types types; - private final Lint lint; private final TypeEnvs typeEnvs; private final Dependencies dependencies; @@ -134,7 +132,6 @@ protected TypeEnter(Context context) { annotate = Annotate.instance(context); typeAnnotations = TypeAnnotations.instance(context); types = Types.instance(context); - lint = Lint.instance(context); typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); Source source = Source.instance(context); @@ -361,7 +358,6 @@ private void resolveImports(JCCompilationUnit tree, Env env) { ImportFilter prevStaticImportFilter = staticImportFilter; ImportFilter prevTypeImportFilter = typeImportFilter; - Lint prevLint = chk.setLint(lint); Env prevEnv = this.env; try { this.env = env; @@ -392,7 +388,6 @@ private void resolveImports(JCCompilationUnit tree, Env env) { } } finally { this.env = prevEnv; - chk.setLint(prevLint); this.staticImportFilter = prevStaticImportFilter; this.typeImportFilter = prevTypeImportFilter; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java index 542b44d72430b..376e9c5399be0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java @@ -52,7 +52,6 @@ import javax.tools.StandardLocation; import com.sun.tools.doclint.DocLint; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.file.BaseFileManager; import com.sun.tools.javac.file.JavacFileManager; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 205d50d8319b9..26bcfaa01f6ed 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -55,10 +55,10 @@ import com.sun.source.util.TaskEvent; import com.sun.tools.javac.api.MultiTaskListener; import com.sun.tools.javac.code.*; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.CompletionFailure; +import com.sun.tools.javac.code.Symbol.ModuleSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.comp.*; import com.sun.tools.javac.comp.CompileStates.CompileState; @@ -86,10 +86,6 @@ import static com.sun.tools.javac.code.Kinds.Kind.*; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.code.Lint.LintCategory; -import com.sun.tools.javac.code.Symbol.ModuleSymbol; - import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.resources.CompilerProperties.Notes; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java index 4fdf58959cab4..618265262b8f5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java @@ -25,7 +25,6 @@ package com.sun.tools.javac.parser; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.code.Source.Feature; From 3f4b5f18208b3c945d55c7463930297f6eccf758 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Sat, 12 Apr 2025 11:38:51 -0500 Subject: [PATCH 24/44] Update ThisEscapeAnalyzer to use LintMapper for Lint calculation. --- .../tools/javac/comp/ThisEscapeAnalyzer.java | 74 ++++++++----------- 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index 77ddd5c65833e..34410106e97fb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -45,7 +45,7 @@ import com.sun.tools.javac.code.Directive; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Lint; +import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Symtab; @@ -61,6 +61,7 @@ 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.LintMapper; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Names; @@ -158,7 +159,7 @@ public class ThisEscapeAnalyzer extends TreeScanner { private final Types types; private final Resolve rs; private final Log log; - private Lint lint; + private final LintMapper lintMapper; // These fields are scoped to the entire compilation unit @@ -238,7 +239,7 @@ protected ThisEscapeAnalyzer(Context context) { syms = Symtab.instance(context); types = Types.instance(context); rs = Resolve.instance(context); - lint = Lint.instance(context); + lintMapper = LintMapper.instance(context); } // @@ -270,7 +271,7 @@ private void doAnalyzeTree(Env env) { Assert.check(methodMap.isEmpty()); // we are not prepared to be used more than once // Short circuit if warnings are totally disabled - if (!lint.isEnabled(Lint.LintCategory.THIS_ESCAPE)) + if (!isWarningEnabled(env.tree)) return; // Determine which packages are exported by the containing module, if any. @@ -289,7 +290,6 @@ private void doAnalyzeTree(Env env) { // Record classes whose outer instance (if any) is non-public. new TreeScanner() { - private Lint lint = ThisEscapeAnalyzer.this.lint; private JCClassDecl currentClass; private boolean nonPublicOuter; @@ -297,8 +297,6 @@ private void doAnalyzeTree(Env env) { public void visitClassDef(JCClassDecl tree) { JCClassDecl currentClassPrev = currentClass; boolean nonPublicOuterPrev = nonPublicOuter; - Lint lintPrev = lint; - lint = lint.augment(tree.sym); try { currentClass = tree; @@ -313,57 +311,44 @@ public void visitClassDef(JCClassDecl tree) { } finally { currentClass = currentClassPrev; nonPublicOuter = nonPublicOuterPrev; - lint = lintPrev; } } @Override public void visitVarDef(JCVariableDecl tree) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try { - // Track warning suppression of fields - if (tree.sym.owner.kind == TYP && !lint.isEnabled(Lint.LintCategory.THIS_ESCAPE)) - suppressed.add(tree.sym); + // Track warning suppression of fields + if (tree.sym.owner.kind == TYP && !isWarningEnabled(tree)) + suppressed.add(tree.sym); - // Recurse - super.visitVarDef(tree); - } finally { - lint = lintPrev; - } + // Recurse + super.visitVarDef(tree); } @Override public void visitMethodDef(JCMethodDecl tree) { - Lint lintPrev = lint; - lint = lint.augment(tree.sym); - try { - // Track warning suppression of constructors - if (TreeInfo.isConstructor(tree) && !lint.isEnabled(Lint.LintCategory.THIS_ESCAPE)) - suppressed.add(tree.sym); + // Track warning suppression of constructors + if (TreeInfo.isConstructor(tree) && !isWarningEnabled(tree)) + suppressed.add(tree.sym); - // Determine if this is a constructor we should analyze - boolean extendable = currentClassIsExternallyExtendable(); - boolean analyzable = extendable && - TreeInfo.isConstructor(tree) && - (tree.sym.flags() & (Flags.PUBLIC | Flags.PROTECTED)) != 0 && - !suppressed.contains(tree.sym); + // Determine if this is a constructor we should analyze + boolean extendable = currentClassIsExternallyExtendable(); + boolean analyzable = extendable && + TreeInfo.isConstructor(tree) && + (tree.sym.flags() & (Flags.PUBLIC | Flags.PROTECTED)) != 0 && + !suppressed.contains(tree.sym); - // Determine if this method is "invokable" in an analysis (can't be overridden) - boolean invokable = !extendable || - TreeInfo.isConstructor(tree) || - (tree.mods.flags & (Flags.STATIC | Flags.PRIVATE | Flags.FINAL)) != 0; + // Determine if this method is "invokable" in an analysis (can't be overridden) + boolean invokable = !extendable || + TreeInfo.isConstructor(tree) || + (tree.mods.flags & (Flags.STATIC | Flags.PRIVATE | Flags.FINAL)) != 0; - // Add method or constructor to map - methodMap.put(tree.sym, new MethodInfo(currentClass, tree, analyzable, invokable)); + // Add method or constructor to map + methodMap.put(tree.sym, new MethodInfo(currentClass, tree, analyzable, invokable)); - // Recurse - super.visitMethodDef(tree); - } finally { - lint = lintPrev; - } + // Recurse + super.visitMethodDef(tree); } // Determines if the current class could be extended in some other package/module @@ -1392,6 +1377,11 @@ private boolean isAnalyzing() { return targetClass != null; } + // Determine if the "this-escape" warning is enabled at the given tree's source code position + private boolean isWarningEnabled(JCTree tree) { + return lintMapper.lintAt(log.currentSourceFile(), tree.pos()).get().isEnabled(LintCategory.THIS_ESCAPE); + }; + // Debugging // Invariant checks From f2d275553dc878e1afe790c387db68f04f6ac35c Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Wed, 16 Apr 2025 17:47:31 -0500 Subject: [PATCH 25/44] Some refactoring & cleanups for ThisEscapeAnalyzer. --- .../tools/javac/comp/ThisEscapeAnalyzer.java | 105 +++++++++--------- .../com/sun/tools/javac/util/LintMapper.java | 3 +- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index 34410106e97fb..6e9180526c1e7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -70,6 +70,7 @@ import static com.sun.tools.javac.code.Kinds.Kind.*; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.tree.JCTree.Tag.*; +import static com.sun.tools.javac.util.Position.NOPOS; /** * Looks for possible 'this' escapes and generates corresponding warnings. @@ -171,9 +172,9 @@ public class ThisEscapeAnalyzer extends TreeScanner { */ private final Map methodMap = new LinkedHashMap<>(); - /** Contains symbols of fields and constructors that have warnings suppressed. + /** Contains symbols of fields that have warnings suppressed. */ - private final Set suppressed = new HashSet<>(); + private final Set suppressedFields = new HashSet<>(); /** Contains classes whose outer instance (if any) is non-public. */ @@ -271,7 +272,7 @@ private void doAnalyzeTree(Env env) { Assert.check(methodMap.isEmpty()); // we are not prepared to be used more than once // Short circuit if warnings are totally disabled - if (!isWarningEnabled(env.tree)) + if (!isWarningEnabledAt(env.tree)) return; // Determine which packages are exported by the containing module, if any. @@ -286,7 +287,7 @@ private void doAnalyzeTree(Env env) { // Build a mapping from symbols of methods to their declarations. // Classify all ctors and methods as analyzable and/or invokable. - // Track which constructors and fields have warnings suppressed. + // Track which constructors and fields don't need to be analyzed. // Record classes whose outer instance (if any) is non-public. new TreeScanner() { @@ -318,8 +319,8 @@ public void visitClassDef(JCClassDecl tree) { public void visitVarDef(JCVariableDecl tree) { // Track warning suppression of fields - if (tree.sym.owner.kind == TYP && !isWarningEnabled(tree)) - suppressed.add(tree.sym); + if (tree.sym.owner.kind == TYP && !isWarningEnabledAt(tree)) + suppressedFields.add(tree.sym); // Recurse super.visitVarDef(tree); @@ -328,24 +329,21 @@ public void visitVarDef(JCVariableDecl tree) { @Override public void visitMethodDef(JCMethodDecl tree) { - // Track warning suppression of constructors - if (TreeInfo.isConstructor(tree) && !isWarningEnabled(tree)) - suppressed.add(tree.sym); + // Gather some useful info + boolean constructor = TreeInfo.isConstructor(tree); + boolean extendableClass = currentClassIsExternallyExtendable(); + boolean nonPrivate = (tree.sym.flags() & (Flags.PUBLIC | Flags.PROTECTED)) != 0; + boolean finalish = (tree.mods.flags & (Flags.STATIC | Flags.PRIVATE | Flags.FINAL)) != 0; + boolean suppressed = !isWarningEnabledAt(tree); // Determine if this is a constructor we should analyze - boolean extendable = currentClassIsExternallyExtendable(); - boolean analyzable = extendable && - TreeInfo.isConstructor(tree) && - (tree.sym.flags() & (Flags.PUBLIC | Flags.PROTECTED)) != 0 && - !suppressed.contains(tree.sym); + boolean analyzable = extendableClass && constructor && nonPrivate && !suppressed; - // Determine if this method is "invokable" in an analysis (can't be overridden) - boolean invokable = !extendable || - TreeInfo.isConstructor(tree) || - (tree.mods.flags & (Flags.STATIC | Flags.PRIVATE | Flags.FINAL)) != 0; + // Determine if it's safe to "invoke" the method in an analysis (i.e., it can't be overridden) + boolean invokable = !extendableClass || constructor || finalish; - // Add method or constructor to map - methodMap.put(tree.sym, new MethodInfo(currentClass, tree, analyzable, invokable)); + // Add this method or constructor to our map + methodMap.put(tree.sym, new MethodInfo(currentClass, tree, constructor, analyzable, invokable, suppressed)); // Recurse super.visitMethodDef(tree); @@ -365,32 +363,29 @@ private boolean currentClassIsExternallyExtendable() { // Analyze non-static field initializers and initialization blocks, // but only for classes having at least one analyzable constructor. methodMap.values().stream() - .filter(MethodInfo::analyzable) - .map(MethodInfo::declaringClass) - .distinct() - .forEach(klass -> { - for (List defs = klass.defs; defs.nonEmpty(); defs = defs.tail) { - - // Ignore static stuff - if ((TreeInfo.flags(defs.head) & Flags.STATIC) != 0) - continue; + .filter(MethodInfo::analyzable) + .map(MethodInfo::declaringClass) + .distinct() + .forEach(klass -> { + klass.defs.stream() + .filter(decl -> (TreeInfo.flags(decl) & Flags.STATIC) == 0) + .forEach(decl -> { + switch (decl) { // Handle field initializers - if (defs.head instanceof JCVariableDecl vardef) { - visitTopLevel(env, klass, () -> { - scan(vardef); + case JCVariableDecl varDecl when !suppressedFields.contains(varDecl.sym) + -> visitTopLevel(env, klass, () -> { + scan(varDecl); copyPendingWarning(); }); - continue; - } // Handle initialization blocks - if (defs.head instanceof JCBlock block) { - visitTopLevel(env, klass, () -> analyzeStatements(block.stats)); - continue; + case JCBlock block -> visitTopLevel(env, klass, () -> analyzeStatements(block.stats)); + + default -> { } } - } - }); + }); + }); // Analyze all of the analyzable constructors we found methodMap.values().stream() @@ -438,16 +433,16 @@ private boolean currentClassIsExternallyExtendable() { DiagnosticPosition[] previous = null; for (DiagnosticPosition[] warning : warningList) { - // Skip duplicates + // Skip duplicates, i.e., when the current stack extends the previous stack if (previous != null && extendsAsPrefix.test(previous, warning)) continue; previous = warning; // Emit warnings showing the entire stack trace - JCDiagnostic.Warning key = LintWarnings.PossibleThisEscape; + JCDiagnostic.LintWarning key = LintWarnings.PossibleThisEscape; int remain = warning.length; do { - DiagnosticPosition pos = warning[--remain]; + DiagnosticPosition pos = warning[--remain].withLintPosition(NOPOS); // skip the normal Lint suppression logic log.warning(pos, key); key = LintWarnings.PossibleThisEscapeLocation; } while (remain > 0); @@ -528,7 +523,7 @@ public void visitVarDef(JCVariableDecl tree) { private void visitVarDef(VarSymbol sym, JCExpression expr) { // Skip if ignoring warnings for this field - if (suppressed.contains(sym)) + if (suppressedFields.contains(sym)) return; // Scan initializer, if any @@ -574,10 +569,6 @@ public void visitApply(JCMethodInvocation invoke) { private void invoke(JCTree site, Symbol sym, List args, RefSet receiverRefs) { - // Skip if ignoring warnings for a constructor invoked via 'this()' - if (suppressed.contains(sym)) - return; - // Ignore final methods in java.lang.Object (getClass(), notify(), etc.) if (sym != null && sym.owner.kind == TYP && @@ -589,6 +580,10 @@ private void invoke(JCTree site, Symbol sym, List args, RefSet args, RefSet args, RefSet receiverRefs, MethodInfo methodInfo) { - Assert.check(methodInfo.invokable()); + Assert.check(methodInfo.invokable); // Collect 'this' references found in method parameters JCMethodDecl method = methodInfo.declaration(); @@ -740,7 +735,7 @@ public void visitNewClass(JCNewClass tree) { RefSet receiverRefs = receiverRefsForConstructor(tree.encl, tsym); // "Invoke" the constructor - if (methodInfo != null && methodInfo.invokable()) + if (methodInfo != null && methodInfo.invokable) invokeInvokable(tree, tree.args, receiverRefs, methodInfo); else invokeUnknown(tree, tree.args, receiverRefs); @@ -1244,7 +1239,7 @@ private void visitTopLevel(Env env, JCClassDecl klass, Runnable act refs.add(new ThisRef(targetClass.sym, EnumSet.of(Indirection.DIRECT))); // Perform action - this.visitScoped(false, action); + visitScoped(false, action); } finally { Assert.check(depth == -1); attrEnv = null; @@ -1378,7 +1373,7 @@ private boolean isAnalyzing() { } // Determine if the "this-escape" warning is enabled at the given tree's source code position - private boolean isWarningEnabled(JCTree tree) { + private boolean isWarningEnabledAt(JCTree tree) { return lintMapper.lintAt(log.currentSourceFile(), tree.pos()).get().isEnabled(LintCategory.THIS_ESCAPE); }; @@ -1784,15 +1779,19 @@ public RefSet clone() { private record MethodInfo( JCClassDecl declaringClass, // the class declaring "declaration" JCMethodDecl declaration, // the method or constructor itself + boolean constructor, // the method is a constructor boolean analyzable, // it's a constructor that we should analyze - boolean invokable) { // it may be safely "invoked" during analysis + boolean invokable, // it may be safely "invoked" during analysis + boolean suppressed) { // the "this-escape" warning is not enabled @Override public String toString() { return "MethodInfo" + "[method=" + declaringClass.sym.flatname + "." + declaration.sym + + ",constructor=" + constructor + ",analyzable=" + analyzable + ",invokable=" + invokable + + ",suppressed=" + suppressed + "]"; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java index 8ed90c5e66e52..4c6439dd1b869 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java @@ -240,7 +240,8 @@ Span findDeclSpan(DiagnosticPosition pos) { // Find the narrowest span in the given list that contains the given position static Optional bestMatch(List lintSpans, DiagnosticPosition pos) { int position = pos.getLintPosition(); - Assert.check(position != Position.NOPOS); + if (position == Position.NOPOS) + return Optional.empty(); LintSpan bestSpan = null; for (LintSpan lintSpan : lintSpans) { if (lintSpan.contains(position) && (bestSpan == null || bestSpan.contains(lintSpan))) { From 3a6dd76be286d3f444a9a4b0597a970e58b566bd Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Wed, 16 Apr 2025 20:01:38 -0500 Subject: [PATCH 26/44] Some refactoring to make code clearer. --- .../sun/tools/javac/util/JCDiagnostic.java | 3 - .../classes/com/sun/tools/javac/util/Log.java | 161 +++++++++--------- 2 files changed, 83 insertions(+), 81 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java index 7e792772b5fbd..25f3297436c35 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java @@ -457,9 +457,6 @@ public enum DiagnosticFlag { /** Flags mandatory warnings that should pass through a mandatory warning aggregator. */ AGGREGATE, - /** Flag that requests verbose logging through the mandatory warning aggregator. - */ - AGGREGATE_VERBOSE, /** Flag for diagnostics that were reported through API methods. */ API, diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index 0df3fded7c2a3..1b313aed77bc8 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -33,7 +33,6 @@ import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -41,7 +40,6 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; import java.util.function.Predicate; import javax.tools.DiagnosticListener; @@ -57,7 +55,6 @@ import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.main.Main; import com.sun.tools.javac.main.Option; -import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.*; @@ -71,6 +68,8 @@ import static com.sun.tools.javac.main.Option.*; import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*; import static com.sun.tools.javac.code.Lint.LintCategory.*; +import static com.sun.tools.javac.resources.CompilerProperties.LintWarnings.RequiresAutomatic; +import static com.sun.tools.javac.resources.CompilerProperties.LintWarnings.RequiresTransitiveAutomatic; import static com.sun.tools.javac.tree.JCTree.Tag.*; /** A class for error logs. Reports errors and warnings, and @@ -135,21 +134,57 @@ protected DiagnosticHandler() { } /** - * Handle a diagnostic. + * Step 1: Handle a diagnostic for which the applicable Lint instance (if any) may not be known yet. */ - public abstract void report(JCDiagnostic diag); + public final void report(JCDiagnostic diag) { + Lint lint = null; + LintCategory category = diag.getLintCategory(); + if (category != null) { // this is a lint warning; find the applicable Lint + DiagnosticPosition pos = diag.getDiagnosticPosition(); + if (pos != null && category.annotationSuppression) { // we should apply the Lint from the warning's position + if ((lint = lintFor(diag)) == null) { + addLintWaiter(currentSourceFile(), diag); // ...but we don't know it yet, so defer + return; + } + } else // we should apply the root Lint + lint = rootLint(); + } + reportWithLint(diag, lint); + } + + /** + * Step 2: Handle a diagnostic for which the applicable Lint instance (if any) is known and provided. + */ + public final void reportWithLint(JCDiagnostic diag, Lint lint) { + + // Apply hackery for REQUIRES_TRANSITIVE_AUTOMATIC (see also Check.checkModuleRequires()) + if (diag.getCode().equals(RequiresTransitiveAutomatic.key()) && !lint.isEnabled(REQUIRES_TRANSITIVE_AUTOMATIC)) { + reportWithLint(diags.warning(diag.getDiagnosticSource(), diag.getDiagnosticPosition(), RequiresAutomatic), lint); + return; + } + + // Apply the lint configuration (if any) and discard the warning if it gets filtered out + if (lint != null) { + LintCategory category = diag.getLintCategory(); + boolean emit = !diag.isFlagSet(DEFAULT_ENABLED) ? // is the warning not enabled by default? + lint.isEnabled(category) : // then emit if the category is enabled + category.annotationSuppression ? // else emit if the category is not suppressed, where + !lint.isSuppressed(category) : // ...suppression happens via @SuppressWarnings + !options.isSet(XLINT_CUSTOM, "-" + category.option); // ...suppression happens via -Xlint:-category + if (!emit) + return; + } + + // Proceed + reportReady(diag); + } /** - * Defer a lint warning because we don't know the {@link Lint} configuration yet. - * - * @param sourceFile the source file - * @param diagnostic waiting diagnostic + * Step 3: Handle a diagnostic to which the applicable Lint instance (if any) has been applied. */ - public void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { - Assert.check(sourceFile != null); - Assert.check(diagnostic.getDiagnosticPosition() != null); - Assert.check(diagnostic.getLintCategory() != null); - Assert.check(diagnostic.getLintCategory().annotationSuppression); + public abstract void reportReady(JCDiagnostic diag); + + protected void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { lintWaitersMap.computeIfAbsent(sourceFile, s -> new LinkedList<>()).add(diagnostic); } @@ -157,35 +192,32 @@ public void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { * Flush any lint waiters whose {@link Lint} configurations are now known. */ public void flushLintWaiters() { - for (Iterator>> i = lintWaitersMap.entrySet().iterator(); i.hasNext(); ) { - Map.Entry> entry = i.next(); + lintWaitersMap.entrySet().removeIf(entry -> { - // Is the file no longer recognized? If so, discard warnings (this can happen with JShell) + // Is the source file no longer recognized? If so, discard warnings (e.g., this can happen with JShell) JavaFileObject sourceFile = entry.getKey(); - if (!lintMapper.isKnown(sourceFile)) { - i.remove(); - continue; - } + if (!lintMapper.isKnown(sourceFile)) + return true; - // Flush those diagnostics for which we know the Lint that applies - List diagnostics = entry.getValue(); + // Flush those diagnostics for which we now know the applicable Lint + List diagnosticList = entry.getValue(); JavaFileObject prevSourceFile = useSource(sourceFile); try { - for (Iterator j = diagnostics.iterator(); j.hasNext(); ) { - JCDiagnostic diag = j.next(); - lintMapper.lintAt(sourceFile, diag.getDiagnosticPosition()).ifPresent(lint -> { - applyLint(lint, diag, this::report); - j.remove(); - }); - } + diagnosticList.removeIf(diag -> { + Lint lint = lintFor(diag); + if (lint != null) { + reportWithLint(diag, lint); + return true; + } + return false; + }); } finally { useSource(prevSourceFile); } - // Discard list if now empty - if (diagnostics.isEmpty()) - i.remove(); - } + // Discard list if empty + return diagnosticList.isEmpty(); + }); } } @@ -195,10 +227,10 @@ public void flushLintWaiters() { public class DiscardDiagnosticHandler extends DiagnosticHandler { @Override - public void report(JCDiagnostic diag) { } + protected void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { } @Override - public void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diagnostic) { } + public void reportReady(JCDiagnostic diag) { } } /** @@ -231,16 +263,16 @@ private boolean deferrable(JCDiagnostic diag) { } @Override - public void report(JCDiagnostic diag) { + public void reportReady(JCDiagnostic diag) { if (deferrable(diag)) { deferred.add(diag); } else { - prev.report(diag); + prev.reportReady(diag); } } @Override - public void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diag) { + protected void addLintWaiter(JavaFileObject sourceFile, JCDiagnostic diag) { if (deferrable(diag)) { super.addLintWaiter(sourceFile, diag); } else { @@ -797,16 +829,11 @@ public void strictWarning(DiagnosticPosition pos, String key, Object ... args) { */ @Override public void report(JCDiagnostic diagnostic) { - LintCategory category = diagnostic.getLintCategory(); - if (category != null) { - if (category.annotationSuppression && diagnostic.getDiagnosticPosition() != null) - diagnosticHandler.addLintWaiter(currentSourceFile(), diagnostic); // subject to @SuppressWarnings - else - applyLint(rootLint(), diagnostic, diagnosticHandler::report); - } else - diagnosticHandler.report(diagnostic); + diagnosticHandler.report(diagnostic); } +// Deferred Lint Calculation + /** * Report unreported lint warnings for which the applicable {@link Lint} configuration is now known. */ @@ -814,33 +841,10 @@ public void reportOutstandingWarnings() { diagnosticHandler.flushLintWaiters(); } - // Apply the given Lint configuration to the diagnostic and, if it survives, pass on downstream - private void applyLint(Lint lint, JCDiagnostic diag, Consumer downstream) { - LintCategory category = diag.getLintCategory(); - - // Fallback hackery for REQUIRES_TRANSITIVE_AUTOMATIC (see also Check.checkModuleRequires()) - if (diag.getCode().equals("compiler.warn.requires.transitive.automatic") && - !lint.isEnabled(REQUIRES_TRANSITIVE_AUTOMATIC)) { - diag = diags.warning(diag.getDiagnosticSource(), diag.getDiagnosticPosition(), LintWarnings.RequiresAutomatic); - category = diag.getLintCategory(); - } - - // Determine whether this diagnostic should be emitted at all - if (diag.isFlagSet(DEFAULT_ENABLED) ? isExplicitlySuppressed(lint, category) : !lint.isEnabled(category)) - return; - - // Configure verbose logging (or not) for diagnostics going through a mandatory warning aggregator - if (diag.isFlagSet(AGGREGATE) && lint.isEnabled(category)) - diag.setFlag(AGGREGATE_VERBOSE); - - // Emit the warning - downstream.accept(diag); - } - - private boolean isExplicitlySuppressed(Lint lint, LintCategory category) { - return category.annotationSuppression ? - lint.isSuppressed(category) : // suppression happens via @SuppressWarnings - options.isSet(XLINT_CUSTOM, "-" + category.option); // suppression happens via -Xlint:-category + // Get the Lint config for the given warning (if known) + private Lint lintFor(JCDiagnostic diag) { + Assert.check(diag.getLintCategory() != null); + return lintMapper.lintAt(currentSourceFile(), diag.getDiagnosticPosition()).orElse(null); } // Obtain root Lint singleton lazily to avoid init loops @@ -910,7 +914,7 @@ public void clear() { */ private class DefaultDiagnosticHandler extends DiagnosticHandler { @Override - public void report(JCDiagnostic diagnostic) { + public void reportReady(JCDiagnostic diagnostic) { if (expectDiagKeys != null) expectDiagKeys.remove(diagnostic.getCode()); @@ -936,9 +940,10 @@ public void report(JCDiagnostic diagnostic) { // Apply the appropriate mandatory warning aggregator, if needed if (diagnostic.isFlagSet(AGGREGATE)) { - boolean verbose = diagnostic.isFlagSet(AGGREGATE_VERBOSE); - if (!aggregatorFor(diagnostic.getLintCategory()).aggregate(diagnostic, verbose)) - break; + LintCategory category = diagnostic.getLintCategory(); + boolean verbose = lintFor(diagnostic).isEnabled(category); + if (!aggregatorFor(category).aggregate(diagnostic, verbose)) + return; } // Emit warning unless not mandatory and warnings are disabled From 7c298b49aa4ea81d43a88451b269ab8ef51f9f42 Mon Sep 17 00:00:00 2001 From: "Archie L. Cobbs" Date: Tue, 22 Apr 2025 15:11:01 -0500 Subject: [PATCH 27/44] More cleanups & refactoring. --- .../tools/javac/comp/ThisEscapeAnalyzer.java | 432 ++++++++++-------- .../sun/tools/javac/main/JavaCompiler.java | 1 + .../com/sun/tools/javac/util/LintMapper.java | 281 +++++++----- .../classes/com/sun/tools/javac/util/Log.java | 11 +- 4 files changed, 411 insertions(+), 314 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java index 6e9180526c1e7..23c20320c6649 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java @@ -25,7 +25,6 @@ package com.sun.tools.javac.comp; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedHashMap; @@ -35,16 +34,19 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collector; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import com.sun.tools.javac.code.Directive; import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.*; @@ -61,6 +63,7 @@ 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.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.LintMapper; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Log; @@ -68,6 +71,7 @@ import com.sun.tools.javac.util.Pair; import static com.sun.tools.javac.code.Kinds.Kind.*; +import static com.sun.tools.javac.code.Lint.LintCategory.THIS_ESCAPE; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.tree.JCTree.Tag.*; import static com.sun.tools.javac.util.Position.NOPOS; @@ -172,10 +176,6 @@ public class ThisEscapeAnalyzer extends TreeScanner { */ private final Map methodMap = new LinkedHashMap<>(); - /** Contains symbols of fields that have warnings suppressed. - */ - private final Set suppressedFields = new HashSet<>(); - /** Contains classes whose outer instance (if any) is non-public. */ private final Set nonPublicOuters = new HashSet<>(); @@ -187,30 +187,25 @@ public class ThisEscapeAnalyzer extends TreeScanner { /** Snapshots of {@link #callStack} where possible 'this' escapes occur. */ - private final ArrayList warningList = new ArrayList<>(); + private final ArrayList warningList = new ArrayList<>(); // These fields are scoped to the constructor being analyzed - /** The declaring class of the "invoked" method we're currently analyzing. + /** The method we're currently analyzing. * This is either the analyzed constructor or some method it invokes. */ - private JCClassDecl methodClass; + private MethodInfo currentMethod; - /** The current "call stack" during our analysis. The first entry is some method - * invoked from the target constructor; if empty, we're still in the constructor. + /** The current "call stack" during our analysis. The first entry is the initial + * constructor we started with, and subsequent entries correspond to invoked methods. + * If we're still in the initial constructor, the list will be empty. */ - private final ArrayDeque callStack = new ArrayDeque<>(); + private final ArrayList callStack = new ArrayList<>(); /** Used to terminate recursion in {@link #invokeInvokable invokeInvokable()}. */ private final Set>> invocations = new HashSet<>(); - /** Snapshot of {@link #callStack} where a possible 'this' escape occurs. - * If non-null, a 'this' escape warning has been found in the current - * constructor statement, initialization block statement, or field initializer. - */ - private DiagnosticPosition[] pendingWarning; - // These fields are scoped to the constructor or invoked method being analyzed /** Current lexical scope depth in the constructor or method we're currently analyzing. @@ -248,6 +243,7 @@ protected ThisEscapeAnalyzer(Context context) { // public void analyzeTree(Env env) { + attrEnv = env; try { doAnalyzeTree(env); } finally { @@ -256,10 +252,9 @@ public void analyzeTree(Env env) { nonPublicOuters.clear(); targetClass = null; warningList.clear(); - methodClass = null; + currentMethod = null; callStack.clear(); invocations.clear(); - pendingWarning = null; depth = -1; refs = null; } @@ -271,8 +266,8 @@ private void doAnalyzeTree(Env env) { Assert.check(checkInvariants(false, false)); Assert.check(methodMap.isEmpty()); // we are not prepared to be used more than once - // Short circuit if warnings are totally disabled - if (!isWarningEnabledAt(env.tree)) + // Short circuit if this calculation is unnecessary + if (!lintMapper.lintAt(env.toplevel.sourcefile, env.tree.pos()).get().isEnabled(THIS_ESCAPE)) return; // Determine which packages are exported by the containing module, if any. @@ -315,17 +310,6 @@ public void visitClassDef(JCClassDecl tree) { } } - @Override - public void visitVarDef(JCVariableDecl tree) { - - // Track warning suppression of fields - if (tree.sym.owner.kind == TYP && !isWarningEnabledAt(tree)) - suppressedFields.add(tree.sym); - - // Recurse - super.visitVarDef(tree); - } - @Override public void visitMethodDef(JCMethodDecl tree) { @@ -334,16 +318,15 @@ public void visitMethodDef(JCMethodDecl tree) { boolean extendableClass = currentClassIsExternallyExtendable(); boolean nonPrivate = (tree.sym.flags() & (Flags.PUBLIC | Flags.PROTECTED)) != 0; boolean finalish = (tree.mods.flags & (Flags.STATIC | Flags.PRIVATE | Flags.FINAL)) != 0; - boolean suppressed = !isWarningEnabledAt(tree); // Determine if this is a constructor we should analyze - boolean analyzable = extendableClass && constructor && nonPrivate && !suppressed; + boolean analyzable = extendableClass && constructor && nonPrivate; // Determine if it's safe to "invoke" the method in an analysis (i.e., it can't be overridden) boolean invokable = !extendableClass || constructor || finalish; // Add this method or constructor to our map - methodMap.put(tree.sym, new MethodInfo(currentClass, tree, constructor, analyzable, invokable, suppressed)); + methodMap.put(tree.sym, new MethodInfo(currentClass, tree, constructor, analyzable, invokable)); // Recurse super.visitMethodDef(tree); @@ -360,103 +343,54 @@ private boolean currentClassIsExternallyExtendable() { } }.scan(env.tree); - // Analyze non-static field initializers and initialization blocks, - // but only for classes having at least one analyzable constructor. + // Analyze the analyzable constructors we found methodMap.values().stream() .filter(MethodInfo::analyzable) - .map(MethodInfo::declaringClass) - .distinct() - .forEach(klass -> { - klass.defs.stream() - .filter(decl -> (TreeInfo.flags(decl) & Flags.STATIC) == 0) - .forEach(decl -> { - switch (decl) { - - // Handle field initializers - case JCVariableDecl varDecl when !suppressedFields.contains(varDecl.sym) - -> visitTopLevel(env, klass, () -> { - scan(varDecl); - copyPendingWarning(); - }); - - // Handle initialization blocks - case JCBlock block -> visitTopLevel(env, klass, () -> analyzeStatements(block.stats)); + .forEach(this::analyzeConstructor); - default -> { } - } - }); - }); + // Manually apply any Lint suppressions + filterWarnings(warning -> !warning.isSuppressed()); - // Analyze all of the analyzable constructors we found - methodMap.values().stream() - .filter(MethodInfo::analyzable) - .forEach(methodInfo -> { - visitTopLevel(env, methodInfo.declaringClass(), - () -> analyzeStatements(methodInfo.declaration().body.stats)); - }); + // Field intitializers and initialization blocks will generate a separate warning for each primary constructor. + // Trim off stack frames up through the super() call so these will have identical stacks and get de-duplicated below. + warningList.forEach(Warning::trimInitializerFrames); - // Eliminate duplicate warnings. Warning B duplicates warning A if the stack trace of A is a prefix - // of the stack trace of B. For example, if constructor Foo(int x) has a leak, and constructor - // Foo() invokes this(0), then emitting a warning for Foo() would be redundant. - BiPredicate extendsAsPrefix = (warning1, warning2) -> { - if (warning2.length < warning1.length) + // Sort warnings so redundant warnings immediately follow whatever they are redundant for, then remove them + warningList.sort(Warning::sortByStackFrames); + AtomicReference previousRef = new AtomicReference<>(); + filterWarnings(warning -> { + Warning previous = previousRef.get(); + if (previous != null && warning.isRedundantWith(previous)) return false; - for (int index = 0; index < warning1.length; index++) { - if (warning2[index].getPreferredPosition() != warning1[index].getPreferredPosition()) - return false; - } + previousRef.set(warning); return true; - }; + }); - // Stack traces are ordered top to bottom, and so duplicates always have the same first element(s). - // Sort the stack traces lexicographically, so that duplicates immediately follow what they duplicate. - Comparator ordering = (warning1, warning2) -> { - for (int index1 = 0, index2 = 0; true; index1++, index2++) { - boolean end1 = index1 >= warning1.length; - boolean end2 = index2 >= warning2.length; - if (end1 && end2) - return 0; - if (end1) - return -1; - if (end2) - return 1; - int posn1 = warning1[index1].getPreferredPosition(); - int posn2 = warning2[index2].getPreferredPosition(); - int diff = Integer.compare(posn1, posn2); - if (diff != 0) - return diff; - } - }; - warningList.sort(ordering); - - // Now emit the warnings, but skipping over duplicates as we go through the list - DiagnosticPosition[] previous = null; - for (DiagnosticPosition[] warning : warningList) { - - // Skip duplicates, i.e., when the current stack extends the previous stack - if (previous != null && extendsAsPrefix.test(previous, warning)) - continue; - previous = warning; - - // Emit warnings showing the entire stack trace - JCDiagnostic.LintWarning key = LintWarnings.PossibleThisEscape; - int remain = warning.length; - do { - DiagnosticPosition pos = warning[--remain].withLintPosition(NOPOS); // skip the normal Lint suppression logic - log.warning(pos, key); + // Limit output to one warning per constructor, field initializer, or initializer block + Set thingsWarnedAbout = new HashSet<>(); + filterWarnings(warning -> thingsWarnedAbout.add(warning.origin)); + + // Emit warnings + for (Warning warning : warningList) { + LintWarning key = LintWarnings.PossibleThisEscape; + for (StackFrame frame : warning.stack) { + log.warning(frame.warningPos(), key); key = LintWarnings.PossibleThisEscapeLocation; - } while (remain > 0); + } } + + // Done warningList.clear(); } - // Analyze statements, but stop at (and record) the first warning generated - private void analyzeStatements(List stats) { - for (JCStatement stat : stats) { - scan(stat); - if (copyPendingWarning()) - break; + // Warning list editor (this is slightly more efficient than removeIf()) + private void filterWarnings(Predicate filter) { + int numRetained = 0; + for (Warning warning : warningList) { + if (filter.test(warning)) + warningList.set(numRetained++, warning); } + warningList.subList(numRetained, warningList.size()).clear(); } @Override @@ -522,10 +456,6 @@ public void visitVarDef(JCVariableDecl tree) { private void visitVarDef(VarSymbol sym, JCExpression expr) { - // Skip if ignoring warnings for this field - if (suppressedFields.contains(sym)) - return; - // Scan initializer, if any scan(expr); if (isParamOrVar(sym)) @@ -559,14 +489,42 @@ public void visitApply(JCMethodInvocation invoke) { } else refs.discardExprs(depth); - // If "super()": ignore - we don't try to track into superclasses - if (TreeInfo.name(invoke.meth) == names._super) + // If "super()": we don't invoke it (we don't track into superclasses) but we do execute any + // non-static field initializers and initialization blocks because this is when they happen. + if (TreeInfo.name(invoke.meth) == names._super) { + currentMethod.declaringClass.defs.stream() + .filter(def -> (TreeInfo.flags(def) & Flags.STATIC) == 0) + .forEach(def -> { + switch (def) { + case JCBlock block -> analyzeInitializer(invoke, block, receiverRefs, () -> visitBlock(block)); + case JCVariableDecl varDecl -> analyzeInitializer(invoke, varDecl, receiverRefs, () -> scan(varDecl)); + default -> { } + } + }); return; + } // "Invoke" the method invoke(invoke, sym, invoke.args, receiverRefs); } + // Analyze a field initializer or initialization block after encountering a super() invocation + private void analyzeInitializer(JCMethodInvocation site, JCTree initializer, RefSet receiverRefs, Runnable action) { + RefSet refsPrev = refs; + refs = RefSet.newEmpty(); + int depthPrev = depth; + depth = 0; + callStack.add(new StackFrame(currentMethod, initializer, site)); + try { + refs.addAll(receiverRefs); + action.run(); + } finally { + callStack.remove(callStack.size() - 1); + depth = depthPrev; + refs = refsPrev; + } + } + private void invoke(JCTree site, Symbol sym, List args, RefSet receiverRefs) { // Ignore final methods in java.lang.Object (getClass(), notify(), etc.) @@ -580,10 +538,6 @@ private void invoke(JCTree site, Symbol sym, List args, RefSet args, - RefSet receiverRefs, MethodInfo methodInfo) { + private void invokeInvokable(JCTree site, List args, RefSet receiverRefs, MethodInfo methodInfo) { Assert.check(methodInfo.invokable); // Collect 'this' references found in method parameters - JCMethodDecl method = methodInfo.declaration(); + JCMethodDecl method = methodInfo.declaration; RefSet paramRefs = RefSet.newEmpty(); List params = method.params; while (args.nonEmpty() && params.nonEmpty()) { @@ -643,13 +596,13 @@ private void invokeInvokable(JCTree site, List args, } // "Invoke" the method - JCClassDecl methodClassPrev = methodClass; - methodClass = methodInfo.declaringClass(); + MethodInfo currentMethodPrev = currentMethod; + currentMethod = methodInfo; RefSet refsPrev = refs; refs = RefSet.newEmpty(); int depthPrev = depth; depth = 0; - callStack.push(site); + callStack.add(new StackFrame(currentMethodPrev, null, site)); try { // Add initial references from method receiver @@ -686,10 +639,10 @@ private void invokeInvokable(JCTree site, List args, .map(ref -> new ExprRef(depthPrev, ref)) .forEach(refsPrev::add); } finally { - callStack.pop(); + callStack.remove(callStack.size() - 1); depth = depthPrev; refs = refsPrev; - methodClass = methodClassPrev; + currentMethod = currentMethodPrev; } } @@ -767,9 +720,10 @@ private RefSet receiverRefsForConstructor(JCExpression explicitOuterThi // Determine if an unqualified "new Foo()" constructor gets 'this' as an implicit outer instance private boolean hasImplicitOuterInstance(TypeSymbol tsym) { - return tsym != methodClass.sym + ClassSymbol currentClassSym = currentMethod.declaringClass.sym; + return tsym != currentClassSym && tsym.hasOuterInstance() - && tsym.isEnclosedBy(methodClass.sym); + && tsym.isEnclosedBy(currentClassSym); } // @@ -954,7 +908,7 @@ public void visitSelect(JCFieldAccess tree) { Stream methodRefs = refs.removeExprs(depth); // Explicit 'this' reference? The expression references whatever 'this' references - Type.ClassType currentClassType = (Type.ClassType)methodClass.sym.type; + Type.ClassType currentClassType = (Type.ClassType)currentMethod.declaringClass.sym.type; if (TreeInfo.isExplicitThisReference(types, currentClassType, tree)) { refs.find(ThisRef.class) .map(ref -> new ExprRef(depth, ref)) @@ -1039,7 +993,7 @@ public void visitIdent(JCIdent tree) { MethodSymbol sym = (MethodSymbol)tree.sym; // Check for implicit 'this' reference - ClassSymbol methodClassSym = methodClass.sym; + ClassSymbol methodClassSym = currentMethod.declaringClass.sym; if (methodClassSym.isSubClass(sym.owner, types)) { refs.find(ThisRef.class) .map(ref -> new ExprRef(depth, ref)) @@ -1223,53 +1177,50 @@ public void visitBinary(JCBinary tree) { // Helper methods - private void visitTopLevel(Env env, JCClassDecl klass, Runnable action) { - Assert.check(attrEnv == null); + private void analyzeConstructor(MethodInfo constructor) { Assert.check(targetClass == null); - Assert.check(methodClass == null); + Assert.check(currentMethod == null); Assert.check(depth == -1); Assert.check(refs == null); - attrEnv = env; - targetClass = klass; - methodClass = klass; + targetClass = constructor.declaringClass; + currentMethod = constructor; try { // Add the initial 'this' reference refs = RefSet.newEmpty(); refs.add(new ThisRef(targetClass.sym, EnumSet.of(Indirection.DIRECT))); - // Perform action - visitScoped(false, action); + // Analyze constructor + visitScoped(false, () -> scan(constructor.declaration.body)); } finally { Assert.check(depth == -1); - attrEnv = null; - methodClass = null; + currentMethod = null; targetClass = null; refs = null; } } // Recurse through indirect code that might get executed later, e.g., a lambda. - // We stash any pending warning and the current RefSet, then recurse into the deferred - // code (still using the current RefSet) to see if it would leak. Then we restore the - // pending warning and the current RefSet. Finally, if the deferred code would have - // leaked, we create an indirect ExprRef because it must be holding a 'this' reference. - // If the deferred code would not leak, then obviously no leak is possible, period. + // We record the current number of (real) warnings, then recurse into the deferred + // code (still using the current RefSet) to see if that number increases, i.e., to + // see if it would leak. Then we discard any new warnings and the current RefSet. + // Finally, if the deferred code would have leaked, we create an indirect ExprRef + // because it must be holding a 'this' reference. If the deferred code would not leak, + // then obviously no leak is possible, period. private void visitDeferred(Runnable deferredCode) { - DiagnosticPosition[] pendingWarningPrev = pendingWarning; - pendingWarning = null; + int numWarningsPrev = warningList.size(); RefSet refsPrev = refs.clone(); boolean deferredCodeLeaks; try { deferredCode.run(); - deferredCodeLeaks = pendingWarning != null; + deferredCodeLeaks = warningList.size() > numWarningsPrev; // There can be ExprRef's if the deferred code returns something. // Don't let them escape unnoticed. deferredCodeLeaks |= refs.discardExprs(depth); } finally { refs = refsPrev; - pendingWarning = pendingWarningPrev; + warningList.subList(numWarningsPrev, warningList.size()).clear(); } if (deferredCodeLeaks) refs.add(new ExprRef(depth, syms.objectType.tsym, EnumSet.of(Indirection.INDIRECT))); @@ -1321,24 +1272,9 @@ private void popScope() { // Note a possible 'this' reference leak at the specified location private void leakAt(JCTree tree) { - - // Generate at most one warning per statement - if (pendingWarning != null) - return; - - // Snapshot the current stack trace - callStack.push(tree.pos()); - pendingWarning = callStack.toArray(new DiagnosticPosition[0]); - callStack.pop(); - } - - // Copy pending warning, if any, to the warning list and reset - private boolean copyPendingWarning() { - if (pendingWarning == null) - return false; - warningList.add(pendingWarning); - pendingWarning = null; - return true; + callStack.add(new StackFrame(currentMethod, null, tree)); // include the point of leakage in the stack + warningList.add(new Warning(targetClass, new ArrayList<>(callStack))); + callStack.remove(callStack.size() - 1); } // Does the symbol correspond to a parameter or local variable (not a field)? @@ -1372,18 +1308,13 @@ private boolean isAnalyzing() { return targetClass != null; } - // Determine if the "this-escape" warning is enabled at the given tree's source code position - private boolean isWarningEnabledAt(JCTree tree) { - return lintMapper.lintAt(log.currentSourceFile(), tree.pos()).get().isEnabled(LintCategory.THIS_ESCAPE); - }; - // Debugging // Invariant checks private boolean checkInvariants(boolean analyzing, boolean allowExpr) { Assert.check(analyzing == isAnalyzing()); if (isAnalyzing()) { - Assert.check(methodClass != null); + Assert.check(currentMethod != null); Assert.check(targetClass != null); Assert.check(refs != null); Assert.check(depth >= 0); @@ -1394,7 +1325,6 @@ private boolean checkInvariants(boolean analyzing, boolean allowExpr) { Assert.check(refs == null); Assert.check(depth == -1); Assert.check(callStack.isEmpty()); - Assert.check(pendingWarning == null); Assert.check(invocations.isEmpty()); } return true; @@ -1773,6 +1703,134 @@ public RefSet clone() { } } +// StackFrame + + // Information about one frame on the call stack + private class StackFrame { + + final MethodInfo method; // the method containing the statement + final JCTree site; // the call site within the method + final JCTree initializer; // originating field or init block, else null + final boolean suppressible; // whether warning can be suppressed at this frame + + StackFrame(MethodInfo method, JCTree initializer, JCTree site) { + this.method = method; + this.initializer = initializer; + this.suppressible = initializer != null || (method.constructor && method.declaringClass == targetClass); + this.site = site; + } + + int pos() { + return site.pos().getPreferredPosition(); + } + + DiagnosticPosition warningPos() { + return site.pos().withLintPosition(NOPOS); // disable normal Lint suppression + } + + Lint lint() { + return lintMapper.lintAt(attrEnv.toplevel.sourcefile, site.pos()).get(); + } + + @Override + public String toString() { + return "StackFrame" + + "[" + method.declaration.sym + "@" + pos() + + (initializer != null ? ",init@" + initializer.pos().getPreferredPosition() : "") + + (suppressible ? ",suppressible" : "") + + "]"; + } + } + +// Warning + + // Information about one warning we have generated + private class Warning { + + final JCClassDecl declaringClass; // the class whose instance is leaked + final ArrayList stack; // the call stack where the leak happens + final JCTree origin; // the originating ctor, field, or init block + + Warning(JCClassDecl declaringClass, ArrayList stack) { + this.declaringClass = declaringClass; + this.stack = stack; + this.origin = stack.stream() + .map(frame -> frame.initializer) + .filter(Objects::nonNull) + .findFirst() + .orElseGet(() -> initialConstructor().declaration); + } + + // Get the initial constructor that generated this warning, which is found at the bottom of the call stack + MethodInfo initialConstructor() { + return stack.get(0).method; + } + + // Used to eliminate redundant warnings. Warning A is redundant with warning B if the call stack of A includes + // the call stack of B plus additional outer frame(s). For example, if constructor B = Foo(int x) generates a + // warning, then generating another warning for some constructor A where it invokes this(123) would be redundant. + boolean isRedundantWith(Warning that) { + int numExtra = this.stack.size() - that.stack.size(); + return numExtra >= 0 && + IntStream.range(0, that.stack.size()) + .allMatch(i -> this.stack.get(numExtra + i).pos() == that.stack.get(i).pos()); + }; + + // Order warnings by stack frame lexicographically from top to bottom, which will cause all + // warnings that are isRedundantWith() some other warning to immediately follow that warning. + static int sortByStackFrames(Warning warning1, Warning warning2) { + int index1 = warning1.stack.size(); + int index2 = warning2.stack.size(); + while (true) { + boolean end1 = --index1 < 0; + boolean end2 = --index2 < 0; + if (end1 && end2) + return 0; + if (end1) + return -1; + if (end2) + return 1; + int diff = Integer.compare(warning1.stack.get(index1).pos(), warning2.stack.get(index2).pos()); + if (diff != 0) + return diff; + } + }; + + // Determine whether this warning is suppressed. A single "this-escape" warning involves multiple source code + // positions, so we must determine suppression manually. We do this as follows: A warning is suppressed if + // "this-escape" is disabled at any position in the stack where that stack frame corresponds to a constructor + // or field initializer in the target class. That means, for example, @SuppressWarnings("this-escape") annotations + // on regular methods are ignored. We work our way back up the call stack from the point of the leak until we + // encounter a suppressible stack frame. + boolean isSuppressed() { + int index = stack.size(); + while (--index >= 0) { + StackFrame frame = stack.get(index); + if (frame.suppressible && !frame.lint().isEnabled(THIS_ESCAPE)) + return true; + } + return false; + } + + // If this is a field or initializer warning, trim the initial stack frame(s) up through the super() call + void trimInitializerFrames() { + for (int i = 0; i < stack.size(); i++) { + if (stack.get(i).initializer != null) { + stack.subList(0, i + 1).clear(); + break; + } + } + } + + @Override + public String toString() { + return "Warning" + + "[class=" + declaringClass.sym.flatname + + ",stack=[\n " + stack.stream().map(StackFrame::toString).collect(Collectors.joining("\n ")) + "]" + + "]"; + } + } + // MethodInfo // Information about a constructor or method in the compilation unit @@ -1781,17 +1839,15 @@ private record MethodInfo( JCMethodDecl declaration, // the method or constructor itself boolean constructor, // the method is a constructor boolean analyzable, // it's a constructor that we should analyze - boolean invokable, // it may be safely "invoked" during analysis - boolean suppressed) { // the "this-escape" warning is not enabled + boolean invokable) { // it may be safely "invoked" during analysis @Override public String toString() { return "MethodInfo" - + "[method=" + declaringClass.sym.flatname + "." + declaration.sym + + "[method=" + declaration.sym + ",constructor=" + constructor + ",analyzable=" + analyzable + ",invokable=" + invokable - + ",suppressed=" + suppressed + "]"; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 26bcfaa01f6ed..81dc219bf50ef 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -1926,6 +1926,7 @@ public void close() { attr = null; chk = null; gen = null; + lintMapper = null; flow = null; transTypes = null; lower = null; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java index 4c6439dd1b869..cb3377f396e24 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/LintMapper.java @@ -32,7 +32,9 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; import java.util.stream.Stream; import javax.tools.DiagnosticListener; @@ -72,10 +74,7 @@ public class LintMapper { // Compiler context private final Context context; - // This calculates the Lint instances that apply to various source code ranges - private final LintSpanCalculator lintSpanCalculator = new LintSpanCalculator(); - - // The root Lint instance, calculated on-demand to avoid init loops + // These are initialized lazily; see initializeIfNeeded() private Lint rootLint; /** @@ -97,12 +96,14 @@ protected LintMapper(Context context) { this.context = context; } - private Lint rootLint() { + // Lazy initialization to avoid dependency loops + private void initializeIfNeeded() { if (rootLint == null) rootLint = Lint.instance(context); - return rootLint; } +// Lint Operations + /** * Determine if the given file is known to this instance. * @@ -121,30 +122,10 @@ public boolean isKnown(JavaFileObject sourceFile) { * @return the applicable {@link Lint}, if known */ public Optional lintAt(JavaFileObject sourceFile, DiagnosticPosition pos) { - - // If the file is completely unknown, we don't know - FileInfo fileInfo = fileInfoMap.get(sourceFile); - if (fileInfo == null) - return Optional.empty(); - - // If the file hasn't been fully parsed yet, we don't know what Lint applies yet - if (!fileInfo.parsed) - return Optional.empty(); - - // Find the top-level declaration that contains pos; if there is none, then the root lint applies - Span declSpan = fileInfo.findDeclSpan(pos); - if (declSpan == null) - return Optional.of(rootLint()); - - // Have we attributed this top-level declaration? If not, we don't know what Lint applies yet - List lintSpans = fileInfo.lintSpanMap.get(declSpan); - if (lintSpans == null) - return Optional.empty(); - - // Find the narrowest containing LintSpan; if there is none, then the root lint applies - return FileInfo.bestMatch(lintSpans, pos) - .map(lintSpan -> lintSpan.lint) - .or(() -> Optional.of(rootLint())); + initializeIfNeeded(); + return Optional.of(sourceFile) + .map(fileInfoMap::get) + .flatMap(fileInfo -> fileInfo.lintAt(pos)); } /** @@ -154,32 +135,24 @@ public Optional lintAt(JavaFileObject sourceFile, DiagnosticPosition pos) * @param tree top-level declaration (class, package, or module) */ public void calculateLints(JavaFileObject sourceFile, JCTree tree, EndPosTable endPositions) { - - // Get the info for this file - FileInfo fileInfo = fileInfoMap.get(sourceFile); - - // Sanity checks - Assert.check(isTopLevelDecl(tree)); - Assert.check(fileInfo != null && fileInfo.parsed); - Span declSpan = new Span(tree, endPositions); - Assert.check(fileInfo.lintSpanMap.containsKey(declSpan), "unknown declaration"); - Assert.check(fileInfo.lintSpanMap.get(declSpan) == null, "duplicate calculateLints()"); - - // Build the list of lints for declarations within the top-level declaration - fileInfo.lintSpanMap.put(declSpan, lintSpanCalculator.calculate(endPositions, tree)); + initializeIfNeeded(); + fileInfoMap.get(sourceFile).afterAttr(tree, endPositions); } /** - * Reset this instance (except for listeners). + * Reset this instance. */ public void clear() { fileInfoMap.clear(); } +// Parsing Notifications + /** * Invoked when file parsing starts to create an entry for the new file. */ public void startParsingFile(JavaFileObject sourceFile) { + initializeIfNeeded(); fileInfoMap.put(sourceFile, new FileInfo()); } @@ -187,68 +160,84 @@ public void startParsingFile(JavaFileObject sourceFile) { * Invoked when file parsing completes to identify the top-level declarations. */ public void finishParsingFile(JCCompilationUnit tree) { - - // Get info for this file - FileInfo fileInfo = fileInfoMap.get(tree.sourcefile); - Assert.check(fileInfo != null, () -> "unknown source " + tree.sourcefile); - Assert.check(!fileInfo.parsed, () -> "source already parsed: " + tree.sourcefile); - Assert.check(fileInfo.lintSpanMap.isEmpty(), () -> "duplicate invocation for " + tree.sourcefile); - - // Mark file as parsed - fileInfo.parsed = true; - - // Create an entry in lintSpanMap for each top-level declaration, with a null value for now - tree.defs.stream() - .filter(this::isTopLevelDecl) - .map(decl -> new Span(decl, tree.endPositions)) - .forEach(span -> fileInfo.lintSpanMap.put(span, null)); - } - - private boolean isTopLevelDecl(JCTree tree) { - return tree.getTag() == Tag.MODULEDEF - || tree.getTag() == Tag.PACKAGEDEF - || tree.getTag() == Tag.CLASSDEF; + fileInfoMap.get(tree.sourcefile).afterParse(tree); } // FileInfo /** - * Holds the calculated {@link Lint}s for top-level declarations in some source file. + * Holds {@link Lint} related information for one source file. * *