|
64 | 64 | import com.sun.tools.javac.tree.JCTree.JCAssert;
|
65 | 65 | import com.sun.tools.javac.tree.JCTree.JCAssign;
|
66 | 66 | import com.sun.tools.javac.tree.JCTree.JCAssignOp;
|
| 67 | +import com.sun.tools.javac.tree.JCTree.JCBlock; |
67 | 68 | import com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
68 | 69 | import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
69 | 70 | import com.sun.tools.javac.tree.JCTree.JCExpression;
|
@@ -336,7 +337,7 @@ public JavaNode visitClassDef(JCClassDecl classDef, TreeContext owner) {
|
336 | 337 | // However we can't restrict ourselves to just classes contained in methods here,
|
337 | 338 | // because that would miss the case of local/anonymous classes in static/member
|
338 | 339 | // initializers. But there's no harm in emitting the same fact twice!
|
339 |
| - getScope(ctx).ifPresent(scope -> entrySets.emitEdge(classNode, EdgeKind.CHILDOF, scope)); |
| 340 | + getScope(ctx).forEach(scope -> entrySets.emitEdge(classNode, EdgeKind.CHILDOF, scope)); |
340 | 341 |
|
341 | 342 | NestingKind nestingKind = classDef.sym.getNestingKind();
|
342 | 343 | if (nestingKind != NestingKind.LOCAL
|
@@ -428,16 +429,58 @@ public JavaNode visitClassDef(JCClassDecl classDef, TreeContext owner) {
|
428 | 429 | // directly in the class body (in static initializers or member initializers).
|
429 | 430 | JavaNode node = ctx.setNode(new JavaNode(classNode));
|
430 | 431 |
|
| 432 | + List<VName> constructors = new ArrayList<>(); |
431 | 433 | for (JCTree member : classDef.getMembers()) {
|
432 |
| - JavaNode n = scan(member, ctx); |
433 |
| - if (n != null) { |
434 |
| - entrySets.emitEdge(n.getVName(), EdgeKind.CHILDOF, classNode); |
| 434 | + if (member instanceof JCMethodDecl) { |
| 435 | + JCMethodDecl method = (JCMethodDecl) member; |
| 436 | + if (!method.sym.isConstructor()) { |
| 437 | + continue; |
| 438 | + } |
| 439 | + |
| 440 | + JavaNode n = scanChild(member, ctx, classNode); |
| 441 | + if (n != null) { |
| 442 | + constructors.add(n.getVName()); |
| 443 | + } |
| 444 | + } |
| 445 | + } |
| 446 | + node.setClassConstructors(constructors); |
| 447 | + |
| 448 | + // TODO(schroeder): flesh out cinit w/ MarkedSource |
| 449 | + node.setClassInit(entrySets.newClassInitAndEmit(classNode).getVName()); |
| 450 | + entrySets.emitEdge(node.getClassInit().get(), EdgeKind.CHILDOF, classNode); |
| 451 | + |
| 452 | + for (JCTree member : classDef.getMembers()) { |
| 453 | + if (member instanceof JCMethodDecl) { |
| 454 | + JCMethodDecl method = (JCMethodDecl) member; |
| 455 | + if (method.sym.isConstructor()) { |
| 456 | + // Already handled above. |
| 457 | + continue; |
| 458 | + } |
435 | 459 | }
|
| 460 | + scanChild(member, ctx, classNode); |
436 | 461 | }
|
437 | 462 |
|
438 | 463 | return node;
|
439 | 464 | }
|
440 | 465 |
|
| 466 | + private JavaNode scanChild(JCTree child, TreeContext owner, VName parent) { |
| 467 | + JavaNode n = scan(child, owner); |
| 468 | + if (n != null) { |
| 469 | + entrySets.emitEdge(n.getVName(), EdgeKind.CHILDOF, parent); |
| 470 | + } |
| 471 | + return n; |
| 472 | + } |
| 473 | + |
| 474 | + @Override |
| 475 | + public JavaNode visitBlock(JCBlock block, TreeContext owner) { |
| 476 | + TreeContext ctx = owner; |
| 477 | + if (block.isStatic() && owner.getNode().getClassInit().isPresent()) { |
| 478 | + ctx = owner.down(block); |
| 479 | + ctx.setNode(new JavaNode(owner.getNode().getClassInit().get())); |
| 480 | + } |
| 481 | + return scan(block.getStatements(), ctx); |
| 482 | + } |
| 483 | + |
441 | 484 | @Override
|
442 | 485 | public JavaNode visitMethodDef(JCMethodDecl methodDef, TreeContext owner) {
|
443 | 486 | TreeContext ctx = owner.down(methodDef);
|
@@ -667,7 +710,7 @@ public JavaNode visitVarDef(JCVariableDecl varDef, TreeContext owner) {
|
667 | 710 | entrySets.emitEdge(varNode, EdgeKind.NAMED, jvmNode);
|
668 | 711 | }
|
669 | 712 |
|
670 |
| - getScope(ctx).ifPresent(scope -> entrySets.emitEdge(varNode, EdgeKind.CHILDOF, scope)); |
| 713 | + getScope(ctx).forEach(scope -> entrySets.emitEdge(varNode, EdgeKind.CHILDOF, scope)); |
671 | 714 | visitAnnotations(varNode, varDef.getModifiers().getAnnotations(), ctx);
|
672 | 715 |
|
673 | 716 | if (varDef.getModifiers().getFlags().contains(Modifier.STATIC)) {
|
@@ -848,7 +891,7 @@ public JavaNode visitNewClass(JCNewClass newClass, TreeContext owner) {
|
848 | 891 | emitAnchor(anchor, EdgeKind.REF, ctorNode, getScope(ctx));
|
849 | 892 |
|
850 | 893 | EntrySet callAnchor = entrySets.newAnchorAndEmit(filePositions, callSpan, ctx.getSnippet());
|
851 |
| - emitAnchor(callAnchor, EdgeKind.REF_CALL, ctorNode, getScope(ctx)); |
| 894 | + emitAnchor(callAnchor, EdgeKind.REF_CALL, ctorNode, getCallScope(ctx)); |
852 | 895 |
|
853 | 896 | scanList(newClass.getTypeArguments(), ctx);
|
854 | 897 | scanList(newClass.getArguments(), ctx);
|
@@ -1002,7 +1045,7 @@ void emitDocReference(Symbol sym, int startChar, int endChar) {
|
1002 | 1045 | filePositions.charToByteOffset(startChar), filePositions.charToByteOffset(endChar));
|
1003 | 1046 | EntrySet anchor = entrySets.newAnchorAndEmit(filePositions, loc);
|
1004 | 1047 | if (anchor != null) {
|
1005 |
| - emitAnchor(anchor, EdgeKind.REF_DOC, node, Optional.empty()); |
| 1048 | + emitAnchor(anchor, EdgeKind.REF_DOC, node, ImmutableList.of()); |
1006 | 1049 | }
|
1007 | 1050 | }
|
1008 | 1051 |
|
@@ -1180,16 +1223,27 @@ private JavaNode emitNameUsage(TreeContext ctx, Symbol sym, Name name, EdgeKind
|
1180 | 1223 | edgeKind,
|
1181 | 1224 | node.getVName(),
|
1182 | 1225 | ctx.getSnippet(),
|
1183 |
| - getScope(ctx)); |
| 1226 | + edgeKind == EdgeKind.REF_CALL ? getCallScope(ctx) : getScope(ctx)); |
1184 | 1227 | statistics.incrementCounter("name-usages-emitted");
|
1185 | 1228 | }
|
1186 | 1229 | return node;
|
1187 | 1230 | }
|
1188 | 1231 |
|
1189 |
| - private static Optional<VName> getScope(TreeContext ctx) { |
1190 |
| - return Optional.ofNullable(ctx.getClassOrMethodParent()) |
| 1232 | + private static List<VName> getCallScope(TreeContext ctx) { |
| 1233 | + TreeContext parent = ctx.getScope(); |
| 1234 | + if (parent.getTree() instanceof JCClassDecl) { |
| 1235 | + // Special-case callsites in non-static initializer blocks to scope to all constructors. |
| 1236 | + return parent.getNode().getClassConstructors(); |
| 1237 | + } |
| 1238 | + return getScope(ctx); |
| 1239 | + } |
| 1240 | + |
| 1241 | + private static List<VName> getScope(TreeContext ctx) { |
| 1242 | + return Optional.ofNullable(ctx.getScope()) |
1191 | 1243 | .map(TreeContext::getNode)
|
1192 |
| - .map(JavaNode::getVName); |
| 1244 | + .map(JavaNode::getVName) |
| 1245 | + .map(ImmutableList::of) |
| 1246 | + .orElse(ImmutableList.of()); |
1193 | 1247 | }
|
1194 | 1248 |
|
1195 | 1249 | // Returns the reference node for the given symbol.
|
@@ -1264,12 +1318,12 @@ private EntrySet emitAnchor(TreeContext anchorContext, EdgeKind kind, VName node
|
1264 | 1318 | filePositions, anchorContext.getTreeSpan(), anchorContext.getSnippet()),
|
1265 | 1319 | kind,
|
1266 | 1320 | node,
|
1267 |
| - getScope(anchorContext)); |
| 1321 | + kind == EdgeKind.REF_CALL ? getCallScope(anchorContext) : getScope(anchorContext)); |
1268 | 1322 | }
|
1269 | 1323 |
|
1270 | 1324 | // Creates/emits an anchor (for an identifier) and an associated edge
|
1271 | 1325 | private EntrySet emitAnchor(
|
1272 |
| - Name name, int startOffset, EdgeKind kind, VName node, Span snippet, Optional<VName> scope) { |
| 1326 | + Name name, int startOffset, EdgeKind kind, VName node, Span snippet, List<VName> scope) { |
1273 | 1327 | EntrySet anchor = entrySets.newAnchorAndEmit(filePositions, name, startOffset, snippet);
|
1274 | 1328 | if (anchor == null) {
|
1275 | 1329 | // TODO(schroederc): Special-case these anchors (most come from visitSelect)
|
@@ -1304,22 +1358,21 @@ private EntrySet emitDefinesBindingAnchorEdge(
|
1304 | 1358 | return anchor;
|
1305 | 1359 | }
|
1306 | 1360 |
|
1307 |
| - private void emitDefinesBindingEdge( |
1308 |
| - Span span, EntrySet anchor, VName node, Optional<VName> scope) { |
| 1361 | + private void emitDefinesBindingEdge(Span span, EntrySet anchor, VName node, List<VName> scope) { |
1309 | 1362 | emitMetadata(span, node);
|
1310 | 1363 | emitAnchor(anchor, EdgeKind.DEFINES_BINDING, node, scope);
|
1311 | 1364 | }
|
1312 | 1365 |
|
1313 | 1366 | // Creates/emits an anchor and an associated edge
|
1314 |
| - private EntrySet emitAnchor(EntrySet anchor, EdgeKind kind, VName node, Optional<VName> scope) { |
| 1367 | + private EntrySet emitAnchor(EntrySet anchor, EdgeKind kind, VName node, List<VName> scope) { |
1315 | 1368 | Preconditions.checkArgument(
|
1316 | 1369 | kind.isAnchorEdge(), "EdgeKind was not intended for ANCHORs: %s", kind);
|
1317 | 1370 | if (anchor == null) {
|
1318 | 1371 | return null;
|
1319 | 1372 | }
|
1320 | 1373 | entrySets.emitEdge(anchor.getVName(), kind, node);
|
1321 | 1374 | if (kind == EdgeKind.REF_CALL || config.getEmitAnchorScopes()) {
|
1322 |
| - scope.ifPresent(s -> entrySets.emitEdge(anchor.getVName(), EdgeKind.CHILDOF, s)); |
| 1375 | + scope.forEach(s -> entrySets.emitEdge(anchor.getVName(), EdgeKind.CHILDOF, s)); |
1323 | 1376 | }
|
1324 | 1377 | return anchor;
|
1325 | 1378 | }
|
|
0 commit comments