8205418: Assorted improvements to source code model

Improving tree positions, better error recovery, fixing Trees.getScope for possibly erroneous lambdas.

Reviewed-by: jjg, mcimadamore, vromero
This commit is contained in:
Jan Lahoda 2018-06-29 10:41:10 +02:00
parent 69191fc4cc
commit eaf0364068
14 changed files with 434 additions and 40 deletions

View File

@ -1659,7 +1659,7 @@ public class Types {
private TypeRelation isCastable = new TypeRelation() {
public Boolean visitType(Type t, Type s) {
if (s.hasTag(ERROR))
if (s.hasTag(ERROR) || t.hasTag(NONE))
return true;
switch (t.getTag()) {

View File

@ -586,8 +586,11 @@ public class Attr extends JCTree.Visitor {
class RecoveryInfo extends ResultInfo {
public RecoveryInfo(final DeferredAttr.DeferredAttrContext deferredAttrContext) {
super(KindSelector.VAL, Type.recoveryType,
new Check.NestedCheckContext(chk.basicHandler) {
this(deferredAttrContext, Type.recoveryType);
}
public RecoveryInfo(final DeferredAttr.DeferredAttrContext deferredAttrContext, Type pt) {
super(KindSelector.VAL, pt, new Check.NestedCheckContext(chk.basicHandler) {
@Override
public DeferredAttr.DeferredAttrContext deferredAttrContext() {
return deferredAttrContext;
@ -598,7 +601,9 @@ public class Attr extends JCTree.Visitor {
}
@Override
public void report(DiagnosticPosition pos, JCDiagnostic details) {
chk.basicHandler.report(pos, details);
if (pt == Type.recoveryType) {
chk.basicHandler.report(pos, details);
}
}
});
}
@ -656,7 +661,7 @@ public class Attr extends JCTree.Visitor {
}
if (tree == breakTree &&
resultInfo.checkContext.deferredAttrContext().mode == AttrMode.CHECK) {
throw new BreakAttr(copyEnv(env));
breakTreeFound(copyEnv(env));
}
return result;
} catch (CompletionFailure ex) {
@ -668,6 +673,10 @@ public class Attr extends JCTree.Visitor {
}
}
protected void breakTreeFound(Env<AttrContext> env) {
throw new BreakAttr(env);
}
Env<AttrContext> copyEnv(Env<AttrContext> env) {
Env<AttrContext> newEnv =
env.dup(env.tree, env.info.dup(copyScope(env.info.scope)));
@ -2506,8 +2515,7 @@ public class Attr extends JCTree.Visitor {
//lambda only allowed in assignment or method invocation/cast context
log.error(that.pos(), Errors.UnexpectedLambda);
}
result = that.type = types.createErrorType(pt());
return;
resultInfo = recoveryInfo;
}
//create an environment for attribution of the lambda expression
final Env<AttrContext> localEnv = lambdaEnv(that, env);
@ -2595,6 +2603,10 @@ public class Attr extends JCTree.Visitor {
attribTree(that.getBody(), localEnv, bodyResultInfo);
} else {
JCBlock body = (JCBlock)that.body;
if (body == breakTree &&
resultInfo.checkContext.deferredAttrContext().mode == AttrMode.CHECK) {
breakTreeFound(copyEnv(localEnv));
}
attribStats(body.stats, localEnv);
}
@ -4126,8 +4138,8 @@ public class Attr extends JCTree.Visitor {
typeargtypes,
noteWarner);
DeferredAttr.DeferredTypeMap checkDeferredMap =
deferredAttr.new DeferredTypeMap(DeferredAttr.AttrMode.CHECK, sym, env.info.pendingResolutionPhase);
DeferredAttr.DeferredTypeMap<Void> checkDeferredMap =
deferredAttr.new DeferredTypeMap<>(DeferredAttr.AttrMode.CHECK, sym, env.info.pendingResolutionPhase);
argtypes = argtypes.map(checkDeferredMap);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2018, 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
@ -28,6 +28,8 @@ package com.sun.tools.javac.comp;
import com.sun.source.tree.LambdaExpressionTree.BodyKind;
import com.sun.source.tree.NewClassTree;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Type.ErrorType;
import com.sun.tools.javac.code.Type.MethodType;
import com.sun.tools.javac.code.Type.StructuralTypeMapping;
import com.sun.tools.javac.code.Types.TypeMapping;
import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext;
@ -59,6 +61,7 @@ import java.util.WeakHashMap;
import java.util.function.Function;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree.JCMemberReference.OverloadKind;
import static com.sun.tools.javac.code.TypeTag.*;
@ -1002,7 +1005,7 @@ public class DeferredAttr extends JCTree.Visitor {
* where T is computed by retrieving the type that has already been
* computed for D during a previous deferred attribution round of the given kind.
*/
class DeferredTypeMap extends StructuralTypeMapping<Void> {
class DeferredTypeMap<T> extends StructuralTypeMapping<T> {
DeferredAttrContext deferredAttrContext;
protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
@ -1011,16 +1014,16 @@ public class DeferredAttr extends JCTree.Visitor {
}
@Override
public Type visitType(Type t, Void _unused) {
public Type visitType(Type t, T p) {
if (!t.hasTag(DEFERRED)) {
return super.visitType(t, null);
return super.visitType(t, p);
} else {
DeferredType dt = (DeferredType)t;
return typeOf(dt);
return typeOf(dt, p);
}
}
protected Type typeOf(DeferredType dt) {
protected Type typeOf(DeferredType dt, T p) {
switch (deferredAttrContext.mode) {
case CHECK:
return dt.tree.type == null ? Type.noType : dt.tree.type;
@ -1039,17 +1042,35 @@ public class DeferredAttr extends JCTree.Visitor {
* attribution round (as before), or (ii) by synthesizing a new type R for D
* (the latter step is useful in a recovery scenario).
*/
public class RecoveryDeferredTypeMap extends DeferredTypeMap {
public class RecoveryDeferredTypeMap extends DeferredTypeMap<Type> {
public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
}
@Override
protected Type typeOf(DeferredType dt) {
Type owntype = super.typeOf(dt);
protected Type typeOf(DeferredType dt, Type pt) {
Type owntype = super.typeOf(dt, pt);
return owntype == Type.noType ?
recover(dt) : owntype;
recover(dt, pt) : owntype;
}
@Override
public Type visitMethodType(Type.MethodType t, Type pt) {
if (t.hasTag(METHOD) && deferredAttrContext.mode == AttrMode.CHECK) {
Type mtype = deferredAttrContext.msym.type;
mtype = mtype.hasTag(ERROR) ? ((ErrorType)mtype).getOriginalType() : null;
if (mtype != null && mtype.hasTag(METHOD)) {
List<Type> argtypes1 = map(t.getParameterTypes(), mtype.getParameterTypes());
Type restype1 = visit(t.getReturnType(), mtype.getReturnType());
List<Type> thrown1 = map(t.getThrownTypes(), mtype.getThrownTypes());
if (argtypes1 == t.getParameterTypes() &&
restype1 == t.getReturnType() &&
thrown1 == t.getThrownTypes()) return t;
else return new MethodType(argtypes1, restype1, thrown1, t.tsym);
}
}
return super.visitMethodType(t, pt);
}
/**
@ -1059,8 +1080,8 @@ public class DeferredAttr extends JCTree.Visitor {
* representation. Remaining deferred types are attributed using
* a default expected type (j.l.Object).
*/
private Type recover(DeferredType dt) {
dt.check(attr.new RecoveryInfo(deferredAttrContext) {
private Type recover(DeferredType dt, Type pt) {
dt.check(attr.new RecoveryInfo(deferredAttrContext, pt != null ? pt : Type.recoveryType) {
@Override
protected Type check(DiagnosticPosition pos, Type found) {
return chk.checkNonVoid(pos, super.check(pos, found));
@ -1068,6 +1089,16 @@ public class DeferredAttr extends JCTree.Visitor {
});
return super.visit(dt);
}
private List<Type> map(List<Type> ts, List<Type> pts) {
if (ts.nonEmpty()) {
List<Type> tail1 = map(ts.tail, pts != null ? pts.tail : null);
Type t = visit(ts.head, pts != null && pts.nonEmpty() ? pts.head : null);
if (tail1 != ts.tail || t != ts.head)
return tail1.prepend(t);
}
return ts;
}
}
/**

View File

@ -602,7 +602,7 @@ public class Infer {
return mtype;
}
//where
class ImplicitArgType extends DeferredAttr.DeferredTypeMap {
class ImplicitArgType extends DeferredAttr.DeferredTypeMap<Void> {
public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) {
(rs.deferredAttr).super(AttrMode.SPECULATIVE, msym, phase);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, 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
@ -210,7 +210,7 @@ public class Operators {
* Report an operator lookup error.
*/
private OperatorSymbol reportErrorIfNeeded(DiagnosticPosition pos, Tag tag, Type... args) {
if (Stream.of(args).noneMatch(Type::isErroneous)) {
if (Stream.of(args).noneMatch(t -> t.isErroneous() || t.hasTag(TypeTag.NONE))) {
Name opName = operatorName(tag);
JCDiagnostic.Error opError = (args.length) == 1 ?
Errors.OperatorCantBeApplied(opName, args[0]) :

View File

@ -2560,8 +2560,8 @@ public class Resolve {
}
@Override
protected Type typeOf(DeferredType dt) {
Type res = super.typeOf(dt);
protected Type typeOf(DeferredType dt, Type pt) {
Type res = super.typeOf(dt, pt);
if (!res.isErroneous()) {
switch (TreeInfo.skipParens(dt.tree).getTag()) {
case LAMBDA:
@ -3992,7 +3992,12 @@ public class Resolve {
@Override
public Symbol access(Name name, TypeSymbol location) {
return types.createErrorType(name, location, syms.errSymbol.type).tsym;
Symbol sym = bestCandidate();
return types.createErrorType(name, location, sym != null ? sym.type : syms.errSymbol.type).tsym;
}
protected Symbol bestCandidate() {
return errCandidate().fst;
}
protected Pair<Symbol, JCDiagnostic> errCandidate() {
@ -4123,6 +4128,16 @@ public class Resolve {
//conform to source order
return details;
}
@Override
protected Symbol bestCandidate() {
Map<Symbol, JCDiagnostic> candidatesMap = mapCandidates();
Map<Symbol, JCDiagnostic> filteredCandidates = filterCandidates(candidatesMap);
if (filteredCandidates.size() == 1) {
return filteredCandidates.keySet().iterator().next();
}
return null;
}
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2018, 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
@ -674,7 +674,7 @@ public class JavaTokenizer {
scanNumber(pos, 10);
} else if (reader.bp == reader.buflen || reader.ch == EOI && reader.bp + 1 == reader.buflen) { // JLS 3.5
tk = TokenKind.EOF;
pos = reader.buflen;
pos = reader.realLength;
} else {
String arg;

View File

@ -1516,6 +1516,7 @@ public class JavacParser implements Parser {
ParensResult analyzeParens() {
int depth = 0;
boolean type = false;
ParensResult defaultResult = ParensResult.PARENS;
outer: for (int lookahead = 0 ; ; lookahead++) {
TokenKind tk = S.token(lookahead).kind;
switch (tk) {
@ -1568,7 +1569,7 @@ public class JavacParser implements Parser {
case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
return ParensResult.CAST;
default:
return ParensResult.PARENS;
return defaultResult;
}
case UNDERSCORE:
case ASSERT:
@ -1580,6 +1581,8 @@ public class JavacParser implements Parser {
} else if (peekToken(lookahead, RPAREN, ARROW)) {
// Identifier, ')' '->' -> implicit lambda
return ParensResult.IMPLICIT_LAMBDA;
} else if (depth == 0 && peekToken(lookahead, COMMA)) {
defaultResult = ParensResult.IMPLICIT_LAMBDA;
}
type = false;
break;
@ -1665,7 +1668,7 @@ public class JavacParser implements Parser {
break;
default:
//this includes EOF
return ParensResult.PARENS;
return defaultResult;
}
}
}
@ -3753,10 +3756,16 @@ public class JavacParser implements Parser {
return defs;
} else {
pos = token.pos;
List<JCTree> err = isVoid
? List.of(toP(F.at(pos).MethodDef(mods, name, type, typarams,
List.nil(), List.nil(), null, null)))
: null;
List<JCTree> err;
if (isVoid || typarams.nonEmpty()) {
JCMethodDecl m =
toP(F.at(pos).MethodDef(mods, name, type, typarams,
List.nil(), List.nil(), null, null));
attach(m, dc);
err = List.of(m);
} else {
err = List.nil();
}
return List.of(syntaxError(token.pos, err, Errors.Expected(LPAREN)));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2018, 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
@ -70,6 +70,7 @@ public class UnicodeReader {
/** A character buffer for saved chars.
*/
protected char[] sbuf = new char[128];
protected int realLength;
protected int sp;
/**
@ -89,6 +90,7 @@ public class UnicodeReader {
protected UnicodeReader(ScannerFactory sf, char[] input, int inputLength) {
log = sf.log;
names = sf.names;
realLength = inputLength;
if (inputLength == input.length) {
if (input.length > 0 && Character.isWhitespace(input[input.length - 1])) {
inputLength--;

View File

@ -3,6 +3,6 @@ T8012003b.java:31:16: compiler.err.prob.found.req: (compiler.misc.stat.expr.expe
T8012003b.java:32:22: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.conditional.target.cant.be.void))
T8012003b.java:33:12: compiler.err.prob.found.req: (compiler.misc.invalid.mref: kindname.method, (compiler.misc.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.String)))
T8012003b.java:34:12: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.mref: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer))
T8012003b.java:35:12: compiler.err.invalid.mref: kindname.method, (compiler.misc.cant.resolve.location.args: kindname.method, k, , , (compiler.misc.location: kindname.class, T8012003b, null))
T8012003b.java:35:12: compiler.err.invalid.mref: kindname.method, (compiler.misc.cant.resolve.location.args: kindname.method, k, , java.lang.String, (compiler.misc.location: kindname.class, T8012003b, null))
- compiler.note.compressed.diags
6 errors

View File

@ -0,0 +1,155 @@
/*
* Copyright (c) 2018, 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 8205418
* @summary Test the outcomes from Trees.getScope
* @modules jdk.compiler
*/
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import javax.lang.model.element.Element;
import javax.tools.JavaCompiler;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.Scope;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
import static javax.tools.JavaFileObject.Kind.SOURCE;
public class TestGetScopeResult {
public static void main(String... args) throws IOException {
new TestGetScopeResult().run();
}
public void run() throws IOException {
String[] simpleLambda = {
"s:java.lang.String",
"i:Test.I",
"super:java.lang.Object",
"this:Test"
};
doTest("class Test { void test() { I i = s -> { }; } interface I { public void test(String s); } }",
simpleLambda);
doTest("class Test { void test() { I i = s -> { }; } interface I { public int test(String s); } }",
simpleLambda);
doTest("class Test { void test() { I i = s -> { }; } interface I { public String test(String s); } }",
simpleLambda);
doTest("class Test { void test() { I i; inv(s -> { }); } void inv(I i) { } interface I { public void test(String s); } }",
simpleLambda);
doTest("class Test { void test() { I i; inv(s -> { }); } void inv(I i) { } interface I { public int test(String s); } }",
simpleLambda);
doTest("class Test { void test() { I i; inv(s -> { }); } void inv(I i) { } interface I { public String test(String s); } }",
simpleLambda);
String[] dualLambda = {
"s:java.lang.String",
"i:Test.I1",
"super:java.lang.Object",
"this:Test",
"s:java.lang.CharSequence",
"i:Test.I1",
"super:java.lang.Object",
"this:Test"
};
doTest("class Test { void test() { I1 i; inv(s -> { }, s -> { }); } void inv(I1 i, I2 i) { } interface I1 { public String test(String s); } interface I2 { public void test(CharSequence s); } }",
dualLambda);
doTest("class Test { void test() { I1 i; inv(s -> { }, s -> { }); } void inv(I1 i, I2 i) { } interface I1 { public String test(String s); } interface I2 { public int test(CharSequence s); } }",
dualLambda);
String[] brokenType = {
"s:<any>",
"u:Undefined",
"super:java.lang.Object",
"this:Test"
};
doTest("class Test { void test() { Undefined u = s -> { }; } }",
brokenType);
String[] multipleCandidates1 = {
"s:<any>",
"super:java.lang.Object",
"this:Test"
};
doTest("class Test { void test() { cand1(s -> { }); } void cand1(I1 i) { } void cand1(I2 i) { } interface I1 { public String test(String s); } interface I2 { public int test(CharSequence s); } }",
multipleCandidates1);
String[] multipleCandidates2 = {
"s:java.lang.String",
"super:java.lang.Object",
"this:Test"
};
doTest("class Test { void test() { cand1(s -> { }); } void cand1(I1 i) { } void cand1(I2 i, int i) { } interface I1 { public String test(String s); } interface I2 { public int test(CharSequence s); } }",
multipleCandidates2);
}
public void doTest(String code, String... expected) throws IOException {
JavaCompiler c = ToolProvider.getSystemJavaCompiler();
try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) {
class MyFileObject extends SimpleJavaFileObject {
MyFileObject() {
super(URI.create("myfo:///Test.java"), SOURCE);
}
@Override
public String getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, List.of(new MyFileObject()));
CompilationUnitTree cut = t.parse().iterator().next();
t.analyze();
List<String> actual = new ArrayList<>();
new TreePathScanner<Void, Void>() {
@Override
public Void visitLambdaExpression(LambdaExpressionTree node, Void p) {
Scope scope = Trees.instance(t).getScope(new TreePath(getCurrentPath(), node.getBody()));
while (scope.getEnclosingClass() != null) {
for (Element el : scope.getLocalElements()) {
actual.add(el.getSimpleName() + ":" +el.asType().toString());
}
scope = scope.getEnclosingScope();
}
return super.visitLambdaExpression(node, p);
}
}.scan(cut, null);
List<String> expectedList = List.of(expected);
if (!expectedList.equals(actual)) {
throw new IllegalStateException("Unexpected scope content: " + actual);
}
}
}
}

View File

@ -1,3 +1,4 @@
BadRecovery.java:17:9: compiler.err.cant.apply.symbol: kindname.method, m, BadRecovery.SAM1, @11, kindname.class, BadRecovery, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.incompatible.arg.types.in.lambda))
BadRecovery.java:17:38: compiler.err.cant.resolve.location.args: kindname.method, someMemberOfReceiver, , @60, (compiler.misc.location.1: kindname.variable, receiver, java.lang.Object)
BadRecovery.java:17:77: compiler.err.cant.resolve.location: kindname.variable, f, , , (compiler.misc.location: kindname.class, BadRecovery, null)
2 errors
3 errors

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 7073631 7159445 7156633 8028235 8065753 8205913
* @bug 7073631 7159445 7156633 8028235 8065753 8205418 8205913
* @summary tests error and diagnostics positions
* @author Jan Lahoda
* @modules jdk.compiler/com.sun.tools.javac.api
@ -51,6 +51,7 @@ import com.sun.source.tree.VariableTree;
import com.sun.source.tree.WhileLoopTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.TreeScanner;
import com.sun.source.util.Trees;
@ -1037,6 +1038,105 @@ public class JavacParserTest extends TestCase {
assertEquals("the error message is not correct, actual: " + actualErrors, expectedErrors, actualErrors);
}
@Test
void testTypeParamsWithoutMethod() throws IOException {
assert tool != null;
String code = "package test; class Test { /**javadoc*/ |public <T> |}";
String[] parts = code.split("\\|");
code = parts[0] + parts[1] + parts[2];
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
null, Arrays.asList(new MyFileObject(code)));
Trees trees = Trees.instance(ct);
SourcePositions pos = trees.getSourcePositions();
CompilationUnitTree cut = ct.parse().iterator().next();
ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
ErroneousTree err = (ErroneousTree) clazz.getMembers().get(0);
MethodTree method = (MethodTree) err.getErrorTrees().get(0);
final int methodStart = parts[0].length();
final int methodEnd = parts[0].length() + parts[1].length();
assertEquals("testTypeParamsWithoutMethod",
methodStart, pos.getStartPosition(cut, method));
assertEquals("testTypeParamsWithoutMethod",
methodEnd, pos.getEndPosition(cut, method));
TreePath path2Method = new TreePath(new TreePath(new TreePath(cut), clazz), method);
String javadoc = trees.getDocComment(path2Method);
if (!"javadoc".equals(javadoc)) {
throw new AssertionError("Expected javadoc not found, actual javadoc: " + javadoc);
}
}
@Test
void testAnalyzeParensWithComma1() throws IOException {
assert tool != null;
String code = "package test; class Test { FI fi = |(s, |";
String[] parts = code.split("\\|", 3);
code = parts[0] + parts[1] + parts[2];
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
null, Arrays.asList(new MyFileObject(code)));
Trees trees = Trees.instance(ct);
SourcePositions pos = trees.getSourcePositions();
CompilationUnitTree cut = ct.parse().iterator().next();
boolean[] found = new boolean[1];
new TreeScanner<Void, Void>() {
@Override
public Void visitLambdaExpression(LambdaExpressionTree tree, Void v) {
found[0] = true;
int lambdaStart = parts[0].length();
int lambdaEnd = parts[0].length() + parts[1].length();
assertEquals("testAnalyzeParensWithComma1",
lambdaStart, pos.getStartPosition(cut, tree));
assertEquals("testAnalyzeParensWithComma1",
lambdaEnd, pos.getEndPosition(cut, tree));
return null;
}
}.scan(cut, null);
assertTrue("testAnalyzeParensWithComma1", found[0]);
}
@Test
void testAnalyzeParensWithComma2() throws IOException {
assert tool != null;
String code = "package test; class Test { FI fi = |(s, o)|";
String[] parts = code.split("\\|", 3);
code = parts[0] + parts[1] + parts[2];
JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
null, Arrays.asList(new MyFileObject(code)));
Trees trees = Trees.instance(ct);
SourcePositions pos = trees.getSourcePositions();
CompilationUnitTree cut = ct.parse().iterator().next();
boolean[] found = new boolean[1];
new TreeScanner<Void, Void>() {
@Override
public Void visitLambdaExpression(LambdaExpressionTree tree, Void v) {
found[0] = true;
int lambdaStart = parts[0].length();
int lambdaEnd = parts[0].length() + parts[1].length();
assertEquals("testAnalyzeParensWithComma2",
lambdaStart, pos.getStartPosition(cut, tree));
assertEquals("testAnalyzeParensWithComma2",
lambdaEnd, pos.getEndPosition(cut, tree));
return null;
}
}.scan(cut, null);
assertTrue("testAnalyzeParensWithComma2", found[0]);
}
void run(String[] args) throws Exception {
int passed = 0, failed = 0;
final Pattern p = (args != null && args.length > 0)
@ -1082,6 +1182,12 @@ abstract class TestCase {
}
}
void assertTrue(String message, boolean bvalue) {
if (bvalue == false) {
fail(message);
}
}
void assertEquals(String message, int i, long l) {
if (i != l) {
fail(message + ":" + i + ":" + l);

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 8017216 8019422 8019421 8054956
* @bug 8017216 8019422 8019421 8054956 8205418
* @summary verify start and end positions
* @modules java.compiler
* jdk.compiler
@ -44,6 +44,13 @@ import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.util.JavacTask;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreeScanner;
import com.sun.source.util.Trees;
public class TreeEndPosTest {
private static JavaFileManager getJavaFileManager(JavaCompiler compiler,
@ -99,6 +106,15 @@ public class TreeEndPosTest {
js.endPos = end;
return js;
}
static JavaSource createFullJavaSource(String code) {
final String name = "Bug";
String[] parts = code.split("\\|", 3);
JavaSource js = new JavaSource(name + ".java", parts[0] + parts[1] + parts[2]);
js.startPos = parts[0].length();
js.endPos = parts[0].length() + parts[1].length();
return js;
}
}
public static void main(String... args) throws IOException {
@ -107,6 +123,7 @@ public class TreeEndPosTest {
testUnresolvableAnnotationAttribute();
testFinalVariableWithDefaultConstructor();
testFinalVariableWithConstructor();
testWholeTextSpan();
}
static void testUninitializedVariable() throws IOException {
@ -133,6 +150,10 @@ public class TreeEndPosTest {
"{}"));
}
static void testWholeTextSpan() throws IOException {
treeSpan(JavaSource.createFullJavaSource("|class X |"));
}
static void compile(JavaSource src) throws IOException {
ByteArrayOutputStream ba = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(ba);
@ -169,4 +190,46 @@ public class TreeEndPosTest {
}
}
}
static void treeSpan(JavaSource src) throws IOException {
ByteArrayOutputStream ba = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(ba);
File tempDir = new File(".");
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector dc = new DiagnosticCollector();
try (JavaFileManager javaFileManager = getJavaFileManager(compiler, dc)) {
List<String> options = new ArrayList<>();
options.add("-cp");
options.add(tempDir.getPath());
options.add("-d");
options.add(tempDir.getPath());
options.add("--should-stop=at=GENERATE");
List<JavaFileObject> sources = new ArrayList<>();
sources.add(src);
JavacTask task = (JavacTask) compiler.getTask(writer, javaFileManager,
dc, options, null,
sources);
SourcePositions sp = Trees.instance(task).getSourcePositions();
boolean[] found = new boolean[1];
new TreeScanner<Void, Void>() {
CompilationUnitTree cut;
@Override
public Void scan(Tree tree, Void p) {
if (tree == null)
return null;
if (tree.getKind() == Kind.COMPILATION_UNIT) {
cut = (CompilationUnitTree) tree;
}
found[0] |= (sp.getStartPosition(cut, tree) == src.startPos) &&
(sp.getEndPosition(cut, tree) == src.endPos);
return super.scan(tree, p);
}
}.scan(task.parse(), null);
if (!found[0]) {
throw new IllegalStateException();
}
}
}
}