# HG changeset patch # User jfranck # Date 1414132491 -7200 # Node ID c1ca668b421e32bc13f672f27c920259469c3d6b # Parent 361efbfa3079fa71ea970ae8f56624976128c84e 8054448: (ann) Cannot reference field of inner class in an anonymous class Reviewed-by: jlahoda, mcimadamore diff -r 361efbfa3079 -r c1ca668b421e langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Fri Oct 24 10:54:04 2014 +0100 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Fri Oct 24 08:34:51 2014 +0200 @@ -989,8 +989,12 @@ // parameters have already been entered env.info.scope.enter(tree.sym); } else { - memberEnter.memberEnter(tree, env); - annotate.flush(); + try { + annotate.enterStart(); + memberEnter.memberEnter(tree, env); + } finally { + annotate.enterDone(); + } } } else { if (tree.init != null) { diff -r 361efbfa3079 -r c1ca668b421e langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java Fri Oct 24 10:54:04 2014 +0100 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/MemberEnter.java Fri Oct 24 08:34:51 2014 +0200 @@ -518,52 +518,46 @@ } Env localEnv = methodEnv(tree, env); - - annotate.enterStart(); + DiagnosticPosition prevLintPos = deferredLintHandler.setPos(tree.pos()); try { - DiagnosticPosition prevLintPos = deferredLintHandler.setPos(tree.pos()); - try { - // Compute the method type - m.type = signature(m, tree.typarams, tree.params, - tree.restype, tree.recvparam, - tree.thrown, - localEnv); - } finally { - deferredLintHandler.setPos(prevLintPos); - } + // Compute the method type + m.type = signature(m, tree.typarams, tree.params, + tree.restype, tree.recvparam, + tree.thrown, + localEnv); + } finally { + deferredLintHandler.setPos(prevLintPos); + } - if (types.isSignaturePolymorphic(m)) { - m.flags_field |= SIGNATURE_POLYMORPHIC; - } + if (types.isSignaturePolymorphic(m)) { + m.flags_field |= SIGNATURE_POLYMORPHIC; + } - // Set m.params - ListBuffer params = new ListBuffer<>(); - JCVariableDecl lastParam = null; - for (List l = tree.params; l.nonEmpty(); l = l.tail) { - JCVariableDecl param = lastParam = l.head; - params.append(Assert.checkNonNull(param.sym)); - } - m.params = params.toList(); + // Set m.params + ListBuffer params = new ListBuffer<>(); + JCVariableDecl lastParam = null; + for (List l = tree.params; l.nonEmpty(); l = l.tail) { + JCVariableDecl param = lastParam = l.head; + params.append(Assert.checkNonNull(param.sym)); + } + m.params = params.toList(); - // mark the method varargs, if necessary - if (lastParam != null && (lastParam.mods.flags & Flags.VARARGS) != 0) - m.flags_field |= Flags.VARARGS; + // mark the method varargs, if necessary + if (lastParam != null && (lastParam.mods.flags & Flags.VARARGS) != 0) + m.flags_field |= Flags.VARARGS; - localEnv.info.scope.leave(); - if (chk.checkUnique(tree.pos(), m, enclScope)) { - enclScope.enter(m); - } + localEnv.info.scope.leave(); + if (chk.checkUnique(tree.pos(), m, enclScope)) { + enclScope.enter(m); + } - annotate.annotateLater(tree.mods.annotations, localEnv, m, tree.pos()); - // Visit the signature of the method. Note that - // TypeAnnotate doesn't descend into the body. - annotate.annotateTypeLater(tree, localEnv, m, tree.pos()); + annotate.annotateLater(tree.mods.annotations, localEnv, m, tree.pos()); + // Visit the signature of the method. Note that + // TypeAnnotate doesn't descend into the body. + annotate.annotateTypeLater(tree, localEnv, m, tree.pos()); - if (tree.defaultValue != null) - annotateDefaultValueLater(tree.defaultValue, localEnv, m); - } finally { - annotate.enterDone(); - } + if (tree.defaultValue != null) + annotateDefaultValueLater(tree.defaultValue, localEnv, m); } /** Create a fresh environment for method bodies. @@ -591,56 +585,51 @@ localEnv.info.staticLevel++; } DiagnosticPosition prevLintPos = deferredLintHandler.setPos(tree.pos()); - annotate.enterStart(); try { - try { - if (TreeInfo.isEnumInit(tree)) { - attr.attribIdentAsEnumType(localEnv, (JCIdent)tree.vartype); - } else { - attr.attribType(tree.vartype, localEnv); - if (TreeInfo.isReceiverParam(tree)) - checkReceiver(tree, localEnv); - } - } finally { - deferredLintHandler.setPos(prevLintPos); - } - - if ((tree.mods.flags & VARARGS) != 0) { - //if we are entering a varargs parameter, we need to - //replace its type (a plain array type) with the more - //precise VarargsType --- we need to do it this way - //because varargs is represented in the tree as a - //modifier on the parameter declaration, and not as a - //distinct type of array node. - ArrayType atype = (ArrayType)tree.vartype.type; - tree.vartype.type = atype.makeVarargs(); + if (TreeInfo.isEnumInit(tree)) { + attr.attribIdentAsEnumType(localEnv, (JCIdent)tree.vartype); + } else { + attr.attribType(tree.vartype, localEnv); + if (TreeInfo.isReceiverParam(tree)) + checkReceiver(tree, localEnv); } - WriteableScope enclScope = enter.enterScope(env); - VarSymbol v = - new VarSymbol(0, tree.name, tree.vartype.type, enclScope.owner); - v.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, v, tree); - tree.sym = v; - if (tree.init != null) { - v.flags_field |= HASINIT; - if ((v.flags_field & FINAL) != 0 && - needsLazyConstValue(tree.init)) { - Env initEnv = getInitEnv(tree, env); - initEnv.info.enclVar = v; - v.setLazyConstValue(initEnv(tree, initEnv), attr, tree); - } + } finally { + deferredLintHandler.setPos(prevLintPos); + } + + if ((tree.mods.flags & VARARGS) != 0) { + //if we are entering a varargs parameter, we need to + //replace its type (a plain array type) with the more + //precise VarargsType --- we need to do it this way + //because varargs is represented in the tree as a + //modifier on the parameter declaration, and not as a + //distinct type of array node. + ArrayType atype = (ArrayType)tree.vartype.type; + tree.vartype.type = atype.makeVarargs(); + } + WriteableScope enclScope = enter.enterScope(env); + VarSymbol v = + new VarSymbol(0, tree.name, tree.vartype.type, enclScope.owner); + v.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, v, tree); + tree.sym = v; + if (tree.init != null) { + v.flags_field |= HASINIT; + if ((v.flags_field & FINAL) != 0 && + needsLazyConstValue(tree.init)) { + Env initEnv = getInitEnv(tree, env); + initEnv.info.enclVar = v; + v.setLazyConstValue(initEnv(tree, initEnv), attr, tree); } - if (chk.checkUnique(tree.pos(), v, enclScope)) { - chk.checkTransparentVar(tree.pos(), v, enclScope); - enclScope.enter(v); - } + } + if (chk.checkUnique(tree.pos(), v, enclScope)) { + chk.checkTransparentVar(tree.pos(), v, enclScope); + enclScope.enter(v); + } - annotate.annotateLater(tree.mods.annotations, localEnv, v, tree.pos()); - annotate.annotateTypeLater(tree.vartype, localEnv, v, tree.pos()); + annotate.annotateLater(tree.mods.annotations, localEnv, v, tree.pos()); + annotate.annotateTypeLater(tree.vartype, localEnv, v, tree.pos()); - v.pos = tree.pos; - } finally { - annotate.enterDone(); - } + v.pos = tree.pos; } // where void checkType(JCTree tree, Type type, String diag) { @@ -866,219 +855,225 @@ return; } - ClassSymbol c = (ClassSymbol)sym; - ClassType ct = (ClassType)c.type; - Env env = typeEnvs.get(c); - JCClassDecl tree = (JCClassDecl)env.tree; - boolean wasFirst = isFirst; - isFirst = false; + try { + annotate.enterStart(); + + ClassSymbol c = (ClassSymbol)sym; + ClassType ct = (ClassType)c.type; + Env env = typeEnvs.get(c); + JCClassDecl tree = (JCClassDecl)env.tree; + boolean wasFirst = isFirst; + isFirst = false; + + JavaFileObject prev = log.useSource(env.toplevel.sourcefile); + DiagnosticPosition prevLintPos = deferredLintHandler.setPos(tree.pos()); + try { + dependencies.push(c); + + // Save class environment for later member enter (2) processing. + halfcompleted.append(env); - JavaFileObject prev = log.useSource(env.toplevel.sourcefile); - DiagnosticPosition prevLintPos = deferredLintHandler.setPos(tree.pos()); - try { - dependencies.push(c); + // Mark class as not yet attributed. + c.flags_field |= UNATTRIBUTED; + + // If this is a toplevel-class, make sure any preceding import + // clauses have been seen. + if (c.owner.kind == PCK) { + memberEnter(env.toplevel, env.enclosing(TOPLEVEL)); + todo.append(env); + } - // Save class environment for later member enter (2) processing. - halfcompleted.append(env); + if (c.owner.kind == TYP) + c.owner.complete(); + + // create an environment for evaluating the base clauses + Env baseEnv = baseEnv(tree, env); - // Mark class as not yet attributed. - c.flags_field |= UNATTRIBUTED; + if (tree.extending != null) + annotate.annotateTypeLater(tree.extending, baseEnv, sym, tree.pos()); + for (JCExpression impl : tree.implementing) + annotate.annotateTypeLater(impl, baseEnv, sym, tree.pos()); + annotate.flush(); - // If this is a toplevel-class, make sure any preceding import - // clauses have been seen. - if (c.owner.kind == PCK) { - memberEnter(env.toplevel, env.enclosing(TOPLEVEL)); - todo.append(env); - } - - if (c.owner.kind == TYP) - c.owner.complete(); - - // create an environment for evaluating the base clauses - Env baseEnv = baseEnv(tree, env); + // Determine supertype. + Type supertype; + if (tree.extending != null) { + dependencies.push(AttributionKind.EXTENDS, tree.extending); + try { + supertype = attr.attribBase(tree.extending, baseEnv, + true, false, true); + } finally { + dependencies.pop(); + } + } else { + supertype = ((tree.mods.flags & Flags.ENUM) != 0) + ? attr.attribBase(enumBase(tree.pos, c), baseEnv, + true, false, false) + : (c.fullname == names.java_lang_Object) + ? Type.noType + : syms.objectType; + } + ct.supertype_field = modelMissingTypes(supertype, tree.extending, false); - if (tree.extending != null) - annotate.annotateTypeLater(tree.extending, baseEnv, sym, tree.pos()); - for (JCExpression impl : tree.implementing) - annotate.annotateTypeLater(impl, baseEnv, sym, tree.pos()); - annotate.flush(); + // Determine interfaces. + ListBuffer interfaces = new ListBuffer<>(); + ListBuffer all_interfaces = null; // lazy init + Set interfaceSet = new HashSet<>(); + List interfaceTrees = tree.implementing; + for (JCExpression iface : interfaceTrees) { + dependencies.push(AttributionKind.IMPLEMENTS, iface); + try { + Type it = attr.attribBase(iface, baseEnv, false, true, true); + if (it.hasTag(CLASS)) { + interfaces.append(it); + if (all_interfaces != null) all_interfaces.append(it); + chk.checkNotRepeated(iface.pos(), types.erasure(it), interfaceSet); + } else { + if (all_interfaces == null) + all_interfaces = new ListBuffer().appendList(interfaces); + all_interfaces.append(modelMissingTypes(it, iface, true)); + } + } finally { + dependencies.pop(); + } + } - // Determine supertype. - Type supertype; - if (tree.extending != null) { - dependencies.push(AttributionKind.EXTENDS, tree.extending); - try { - supertype = attr.attribBase(tree.extending, baseEnv, - true, false, true); - } finally { - dependencies.pop(); + if ((c.flags_field & ANNOTATION) != 0) { + ct.interfaces_field = List.of(syms.annotationType); + ct.all_interfaces_field = ct.interfaces_field; + } else { + ct.interfaces_field = interfaces.toList(); + ct.all_interfaces_field = (all_interfaces == null) + ? ct.interfaces_field : all_interfaces.toList(); } - } else { - supertype = ((tree.mods.flags & Flags.ENUM) != 0) - ? attr.attribBase(enumBase(tree.pos, c), baseEnv, - true, false, false) - : (c.fullname == names.java_lang_Object) - ? Type.noType - : syms.objectType; - } - ct.supertype_field = modelMissingTypes(supertype, tree.extending, false); + + if (c.fullname == names.java_lang_Object) { + if (tree.extending != null) { + chk.checkNonCyclic(tree.extending.pos(), + supertype); + ct.supertype_field = Type.noType; + } + else if (tree.implementing.nonEmpty()) { + chk.checkNonCyclic(tree.implementing.head.pos(), + ct.interfaces_field.head); + ct.interfaces_field = List.nil(); + } + } + + // Annotations. + // In general, we cannot fully process annotations yet, but we + // can attribute the annotation types and then check to see if the + // @Deprecated annotation is present. + attr.attribAnnotationTypes(tree.mods.annotations, baseEnv); + if (hasDeprecatedAnnotation(tree.mods.annotations)) + c.flags_field |= DEPRECATED; + annotate.annotateLater(tree.mods.annotations, baseEnv, + c, tree.pos()); + + chk.checkNonCyclicDecl(tree); + + // class type parameters use baseEnv but everything uses env + attr.attribTypeVariables(tree.typarams, baseEnv); + for (JCTypeParameter tp : tree.typarams) + annotate.annotateTypeLater(tp, baseEnv, sym, tree.pos()); - // Determine interfaces. - ListBuffer interfaces = new ListBuffer<>(); - ListBuffer all_interfaces = null; // lazy init - Set interfaceSet = new HashSet<>(); - List interfaceTrees = tree.implementing; - for (JCExpression iface : interfaceTrees) { - dependencies.push(AttributionKind.IMPLEMENTS, iface); - try { - Type it = attr.attribBase(iface, baseEnv, false, true, true); - if (it.hasTag(CLASS)) { - interfaces.append(it); - if (all_interfaces != null) all_interfaces.append(it); - chk.checkNotRepeated(iface.pos(), types.erasure(it), interfaceSet); - } else { - if (all_interfaces == null) - all_interfaces = new ListBuffer().appendList(interfaces); - all_interfaces.append(modelMissingTypes(it, iface, true)); + // Add default constructor if needed. + if ((c.flags() & INTERFACE) == 0 && + !TreeInfo.hasConstructors(tree.defs)) { + List argtypes = List.nil(); + List typarams = List.nil(); + List thrown = List.nil(); + long ctorFlags = 0; + boolean based = false; + boolean addConstructor = true; + JCNewClass nc = null; + if (c.name.isEmpty()) { + nc = (JCNewClass)env.next.tree; + if (nc.constructor != null) { + addConstructor = nc.constructor.kind != ERR; + Type superConstrType = types.memberType(c.type, + nc.constructor); + argtypes = superConstrType.getParameterTypes(); + typarams = superConstrType.getTypeArguments(); + ctorFlags = nc.constructor.flags() & VARARGS; + if (nc.encl != null) { + argtypes = argtypes.prepend(nc.encl.type); + based = true; + } + thrown = superConstrType.getThrownTypes(); + } } - } finally { - dependencies.pop(); + if (addConstructor) { + MethodSymbol basedConstructor = nc != null ? + (MethodSymbol)nc.constructor : null; + JCTree constrDef = DefaultConstructor(make.at(tree.pos), c, + basedConstructor, + typarams, argtypes, thrown, + ctorFlags, based); + tree.defs = tree.defs.prepend(constrDef); + } } - } - if ((c.flags_field & ANNOTATION) != 0) { - ct.interfaces_field = List.of(syms.annotationType); - ct.all_interfaces_field = ct.interfaces_field; - } else { - ct.interfaces_field = interfaces.toList(); - ct.all_interfaces_field = (all_interfaces == null) - ? ct.interfaces_field : all_interfaces.toList(); - } + // enter symbols for 'this' into current scope. + VarSymbol thisSym = + new VarSymbol(FINAL | HASINIT, names._this, c.type, c); + thisSym.pos = Position.FIRSTPOS; + env.info.scope.enter(thisSym); + // if this is a class, enter symbol for 'super' into current scope. + if ((c.flags_field & INTERFACE) == 0 && + ct.supertype_field.hasTag(CLASS)) { + VarSymbol superSym = + new VarSymbol(FINAL | HASINIT, names._super, + ct.supertype_field, c); + superSym.pos = Position.FIRSTPOS; + env.info.scope.enter(superSym); + } - if (c.fullname == names.java_lang_Object) { - if (tree.extending != null) { - chk.checkNonCyclic(tree.extending.pos(), - supertype); - ct.supertype_field = Type.noType; + // check that no package exists with same fully qualified name, + // but admit classes in the unnamed package which have the same + // name as a top-level package. + if (checkClash && + c.owner.kind == PCK && c.owner != syms.unnamedPackage && + syms.packageExists(c.fullname)) { + log.error(tree.pos, "clash.with.pkg.of.same.name", Kinds.kindName(sym), c); } - else if (tree.implementing.nonEmpty()) { - chk.checkNonCyclic(tree.implementing.head.pos(), - ct.interfaces_field.head); - ct.interfaces_field = List.nil(); + if (c.owner.kind == PCK && (c.flags_field & PUBLIC) == 0 && + !env.toplevel.sourcefile.isNameCompatible(c.name.toString(),JavaFileObject.Kind.SOURCE)) { + c.flags_field |= AUXILIARY; } + } catch (CompletionFailure ex) { + chk.completionError(tree.pos(), ex); + } finally { + deferredLintHandler.setPos(prevLintPos); + log.useSource(prev); + dependencies.pop(); } - // Annotations. - // In general, we cannot fully process annotations yet, but we - // can attribute the annotation types and then check to see if the - // @Deprecated annotation is present. - attr.attribAnnotationTypes(tree.mods.annotations, baseEnv); - if (hasDeprecatedAnnotation(tree.mods.annotations)) - c.flags_field |= DEPRECATED; - annotate.annotateLater(tree.mods.annotations, baseEnv, - c, tree.pos()); - - chk.checkNonCyclicDecl(tree); - - // class type parameters use baseEnv but everything uses env - attr.attribTypeVariables(tree.typarams, baseEnv); - for (JCTypeParameter tp : tree.typarams) - annotate.annotateTypeLater(tp, baseEnv, sym, tree.pos()); - - // Add default constructor if needed. - if ((c.flags() & INTERFACE) == 0 && - !TreeInfo.hasConstructors(tree.defs)) { - List argtypes = List.nil(); - List typarams = List.nil(); - List thrown = List.nil(); - long ctorFlags = 0; - boolean based = false; - boolean addConstructor = true; - JCNewClass nc = null; - if (c.name.isEmpty()) { - nc = (JCNewClass)env.next.tree; - if (nc.constructor != null) { - addConstructor = nc.constructor.kind != ERR; - Type superConstrType = types.memberType(c.type, - nc.constructor); - argtypes = superConstrType.getParameterTypes(); - typarams = superConstrType.getTypeArguments(); - ctorFlags = nc.constructor.flags() & VARARGS; - if (nc.encl != null) { - argtypes = argtypes.prepend(nc.encl.type); - based = true; + // Enter all member fields and methods of a set of half completed + // classes in a second phase. + if (wasFirst) { + Set topLevels = new HashSet<>(); + try { + while (halfcompleted.nonEmpty()) { + Env toFinish = halfcompleted.next(); + topLevels.add(toFinish.toplevel); + finish(toFinish); + if (allowTypeAnnos) { + typeAnnotations.organizeTypeAnnotationsSignatures(toFinish, (JCClassDecl)toFinish.tree); + typeAnnotations.validateTypeAnnotationsSignatures(toFinish, (JCClassDecl)toFinish.tree); } - thrown = superConstrType.getThrownTypes(); } + } finally { + isFirst = true; } - if (addConstructor) { - MethodSymbol basedConstructor = nc != null ? - (MethodSymbol)nc.constructor : null; - JCTree constrDef = DefaultConstructor(make.at(tree.pos), c, - basedConstructor, - typarams, argtypes, thrown, - ctorFlags, based); - tree.defs = tree.defs.prepend(constrDef); - } - } - // enter symbols for 'this' into current scope. - VarSymbol thisSym = - new VarSymbol(FINAL | HASINIT, names._this, c.type, c); - thisSym.pos = Position.FIRSTPOS; - env.info.scope.enter(thisSym); - // if this is a class, enter symbol for 'super' into current scope. - if ((c.flags_field & INTERFACE) == 0 && - ct.supertype_field.hasTag(CLASS)) { - VarSymbol superSym = - new VarSymbol(FINAL | HASINIT, names._super, - ct.supertype_field, c); - superSym.pos = Position.FIRSTPOS; - env.info.scope.enter(superSym); - } + for (JCCompilationUnit toplevel : topLevels) { + chk.checkImportsResolvable(toplevel); + } - // check that no package exists with same fully qualified name, - // but admit classes in the unnamed package which have the same - // name as a top-level package. - if (checkClash && - c.owner.kind == PCK && c.owner != syms.unnamedPackage && - syms.packageExists(c.fullname)) { - log.error(tree.pos, "clash.with.pkg.of.same.name", Kinds.kindName(sym), c); - } - if (c.owner.kind == PCK && (c.flags_field & PUBLIC) == 0 && - !env.toplevel.sourcefile.isNameCompatible(c.name.toString(),JavaFileObject.Kind.SOURCE)) { - c.flags_field |= AUXILIARY; } - } catch (CompletionFailure ex) { - chk.completionError(tree.pos(), ex); } finally { - deferredLintHandler.setPos(prevLintPos); - log.useSource(prev); - dependencies.pop(); - } - - // Enter all member fields and methods of a set of half completed - // classes in a second phase. - if (wasFirst) { - Set topLevels = new HashSet<>(); - try { - while (halfcompleted.nonEmpty()) { - Env toFinish = halfcompleted.next(); - topLevels.add(toFinish.toplevel); - finish(toFinish); - if (allowTypeAnnos) { - typeAnnotations.organizeTypeAnnotationsSignatures(toFinish, (JCClassDecl)toFinish.tree); - typeAnnotations.validateTypeAnnotationsSignatures(toFinish, (JCClassDecl)toFinish.tree); - } - } - } finally { - isFirst = true; - } - - for (JCCompilationUnit toplevel : topLevels) { - chk.checkImportsResolvable(toplevel); - } - + annotate.enterDone(); } } diff -r 361efbfa3079 -r c1ca668b421e langtools/test/tools/javac/annotations/FinalStringInNested.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/annotations/FinalStringInNested.java Fri Oct 24 08:34:51 2014 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, 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 8054448 + * @summary Verify that constant strings in nested classes in anonymous classes + * can be used in annotations. + * @compile FinalStringInNested.java + */ + +public class FinalStringInNested { + + public void f() { + Object o = new Object() { + @FinalStringInNested.Annotation(Nested.ID) + class Nested { + static final String ID = "B"; + } + }; + } + + @interface Annotation { + String value(); + } +}