8295184: Printing messages with a RecordComponentElement does not include position

Reviewed-by: vromero
This commit is contained in:
Srikanth Adayapalam 2022-12-06 04:23:40 +00:00
parent ba2d28e911
commit ee9ba74564
7 changed files with 223 additions and 16 deletions

View File

@ -1519,14 +1519,14 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
/* creates a record component if non is related to the given variable and recreates a brand new one
* in other case
*/
public RecordComponent createRecordComponent(RecordComponent existing, JCVariableDecl var, List<JCAnnotation> annotations) {
public RecordComponent createRecordComponent(RecordComponent existing, JCVariableDecl rcDecl, VarSymbol varSym) {
RecordComponent rc = null;
if (existing != null) {
recordComponents = List.filter(recordComponents, existing);
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, existing.originalAnnos, existing.isVarargs));
recordComponents = recordComponents.append(rc = new RecordComponent(varSym, existing.ast, existing.isVarargs));
} else {
// Didn't find the record component: create one.
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, annotations));
recordComponents = recordComponents.append(rc = new RecordComponent(varSym, rcDecl));
}
return rc;
}
@ -1786,9 +1786,7 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
public static class RecordComponent extends VarSymbol implements RecordComponentElement {
public MethodSymbol accessor;
public JCTree.JCMethodDecl accessorMeth;
/* the original annotations applied to the record component
*/
private final List<JCAnnotation> originalAnnos;
/* if the user happens to erroneously declare two components with the same name, we need a way to differentiate
* them, the code will fail anyway but we need to keep the information for better error recovery
*/
@ -1796,23 +1794,25 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
private final boolean isVarargs;
private JCVariableDecl ast;
/**
* Construct a record component, given its flags, name, type and owner.
*/
public RecordComponent(Name name, Type type, Symbol owner) {
super(PUBLIC, name, type, owner);
pos = -1;
originalAnnos = List.nil();
ast = null;
isVarargs = false;
}
public RecordComponent(VarSymbol field, List<JCAnnotation> annotations) {
this(field, annotations, field.type.hasTag(TypeTag.ARRAY) && ((ArrayType)field.type).isVarargs());
public RecordComponent(VarSymbol field, JCVariableDecl ast) {
this(field, ast, field.type.hasTag(TypeTag.ARRAY) && ((ArrayType)field.type).isVarargs());
}
public RecordComponent(VarSymbol field, List<JCAnnotation> annotations, boolean isVarargs) {
public RecordComponent(VarSymbol field, JCVariableDecl ast, boolean isVarargs) {
super(PUBLIC, field.name, field.type, field.owner);
this.originalAnnos = annotations;
this.ast = ast;
this.pos = field.pos;
/* it is better to store the original information for this one, instead of relying
* on the info in the type of the symbol. This is because on the presence of APs
@ -1822,7 +1822,9 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
this.isVarargs = isVarargs;
}
public List<JCAnnotation> getOriginalAnnos() { return originalAnnos; }
public List<JCAnnotation> getOriginalAnnos() { return this.ast == null ? List.nil() : this.ast.mods.annotations; }
public JCVariableDecl declarationFor() { return this.ast; }
public boolean isVarargs() {
return isVarargs;

View File

@ -3006,6 +3006,16 @@ public class Check {
Arrays.stream(getTargetNames(anno.type.tsym)).anyMatch(name -> name == names.RECORD_COMPONENT)
).collect(List.collector()));
JCVariableDecl fieldAST = (JCVariableDecl) declarationTree;
for (JCAnnotation fieldAnnot : fieldAST.mods.annotations) {
for (JCAnnotation rcAnnot : rc.declarationFor().mods.annotations) {
if (rcAnnot.pos == fieldAnnot.pos) {
rcAnnot.setType(fieldAnnot.type);
break;
}
}
}
/* At this point, we used to carry over any type annotations from the VARDEF to the record component, but
* that is problematic, since we get here only when *some* annotation is applied to the SE5 (declaration)
* annotation location, inadvertently failing to carry over the type annotations when the VarDef has no

View File

@ -996,10 +996,8 @@ public class TypeEnter implements Completer {
memberEnter.memberEnter(field, env);
sym.createRecordComponent(rc, field,
field.mods.annotations.isEmpty() ?
List.nil() :
new TreeCopier<JCTree>(make.at(field.pos)).copy(field.mods.annotations));
JCVariableDecl rcDecl = new TreeCopier<JCTree>(make.at(field.pos)).copy(field);
sym.createRecordComponent(rc, rcDecl, field.sym);
}
enterThisAndSuper(sym, env);

View File

@ -30,6 +30,7 @@ package com.sun.tools.javac.tree;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Symbol.RecordComponent;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.tree.JCTree.*;
@ -46,6 +47,7 @@ import static com.sun.tools.javac.tree.JCTree.Tag.*;
import static com.sun.tools.javac.tree.JCTree.Tag.BLOCK;
import static com.sun.tools.javac.tree.JCTree.Tag.SYNCHRONIZED;
import javax.lang.model.element.ElementKind;
import javax.tools.JavaFileObject;
import java.util.function.Predicate;
@ -792,6 +794,12 @@ public class TreeInfo {
result = that;
return true;
}
if (this.sym.getKind() == ElementKind.RECORD_COMPONENT) {
if (thatSym != null && thatSym.getKind() == ElementKind.FIELD && (thatSym.flags_field & RECORD) != 0) {
RecordComponent rc = thatSym.enclClass().getRecordComponent((VarSymbol)thatSym);
return checkMatch(rc.declarationFor(), rc);
}
}
return false;
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2022, 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 8295184
* @summary Printing messages with a RecordComponentElement does not include position
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* @compile TestWarning.java
* @compile ReproducingAP.java
* @run main RecordComponentSourcePositionTest
*/
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import toolbox.JavacTask;
import toolbox.TestRunner;
import toolbox.ToolBox;
import toolbox.Task;
public class RecordComponentSourcePositionTest extends TestRunner {
ToolBox tb;
public RecordComponentSourcePositionTest() {
super(System.err);
tb = new ToolBox();
}
public static void main(String[] args) throws Exception {
RecordComponentSourcePositionTest t = new RecordComponentSourcePositionTest();
t.runTests();
}
@Test
public void testRecordComponentPositionInDiagnostics() throws Exception {
String code = """
@TestWarning(includeAnnotation = true)
public record Test(
@TestWarning(includeAnnotation = true) int first,
@TestWarning int second) {
}
@TestWarning
record Test2() {}
""";
Path curPath = Path.of(".");
List<String> output = new JavacTask(tb)
.sources(code)
.outdir(curPath)
.options("-XDrawDiagnostics", "-processor", "ReproducingAP")
.run()
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
List<String> expected = Arrays.asList(
"Test.java:1:1: compiler.warn.proc.messager: Reporting Test with an annotation",
"Test.java:3:9: compiler.warn.proc.messager: Reporting first with an annotation",
"Test.java:4:26: compiler.warn.proc.messager: Reporting second",
"Test.java:8:1: compiler.warn.proc.messager: Reporting Test2",
"4 warnings");
tb.checkEqual(expected, output);
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2022, 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.
*/
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
public class ReproducingAP extends AbstractProcessor {
@Override
public Set<String> getSupportedAnnotationTypes() {
return Set.of(TestWarning.class.getName());
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
roundEnv.getElementsAnnotatedWith(TestWarning.class).forEach(e -> {
var annotation = e.getAnnotation(TestWarning.class);
if (annotation.includeAnnotation()) {
processingEnv.getMessager().printMessage(
javax.tools.Diagnostic.Kind.WARNING,
"Reporting " + e.getSimpleName() + " with an annotation",
e,
e.getAnnotationMirrors().get(0));
} else {
processingEnv.getMessager().printMessage(
javax.tools.Diagnostic.Kind.WARNING,
"Reporting " + e.getSimpleName(),
e);
}
});
return false;
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2022, 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.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* Direct {@link ReproducingAP} to emit a warning.
*/
@Target({ElementType.TYPE, ElementType.RECORD_COMPONENT})
public @interface TestWarning {
/**
* {@return {@code true} to include the relevant mirror in the warning message}
*/
boolean includeAnnotation() default false;
}