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  * @library /tools/javac/lib
 29  * @modules jdk.compiler/com.sun.tools.javac.api
 30  *          jdk.compiler/com.sun.tools.javac.file
 31  *          jdk.compiler/com.sun.tools.javac.util
 32  * @build combo.ComboTestHelper
 33  * @run main LocalStaticDeclarations
 34  */
 35 
 36 import javax.lang.model.element.Element;
 37 import javax.tools.Diagnostic;
 38 import javax.tools.JavaFileObject;
 39 
 40 import com.sun.tools.javac.util.Assert;
 41 
 42 import com.sun.tools.javac.api.ClientCodeWrapper;
 43 import com.sun.tools.javac.util.JCDiagnostic;
 44 import com.sun.tools.javac.util.List;
 45 import combo.ComboInstance;
 46 import combo.ComboParameter;
 47 import combo.ComboTask;
 48 import combo.ComboTask.Result;
 49 import combo.ComboTestHelper;
 50 
 51 /** this test checks two thinks:
 52  *  1 - that static declarations are allowed inside inner classes
 53  *  2 - and in addtion that non-static variables can't be captured
 54  *      by static contexts
 55  */
 56 
 57 public class LocalStaticDeclarations extends ComboInstance<LocalStaticDeclarations> {
 58 
 59     static final String sourceTemplate =
 60             """
 61             import java.lang.annotation.*;
 62             class Test {
 63                 int INSTANCE_FIELD = 0;
 64                 static int STATIC_FIELD = 0;
 65                 // instance initializer
 66                 {
 67                     int LOCAL_VARIABLE = 0;
 68                     #{CONTAINER}
 69                 }
 70                 Test() {
 71                     int LOCAL_VARIABLE = 0;
 72                     #{CONTAINER}
 73                 }
 74                 void m() {
 75                     int LOCAL_VARIABLE = 0;
 76                     #{CONTAINER}
 77                 }
 78                 static void foo() {
 79                     int LOCAL_VARIABLE = 0;
 80                     #{CONTAINER}
 81                 }
 82             }
 83             """;
 84 
 85     enum Container implements ComboParameter {
 86         NO_CONTAINER("#{STATIC_LOCAL}"),
 87         INTERFACE("interface CI { #{STATIC_LOCAL} }"),
 88         ANONYMOUS(
 89                 """
 90                     new Object() {
 91                         // instance initializer
 92                         {
 93                             #{STATIC_LOCAL}
 94                         }
 95 
 96                         void m() {
 97                             #{STATIC_LOCAL}
 98                         }
 99                     };
100                 """
101         ),
102         RECORD("record CR() { #{STATIC_LOCAL} }"),
103         CLASS("class CC { #{STATIC_LOCAL} }"),
104         ENUM("enum CE { CE1; #{STATIC_LOCAL} }"),
105         LAMBDA("Runnable run = () -> { #{STATIC_LOCAL} };");
106 
107         String container;
108 
109         Container(String container) {
110             this.container = container;
111         }
112 
113         public String expand(String optParameter) {
114             return container;
115         }
116     }
117 
118     enum StaticLocalDecl implements ComboParameter {
119         ENUM("enum E { E1; #{MEMBER} }"),
120         RECORD("record R() { #{MEMBER} }"),
121         INTERFACE("interface I { #{MEMBER} }");
122 
123         String localDecl;
124 
125         StaticLocalDecl(String localDecl) {
126             this.localDecl = localDecl;
127         }
128 
129         public String expand(String optParameter) {
130             return localDecl;
131         }
132     }
133 
134     enum Member implements ComboParameter {
135         METHOD("int foo() { return #{EXPR}; }"),
136         DEFAULT_METHOD("default int foo() { return #{EXPR}; }");
137 
138         String member;
139 
140         Member(String member) {
141             this.member = member;
142         }
143 
144         public String expand(String optParameter) {
145             return member;
146         }
147     }
148 
149     enum Expression implements ComboParameter {
150          LITERAL("1"),
151          STATIC_FIELD("STATIC_FIELD"),
152          LOCAL_VARIABLE("LOCAL_VARIABLE"),
153          INSTANCE_FIELD("INSTANCE_FIELD");
154 
155          String expr;
156 
157          Expression(String expr) {
158              this.expr = expr;
159          }
160 
161         public String expand(String optParameter) {
162             return expr;
163         }
164     }
165 
166     public static void main(String... args) throws Exception {
167         new combo.ComboTestHelper<LocalStaticDeclarations>()
168                 .withFilter(LocalStaticDeclarations::notTriviallyIncorrect)
169                 .withDimension("CONTAINER", (x, t) -> { x.container = t; }, Container.values())
170                 .withDimension("STATIC_LOCAL", (x, t) -> { x.decl = t; }, StaticLocalDecl.values())
171                 .withDimension("MEMBER", (x, t) -> { x.member = t; }, Member.values())
172                 .withDimension("EXPR", (x, expr) -> x.expr = expr, Expression.values())
173                 .run(LocalStaticDeclarations::new);
174     }
175 
176     Container container;
177     StaticLocalDecl decl;
178     Member member;
179     Expression expr;
180 
181     @Override
182     public void doWork() throws Throwable {
183         newCompilationTask()
184                 .withSourceFromTemplate("Test", sourceTemplate)
185                 .generate(this::check);
186     }
187 
188     boolean notTriviallyIncorrect() {
189         return decl == StaticLocalDecl.INTERFACE && member == Member.DEFAULT_METHOD ||
190                decl != StaticLocalDecl.INTERFACE && member == Member.METHOD;
191     }
192 
193     void check(ComboTask.Result<Iterable<? extends JavaFileObject>> result) {
194         if (shouldFail()) {
195             Assert.check(result.hasErrors(), "unexpected compilation\n" + result.compilationInfo());
196             if (!expectedDiagFound(result)) {
197                 fail("test failing with unexpected error message\n" + result.compilationInfo());
198             }
199         } else {
200             Assert.check(!result.hasErrors(), result.compilationInfo());
201         }
202     }
203 
204     boolean shouldFail() {
205         return (expr == Expression.LOCAL_VARIABLE || expr == Expression.INSTANCE_FIELD);
206     }
207 
208     boolean acceptableExpr() {
209         return (expr == Expression.LITERAL || expr == Expression.STATIC_FIELD);
210     }
211 
212     boolean expectedDiagFound(ComboTask.Result<Iterable<? extends JavaFileObject>> result) {
213         if (expr == Expression.LOCAL_VARIABLE || expr == Expression.INSTANCE_FIELD) {
214             return result.containsKey("compiler.err.non-static.cant.be.ref");
215         }
216         return false;
217     }
218 }