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 
Container(String container)109         Container(String container) {
110             this.container = container;
111         }
112 
expand(String optParameter)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 
StaticLocalDecl(String localDecl)125         StaticLocalDecl(String localDecl) {
126             this.localDecl = localDecl;
127         }
128 
expand(String optParameter)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 
Member(String member)140         Member(String member) {
141             this.member = member;
142         }
143 
expand(String optParameter)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 
Expression(String expr)157          Expression(String expr) {
158              this.expr = expr;
159          }
160 
expand(String optParameter)161         public String expand(String optParameter) {
162             return expr;
163         }
164     }
165 
main(String... args)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
doWork()182     public void doWork() throws Throwable {
183         newCompilationTask()
184                 .withSourceFromTemplate("Test", sourceTemplate)
185                 .generate(this::check);
186     }
187 
notTriviallyIncorrect()188     boolean notTriviallyIncorrect() {
189         return decl == StaticLocalDecl.INTERFACE && member == Member.DEFAULT_METHOD ||
190                decl != StaticLocalDecl.INTERFACE && member == Member.METHOD;
191     }
192 
check(ComboTask.Result<Iterable<? extends JavaFileObject>> result)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 
shouldFail()204     boolean shouldFail() {
205         return (expr == Expression.LOCAL_VARIABLE || expr == Expression.INSTANCE_FIELD);
206     }
207 
acceptableExpr()208     boolean acceptableExpr() {
209         return (expr == Expression.LITERAL || expr == Expression.STATIC_FIELD);
210     }
211 
expectedDiagFound(ComboTask.Result<Iterable<? extends JavaFileObject>> result)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 }
219