11/*
2- * Copyright (c) 2011, 2024 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2011, 2025 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
2525
2626package com .sun .tools .javac .code ;
2727
28+ import java .util .ArrayDeque ;
29+ import java .util .ArrayList ;
2830import java .util .HashMap ;
29- import java .util .Map ;
31+ import java .util .Optional ;
32+ import java .util .function .Consumer ;
3033
31- import com .sun .tools .javac .tree .EndPosTable ;
3234import com .sun .tools .javac .tree .JCTree ;
35+ import com .sun .tools .javac .tree .JCTree .Tag ;
3336import com .sun .tools .javac .util .Assert ;
3437import com .sun .tools .javac .util .Context ;
35- import com .sun .tools .javac .util .JCDiagnostic .DiagnosticPosition ;
36- import com .sun .tools .javac .util .ListBuffer ;
3738
3839/**
40+ * Holds pending {@link Lint} warnings until the {@lint Lint} instance associated with the containing
41+ * module, package, class, method, or variable declaration is known so that {@link @SupressWarnings}
42+ * suppressions may be applied.
43+ *
44+ * <p>
45+ * Warnings are regsistered at any time prior to attribution via {@link #report}. The warning will be
46+ * associated with the declaration placed in context by the most recent invocation of {@link #push push()}
47+ * not yet {@link #pop}'d. Warnings are actually emitted later, during attribution, via {@link #flush}.
48+ *
49+ * <p>
50+ * There is also an "immediate" mode, where warnings are emitted synchronously; see {@link #pushImmediate}.
51+ *
52+ * <p>
53+ * Deferred warnings are grouped by the innermost containing module, package, class, method, or variable
54+ * declaration (represented by {@link JCTree} nodes), so that the corresponding {@link Lint} configuration
55+ * can be applied when the warning is eventually generated.
3956 *
4057 * <p><b>This is NOT part of any supported API.
4158 * If you write code that depends on this, you do so at your own risk.
4259 * This code and its internal interfaces are subject to change or
4360 * deletion without notice.</b>
4461 */
4562public class DeferredLintHandler {
63+
4664 protected static final Context .Key <DeferredLintHandler > deferredLintHandlerKey = new Context .Key <>();
4765
4866 public static DeferredLintHandler instance (Context context ) {
@@ -52,99 +70,107 @@ public static DeferredLintHandler instance(Context context) {
5270 return instance ;
5371 }
5472
55- /** The Lint to use when {@link #immediate(Lint)} is used,
56- * instead of {@link #setPos(DiagnosticPosition)}. */
57- private Lint immediateLint ;
73+ /**
74+ * Registered {@link LintLogger}s grouped by the innermost containing module, package, class,
75+ * method, or variable declaration.
76+ */
77+ private final HashMap <JCTree , ArrayList <LintLogger >> deferralMap = new HashMap <>();
78+
79+ /**
80+ * The current "reporter" stack, reflecting calls to {@link #push} and {@link #pop}.
81+ *
82+ * <p>
83+ * The top of the stack determines how calls to {@link #report} are handled.
84+ */
85+ private final ArrayDeque <Consumer <LintLogger >> reporterStack = new ArrayDeque <>();
5886
5987 @ SuppressWarnings ("this-escape" )
6088 protected DeferredLintHandler (Context context ) {
6189 context .put (deferredLintHandlerKey , this );
62- this . currentPos = IMMEDIATE_POSITION ;
63- immediateLint = Lint . instance ( context );
90+ Lint rootLint = Lint . instance ( context ) ;
91+ pushImmediate ( rootLint ); // default to "immediate" mode
6492 }
6593
94+ // LintLogger
95+
6696 /**An interface for deferred lint reporting - loggers passed to
6797 * {@link #report(LintLogger) } will be called when
6898 * {@link #flush(DiagnosticPosition) } is invoked.
6999 */
70100 public interface LintLogger {
101+
102+ /**
103+ * Generate a warning if appropriate.
104+ *
105+ * @param lint the applicable lint configuration
106+ */
71107 void report (Lint lint );
72108 }
73109
74- private DiagnosticPosition currentPos ;
75- private Map <DiagnosticPosition , ListBuffer <LintLogger >> loggersQueue = new HashMap <>();
110+ // Reporter Stack
76111
77- /**Associate the given logger with the current position as set by {@link #setPos(DiagnosticPosition) }.
78- * Will be invoked when {@link #flush(DiagnosticPosition) } will be invoked with the same position .
79- * <br>
80- * Will invoke the logger synchronously if {@link #immediate() } was called
81- * instead of {@link #setPos(DiagnosticPosition) }.
112+ /**
113+ * Defer {@link #report}ed warnings until the given declaration is flushed .
114+ *
115+ * @param decl module, package, class, method, or variable declaration
116+ * @see #pop
82117 */
83- public void report (LintLogger logger ) {
84- if (currentPos == IMMEDIATE_POSITION ) {
85- logger .report (immediateLint );
86- } else {
87- ListBuffer <LintLogger > loggers = loggersQueue .get (currentPos );
88- if (loggers == null ) {
89- loggersQueue .put (currentPos , loggers = new ListBuffer <>());
90- }
91- loggers .append (logger );
92- }
118+ public void push (JCTree decl ) {
119+ Assert .check (decl .getTag () == Tag .MODULEDEF
120+ || decl .getTag () == Tag .PACKAGEDEF
121+ || decl .getTag () == Tag .CLASSDEF
122+ || decl .getTag () == Tag .METHODDEF
123+ || decl .getTag () == Tag .VARDEF );
124+ reporterStack .push (logger -> deferralMap
125+ .computeIfAbsent (decl , s -> new ArrayList <>())
126+ .add (logger ));
93127 }
94128
95- /**Invoke all {@link LintLogger}s that were associated with the provided {@code pos}.
129+ /**
130+ * Enter "immediate" mode so that {@link #report}ed warnings are emitted synchonously.
131+ *
132+ * @param lint lint configuration to use for reported warnings
96133 */
97- public void flush (DiagnosticPosition pos , Lint lint ) {
98- ListBuffer <LintLogger > loggers = loggersQueue .get (pos );
99- if (loggers != null ) {
100- for (LintLogger lintLogger : loggers ) {
101- lintLogger .report (lint );
102- }
103- loggersQueue .remove (pos );
104- }
134+ public void pushImmediate (Lint lint ) {
135+ reporterStack .push (logger -> logger .report (lint ));
105136 }
106137
107- /**Sets the current position to the provided {@code currentPos}. {@link LintLogger}s
108- * passed to subsequent invocations of {@link #report(LintLogger) } will be associated
109- * with the given position.
138+ /**
139+ * Revert to the previous configuration in effect prior to the most recent invocation
140+ * of {@link #push} or {@link #pushImmediate}.
141+ *
142+ * @see #pop
110143 */
111- public DiagnosticPosition setPos (DiagnosticPosition currentPos ) {
112- DiagnosticPosition prevPosition = this .currentPos ;
113- this .currentPos = currentPos ;
114- return prevPosition ;
144+ public void pop () {
145+ Assert .check (reporterStack .size () > 1 ); // the bottom stack entry should never be popped
146+ reporterStack .pop ();
115147 }
116148
117- /**{@link LintLogger}s passed to subsequent invocations of
118- * {@link #report(LintLogger) } will be invoked immediately.
149+ /**
150+ * Report a warning.
151+ *
152+ * <p>
153+ * In immediate mode, the warning is emitted synchronously. Otherwise, the warning is emitted later
154+ * when the current declaration is flushed.
119155 */
120- public DiagnosticPosition immediate ( Lint lint ) {
121- immediateLint = lint ;
122- return setPos ( IMMEDIATE_POSITION );
156+ public void report ( LintLogger logger ) {
157+ Assert . check (! reporterStack . isEmpty ()) ;
158+ reporterStack . peek (). accept ( logger );
123159 }
124160
125- private static final DiagnosticPosition IMMEDIATE_POSITION = new DiagnosticPosition () {
126- @ Override
127- public JCTree getTree () {
128- Assert .error ();
129- return null ;
130- }
131-
132- @ Override
133- public int getStartPosition () {
134- Assert .error ();
135- return -1 ;
136- }
137-
138- @ Override
139- public int getPreferredPosition () {
140- Assert .error ();
141- return -1 ;
142- }
143-
144- @ Override
145- public int getEndPosition (EndPosTable endPosTable ) {
146- Assert .error ();
147- return -1 ;
148- }
149- };
161+ // Warning Flush
162+
163+ /**
164+ * Emit deferred warnings encompassed by the given declaration.
165+ *
166+ * @param decl module, package, class, method, or variable declaration
167+ * @param lint lint configuration corresponding to {@code decl}
168+ */
169+ public void flush (JCTree decl , Lint lint ) {
170+ Optional .of (decl )
171+ .map (deferralMap ::remove )
172+ .stream ()
173+ .flatMap (ArrayList ::stream )
174+ .forEach (logger -> logger .report (lint ));
175+ }
150176}
0 commit comments