1 /*
  2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @bug 8242293 8246774
 27  * @summary allow for local interfaces and enums plus nested records, interfaces and enums
 28  * @enablePreview
 29  * @library /tools/javac/lib
 30  * @modules jdk.compiler/com.sun.tools.javac.api
 31  *          jdk.compiler/com.sun.tools.javac.file
 32  *          jdk.compiler/com.sun.tools.javac.util
 33  * @build combo.ComboTestHelper
 34  * @run main LocalStaticDeclarations
 35  */
 36 
 37 import javax.lang.model.element.Element;
 38 import javax.tools.Diagnostic;
 39 import javax.tools.JavaFileObject;
 40 
 41 import com.sun.tools.javac.util.Assert;
 42 
 43 import com.sun.tools.javac.api.ClientCodeWrapper;
 44 import com.sun.tools.javac.util.JCDiagnostic;
 45 import com.sun.tools.javac.util.List;
 46 import combo.ComboInstance;
 47 import combo.ComboParameter;
 48 import combo.ComboTask;
 49 import combo.ComboTask.Result;
 50 import combo.ComboTestHelper;
 51 
 52 /** this test checks two thinks:
 53  *  1 - that static declarations are allowed inside inner classes
 54  *  2 - and in addtion that non-static variables can't be captured
 55  *      by static contexts
 56  */
 57 
 58 public class LocalStaticDeclarations extends ComboInstance<LocalStaticDeclarations> {
 59 
 60     static final String sourceTemplate =
 61             """
 62             import java.lang.annotation.*;
 63             class Test {
 64                 int INSTANCE_FIELD = 0;
 65                 static int STATIC_FIELD = 0;
 66                 // instance initializer
 67                 {
 68                     int LOCAL_VARIABLE = 0;
 69                     #{CONTAINER}
 70                 }
 71                 Test() {
 72                     int LOCAL_VARIABLE = 0;
 73                     #{CONTAINER}
 74                 }
 75                 void m() {
 76                     int LOCAL_VARIABLE = 0;
 77                     #{CONTAINER}
 78                 }
 79                 static void foo() {
 80                     int LOCAL_VARIABLE = 0;
 81                     #{CONTAINER}
 82                 }
 83             }
 84             """;
 85 
 86     enum Container implements ComboParameter {
 87         NO_CONTAINER("#{STATIC_LOCAL}"),
 88         INTERFACE("interface CI { #{STATIC_LOCAL} }"),
 89         ANONYMOUS(
 90                 """
 91                     new Object() {
 92                         // instance initializer
 93                         {
 94                             #{STATIC_LOCAL}
 95                         }
 96 
 97                         void m() {
 98                             #{STATIC_LOCAL}
 99                         }
100                     };
101                 """
102         ),
103         RECORD("record CR() { #{STATIC_LOCAL} }"),
104         CLASS("class CC { #{STATIC_LOCAL} }"),
105         ENUM("enum CE { CE1; #{STATIC_LOCAL} }"),
106         LAMBDA("Runnable run = () -> { #{STATIC_LOCAL} };");
107 
108         String container;
109 
110         Container(String container) {
111             this.container = container;
112         }
113 
114         public String expand(String optParameter) {
115             return container;
116         }
117     }
118 
119     enum StaticLocalDecl implements ComboParameter {
120         ENUM("enum E { E1; #{MEMBER} }"),
121         RECORD("record R() { #{MEMBER} }"),
122         INTERFACE("interface I { #{MEMBER} }");
123 
124         String localDecl;
125 
126         StaticLocalDecl(String localDecl) {
127             this.localDecl = localDecl;
128         }
129 
130         public String expand(String optParameter) {
131             return localDecl;
132         }
133     }
134 
135     enum Member implements ComboParameter {
136         METHOD("int foo() { return #{EXPR}; }"),
137         DEFAULT_METHOD("default int foo() { return #{EXPR}; }");
138 
139         String member;
140 
141         Member(String member) {
142             this.member = member;
143         }
144 
145         public String expand(String optParameter) {
146             return member;
147         }
148     }
149 
150     enum Expression implements ComboParameter {
151          LITERAL("1"),
152          STATIC_FIELD("STATIC_FIELD"),
153          LOCAL_VARIABLE("LOCAL_VARIABLE"),
154          INSTANCE_FIELD("INSTANCE_FIELD");
155 
156          String expr;
157 
158          Expression(String expr) {
159              this.expr = expr;
160          }
161 
162         public String expand(String optParameter) {
163             return expr;
164         }
165     }
166 
167     public static void main(String... args) throws Exception {
168         new combo.ComboTestHelper<LocalStaticDeclarations>()
169                 .withFilter(LocalStaticDeclarations::notTriviallyIncorrect)
170                 .withDimension("CONTAINER", (x, t) -> { x.container = t; }, Container.values())
171                 .withDimension("STATIC_LOCAL", (x, t) -> { x.decl = t; }, StaticLocalDecl.values())
172                 .withDimension("MEMBER", (x, t) -> { x.member = t; }, Member.values())
173                 .withDimension("EXPR", (x, expr) -> x.expr = expr, Expression.values())
174                 .run(LocalStaticDeclarations::new);
175     }
176 
177     Container container;
178     StaticLocalDecl decl;
179     Member member;
180     Expression expr;
181 
182     @Override
183     public void doWork() throws Throwable {
184         newCompilationTask()
185                 .withSourceFromTemplate("Test", sourceTemplate)
186                 .generate(this::check);
187     }
188 
189     boolean notTriviallyIncorrect() {
190         return decl == StaticLocalDecl.INTERFACE && member == Member.DEFAULT_METHOD ||
191                decl != StaticLocalDecl.INTERFACE && member == Member.METHOD;
192     }
193 
194     void check(ComboTask.Result<Iterable<? extends JavaFileObject>> result) {
195         if (shouldFail()) {
196             Assert.check(result.hasErrors(), "unexpected compilation\n" + result.compilationInfo());
197             if (!expectedDiagFound(result)) {
198                 fail("test failing with unexpected error message\n" + result.compilationInfo());
199             }
200         } else {
201             Assert.check(!result.hasErrors(), result.compilationInfo());
202         }
203     }
204 
205     boolean shouldFail() {
206         return (expr == Expression.LOCAL_VARIABLE || expr == Expression.INSTANCE_FIELD);
207     }
208 
209     boolean acceptableExpr() {
210         return (expr == Expression.LITERAL || expr == Expression.STATIC_FIELD);
211     }
212 
213     boolean expectedDiagFound(ComboTask.Result<Iterable<? extends JavaFileObject>> result) {
214         if (expr == Expression.LOCAL_VARIABLE || expr == Expression.INSTANCE_FIELD) {
215             return result.containsKey("compiler.err.non-static.cant.be.ref");
216         }
217         return false;
218     }
219 }