From 281f3dcd0e945800894e3e6af0bfb5f03276c5dd Mon Sep 17 00:00:00 2001 From: Chris Wendt Date: Thu, 24 Oct 2019 23:12:32 -0600 Subject: [PATCH 1/4] spoon --- pom.xml | 6 + src/main/java/DocumentIndexer.java | 281 ++++++++++++++--------------- src/main/java/ProjectIndexer.java | 36 ++-- 3 files changed, 160 insertions(+), 163 deletions(-) diff --git a/pom.xml b/pom.xml index d391e3f36..1c0295e88 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,12 @@ 3.14.16 + + fr.inria.gforge.spoon + spoon-core + 7.5.0 + + com.google.code.gson gson diff --git a/src/main/java/DocumentIndexer.java b/src/main/java/DocumentIndexer.java index 0a1118cdb..da5df0a79 100644 --- a/src/main/java/DocumentIndexer.java +++ b/src/main/java/DocumentIndexer.java @@ -1,29 +1,12 @@ import com.github.javaparser.Position; import com.github.javaparser.Range; -import com.github.javaparser.StaticJavaParser; -import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.body.FieldDeclaration; -import com.github.javaparser.ast.body.MethodDeclaration; -import com.github.javaparser.ast.body.Parameter; -import com.github.javaparser.ast.expr.FieldAccessExpr; -import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.expr.NameExpr; -import com.github.javaparser.ast.expr.VariableDeclarationExpr; -import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName; -import com.github.javaparser.ast.visitor.VoidVisitorAdapter; -import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration; -import com.github.javaparser.resolution.declarations.ResolvedDeclaration; -import com.github.javaparser.resolution.types.ResolvedType; -import com.github.javaparser.symbolsolver.JavaSymbolSolver; -import com.github.javaparser.symbolsolver.javaparsermodel.declarations.*; -import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; - -import java.io.File; -import java.io.FileNotFoundException; +import spoon.reflect.code.*; +import spoon.reflect.cu.SourcePosition; +import spoon.reflect.cu.position.NoSourcePosition; +import spoon.reflect.declaration.*; +import spoon.reflect.reference.CtTypeReference; +import spoon.reflect.visitor.CtScanner; + import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -34,6 +17,7 @@ public class DocumentIndexer { private String projectRoot; private boolean noContents; private String pathname; + private CtElement spoonElement; private String projectId; private Emitter emitter; private Map indexers; @@ -46,6 +30,7 @@ public DocumentIndexer( String projectRoot, boolean noContents, String pathname, + CtElement spoonElement, String projectId, Emitter emitter, Map indexers @@ -53,6 +38,7 @@ public DocumentIndexer( this.projectRoot = projectRoot; this.noContents = noContents; this.pathname = pathname; + this.spoonElement = spoonElement; this.projectId = projectId; this.emitter = emitter; this.indexers = indexers; @@ -72,10 +58,6 @@ public void index() { } private void doIndex() { - JavaSymbolSolver symbolSolver = getSymbolSolver(); - StaticJavaParser.getConfiguration().setSymbolResolver(symbolSolver); - CompilationUnit cu = parse(); - Map args = Map.of( "languageId", "java", "uri", String.format("file://%s", Paths.get(pathname).toAbsolutePath().toString()) @@ -91,7 +73,7 @@ private void doIndex() { } this.documentId = emitter.emitVertex("document", args); - cu.accept(new LSIFVisitor(symbolSolver), null); + this.spoonElement.accept(new LSIFVisitor()); } public void postIndex() { @@ -103,25 +85,6 @@ public void postIndex() { emitter.emitEdge("contains", Map.of("outV", documentId, "inVs", rangeIds.stream().sorted().toArray())); } - private JavaSymbolSolver getSymbolSolver() { - return new JavaSymbolSolver(getTypeSolver()); - } - - private TypeSolver getTypeSolver() { - CombinedTypeSolver typeSolver = new CombinedTypeSolver(); - typeSolver.add(new JavaParserTypeSolver(projectRoot)); - typeSolver.add(new ReflectionTypeSolver()); - return typeSolver; - } - - private CompilationUnit parse() { - try { - return StaticJavaParser.parse(new File(pathname)); - } catch (FileNotFoundException ex) { - throw new RuntimeException(String.format("Failed to parse %s", pathname)); - } - } - private void linkUses(DefinitionMeta meta, String documentId) { String resultId = emitter.emitVertex("referenceResult", Map.of()); @@ -143,55 +106,100 @@ private void linkUses(DefinitionMeta meta, String documentId) { } } - private class LSIFVisitor extends VoidVisitorAdapter { - private JavaSymbolSolver symbolSolver; - - public LSIFVisitor(JavaSymbolSolver symbolSolver) { - this.symbolSolver = symbolSolver; - } - + private class LSIFVisitor extends CtScanner { // TODO - field access // TODO - types to classes // TODO - enums // TODO - inner classes? @Override - public void visit(final MethodCallExpr n, final Void arg) { - super.visit(n, arg); - emitUse(n); + public void visitCtParameter(CtParameter a) { + emitDefinition(mkRange(a.getPosition()), mkDoc(a.getType(), a.getDocComment())); } @Override - public void visit(final VariableDeclarationExpr n, final Void arg) { - super.visit(n, arg); - emitDefinition(n); + public void visitCtVariableRead(CtVariableRead a) { + emitUse(mkRange(a.getPosition()), mkRange(a.getVariable().getDeclaration().getPosition()), a.getVariable().getDeclaration().getDocComment(), a.getVariable().getDeclaration().getPosition().getFile().getPath()); } @Override - public void visit(final MethodDeclaration n, final Void arg) { - super.visit(n, arg); - emitDefinition(n); + public void visitCtMethod(CtMethod a) { + super.visitCtMethod(a); + emitDefinition(nameRange(a), mkDoc(a.getType(), a.getDocComment())); } @Override - public void visit(final NameExpr n, final Void arg) { - super.visit(n, arg); - emitUse(n); + public void visitCtInvocation(CtInvocation a) { + super.visitCtInvocation(a); + if (a.getPosition() instanceof NoSourcePosition) { + return; + } + if (a.getExecutable() == null || a.getExecutable().getDeclaration() == null) { + return; + } + Range useRange = identifierRange(a, a.getExecutable().getSimpleName()); + emitUse( + useRange, + nameRange(a.getExecutable().getDeclaration()), + a.getExecutable().getDeclaration().getDocComment(), + a.getExecutable().getDeclaration().getPosition().getFile().getPath() + ); } @Override - public void visit(final Parameter n, final Void arg) { - super.visit(n, arg); - emitDefinition(n); + public void visitCtField(CtField a) { + super.visitCtField(a); + emitDefinition(nameRange(a), mkDoc(a.getType(), a.getDocComment())); } - private void emitDefinition(Node n) { - Optional rangeContainer = getRange(n); - if (!rangeContainer.isPresent()) { - throw new RuntimeException("Unexpected range-less AST node: " + n.toString()); + @Override + public void visitCtFieldRead(CtFieldRead a) { + super.visitCtFieldRead(a); + if (a.getPosition() instanceof NoSourcePosition) { + System.out.println("Skipping CtFieldRead (NoSourcePosition) " + a); + return; + } + Range useRange = identifierRange(a, a.getVariable().getSimpleName()); + CtField decl = a.getVariable().getDeclaration(); + if (decl == null) { + System.out.println("Skipping CtFieldRead (null declaration) " + a); + return; } + emitUse( + useRange, + nameRange(decl), + decl.getDocComment(), + decl.getPosition().getFile().getPath() + ); + } - Range range = rangeContainer.get(); + @Override + public void visitCtClass(CtClass a) { + super.visitCtClass(a); + emitDefinition(nameRange(a), a.getDocComment()); + } + + @Override + public void visitCtTypeReference(CtTypeReference a) { + super.visitCtTypeReference(a); + if (a.getPosition() instanceof NoSourcePosition) { + System.out.println("Skipping CtTypeReference (NoSourcePosition) " + a); + return; + } + CtType decl = a.getDeclaration(); + if (decl == null) { + System.out.println("Skipping CtFieldRead (null declaration) " + a); + return; + } + emitUse( + mkRange(a.getPosition()), + nameRange(decl), + decl.getDocComment(), + decl.getPosition().getFile().getPath() + ); + } + + private void emitDefinition(Range range, String doc) { if (definitions.containsKey(range)) { return; } @@ -199,8 +207,8 @@ private void emitDefinition(Node n) { String hoverId = emitter.emitVertex("hoverResult", Map.of( "result", Map.of( "contents", Map.of( - "language", "java", - "value", n.toString() + "kind", "markdown", + "value", doc ) ) )); @@ -214,68 +222,23 @@ private void emitDefinition(Node n) { definitions.put(range, new DefinitionMeta(rangeId, resultSetId)); // + contents? } - private void emitUse(Node n) { - ResolvedDeclaration definition; - try { - definition = symbolSolver.resolveDeclaration(n, ResolvedDeclaration.class); - } catch (RuntimeException ex) { - // ignore - return; - } - - Node definitionNode = getNode(definition); - if (definitionNode == null) { - return; - } - - Optional definitionPathContainer = definitionNode.findCompilationUnit() - .flatMap(cu -> cu.getStorage()) - .map(s -> s.getPath().toString()); + private void emitUse(Range use, Range def, String doc, String defPath) { + DocumentIndexer indexer = indexers.get(defPath); - if (definitionPathContainer.isPresent() && definitionPathContainer.get().equals(pathname)) { - emitDefinition(definitionNode); + if (defPath.equals(pathname)) { + emitDefinition(def, doc); } else { - indexers.get(definitionPathContainer.get()).index(); + indexer.index(); } - emitUse(n, definitionNode, definitionPathContainer.orElse(pathname)); - } - - private Node getNode(ResolvedDeclaration def) { - return - JavaParserClassDeclaration.class.isInstance(def) ? JavaParserClassDeclaration.class.cast(def).getWrappedNode() - : JavaParserConstructorDeclaration.class.isInstance(def) ? JavaParserConstructorDeclaration.class.cast(def).getWrappedNode() -// : JavaParserEnumConstantDeclaration.class.isInstance(def) ? JavaParserEnumConstantDeclaration.class.cast(def).getWrappedNode() -// : JavaParserEnumDeclaration.class.isInstance(def) ? JavaParserEnumDeclaration.class.cast(def).getWrappedNode() - : JavaParserFieldDeclaration.class.isInstance(def) ? JavaParserFieldDeclaration.class.cast(def).getWrappedNode() -// : JavaParserInterfaceDeclaration.class.isInstance(def) ? JavaParserInterfaceDeclaration.class.cast(def).getWrappedNode() - : JavaParserMethodDeclaration.class.isInstance(def) ? JavaParserMethodDeclaration.class.cast(def).getWrappedNode() - : JavaParserParameterDeclaration.class.isInstance(def) ? JavaParserParameterDeclaration.class.cast(def).getWrappedNode() - : JavaParserSymbolDeclaration.class.isInstance(def) ? JavaParserSymbolDeclaration.class.cast(def).getWrappedNode() -// : JavaParserTypeVariableDeclaration.class.isInstance(def) ? JavaParserTypeVariableDeclaration.class.cast(def).getWrappedNode() - : JavaParserVariableDeclaration.class.isInstance(def) ? JavaParserVariableDeclaration.class.cast(def).getWrappedNode() - : null; - } - - private void emitUse(Node n, Node definition, String definitionPath) { - DocumentIndexer indexer = indexers.get(definitionPath); - - Optional rangeContainer = getRange(n); - Optional definitionRangeContainer = getRange(definition); - - if (!rangeContainer.isPresent()) { - throw new RuntimeException("Unexpected range-less AST node: " + n.toString()); - } + System.out.println(pathname + ":" + humanRange(use) + " -> " + defPath + ":" + humanRange(def)); - if (!definitionRangeContainer.isPresent()) { - throw new RuntimeException("Unexpected range-less AST node: " + definition.toString()); + DefinitionMeta meta = indexer.definitions.get(def); + if (meta == null) { + throw new RuntimeException("BUG definition doesn't exist, usePath " + pathname + ", use " + humanRange(use) + ", defPath " + defPath + ", def " + humanRange(def)); } - Range range = rangeContainer.get(); - Range definitionRange = definitionRangeContainer.get(); - DefinitionMeta meta = indexer.definitions.get(definitionRange); - - String rangeId = emitter.emitVertex("range", createRange(range)); + String rangeId = emitter.emitVertex("range", createRange(use)); emitter.emitEdge("next", Map.of("outV", rangeId, "inV", meta.resultSetId)); if (meta.definitionResultId == null) { @@ -297,27 +260,39 @@ private void emitUse(Node n, Node definition, String definitionPath) { meta.referenceRangeIds.put(documentId, set); } - private Optional getRange(Node decl) { - if (decl.getClass().isAssignableFrom(VariableDeclarationExpr.class)) { - // Unwrap variable declarations - decl = ((VariableDeclarationExpr) decl).getVariables().get(0); // TODO - how to support multiple? - } + private Range nameRange(CtNamedElement a) { + return nameRange(a.getPosition(), a.getSimpleName()); + } - if (decl.getClass().isAssignableFrom(FieldDeclaration.class)) { - // Unwrap variable declarations - decl = ((FieldDeclaration) decl).getVariables().get(0); // TODO - how to support multiple? - } + private Range nameRange(SourcePosition a, String name) { + return Range.range( + a.getLine(), + a.getColumn(), + a.getLine(), + a.getColumn() + name.length() + ); + } - try { - // Extract only the name portion of the AST node - return ((NodeWithSimpleName) decl).getName().getRange(); - } catch (ClassCastException ex) { - // ignore - } + private Range nameRange(Range a, String name) { + return Range.range( + a.begin.line, + a.begin.column, + a.end.line, + a.end.column + name.length() + ); + } - return decl.getRange(); + private String mkDoc(CtTypeReference t, String docComment) { + return "```java\n" + t + "\n```" + (docComment.equals("") ? "" : "\n---\n" + docComment); } + private String humanRange(Range r) { + return r.begin.line + ":" + r.begin.column + "-" + r.end.line + ":" + r.end.column; + } + + private Range mkRange(SourcePosition pos) { + return Range.range(pos.getLine(), pos.getColumn(), pos.getEndLine(), pos.getEndColumn()); + } private Map createRange(Range range) { return Map.of("start", createPosition(range.begin), "end", createPosition(range.end)); @@ -326,5 +301,15 @@ private Map createRange(Range range) { private Map createPosition(Position position) { return Map.of("line", position.line - 1, "character", position.column - 1); } + + private Range identifierRange(CtTargetedExpression a, String b) { + // Ugh, we only want the range for the identifier, not the whole expression. + // This will probably break on fields/methods that are spread across lines. + // +1 for `.`, +1 because (I'm guessing) end is inclusive + SourcePosition p = a.getTarget().getPosition(); + return p instanceof NoSourcePosition ? + mkRange(a.getPosition()) : + Range.range(p.getEndLine(), p.getEndColumn() + 1 + 1, p.getEndLine(), p.getEndColumn() + 1 + 1 + b.length()); + } } } diff --git a/src/main/java/ProjectIndexer.java b/src/main/java/ProjectIndexer.java index c6f9f6567..88763c593 100644 --- a/src/main/java/ProjectIndexer.java +++ b/src/main/java/ProjectIndexer.java @@ -1,10 +1,13 @@ +import spoon.MavenLauncher; +import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtType; +import spoon.reflect.visitor.CtIterator; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -31,30 +34,33 @@ public void index() { "kind", "java" )); - List files = createFileStream() - .map(x -> x.toString()) - .filter(f -> f.endsWith(".java")) - .sorted() - .collect(Collectors.toList()); + MavenLauncher spoon = new MavenLauncher(arguments.projectRoot, MavenLauncher.SOURCE_TYPE.ALL_SOURCE); + spoon.buildModel(); + Map typeByFile = new HashMap(); + for (CtElement el : spoon.getModel().getRootPackage().getTypes()) { + System.out.println(el.getPosition().getFile().getPath()); + typeByFile.put(el.getPosition().getFile().getPath(), el); + } Map indexers = new HashMap<>(); - for (String pathname : files) { - indexers.put(pathname, new DocumentIndexer( + for (Map.Entry pathname : typeByFile.entrySet()) { + indexers.put(pathname.getKey(), new DocumentIndexer( arguments.projectRoot, arguments.noContents, - pathname, + pathname.getKey(), + pathname.getValue(), projectId, emitter, indexers )); } - for (String pathname : files) { - indexers.get(pathname).index(); + for (DocumentIndexer indexer : indexers.values()) { + indexer.index(); } - for (String pathname : files) { - indexers.get(pathname).postIndex(); + for (DocumentIndexer indexer : indexers.values()) { + indexer.postIndex(); } for (DocumentIndexer indexer : indexers.values()) { From fdbcad660a5af6a1c97bc84ab084fe6825a31ef0 Mon Sep 17 00:00:00 2001 From: Chris Wendt Date: Mon, 28 Oct 2019 16:21:35 -0600 Subject: [PATCH 2/4] all types, not just root --- src/main/java/ProjectIndexer.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/ProjectIndexer.java b/src/main/java/ProjectIndexer.java index 88763c593..3fa318513 100644 --- a/src/main/java/ProjectIndexer.java +++ b/src/main/java/ProjectIndexer.java @@ -1,5 +1,7 @@ import spoon.MavenLauncher; import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtModule; +import spoon.reflect.declaration.CtPackage; import spoon.reflect.declaration.CtType; import spoon.reflect.visitor.CtIterator; @@ -37,8 +39,8 @@ public void index() { MavenLauncher spoon = new MavenLauncher(arguments.projectRoot, MavenLauncher.SOURCE_TYPE.ALL_SOURCE); spoon.buildModel(); Map typeByFile = new HashMap(); - for (CtElement el : spoon.getModel().getRootPackage().getTypes()) { - System.out.println(el.getPosition().getFile().getPath()); + for (CtElement el : spoon.getModel().getAllTypes()) { + System.out.println("Found " + el.getPosition().getFile().getPath()); typeByFile.put(el.getPosition().getFile().getPath(), el); } From 2fb43bd7f86cb53b42ffb458370263d0a015cd3e Mon Sep 17 00:00:00 2001 From: Chris Wendt Date: Mon, 28 Oct 2019 16:22:18 -0600 Subject: [PATCH 3/4] . --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ee44a9639..67dc51000 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea target +spoon.classpath-app.tmp From 62d49665ec7576040f1cc711211f19f43552bf49 Mon Sep 17 00:00:00 2001 From: Chris Wendt Date: Mon, 28 Oct 2019 16:29:39 -0600 Subject: [PATCH 4/4] . --- src/main/java/DocumentIndexer.java | 79 +++++++++++++++--------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/src/main/java/DocumentIndexer.java b/src/main/java/DocumentIndexer.java index da5df0a79..550250a0d 100644 --- a/src/main/java/DocumentIndexer.java +++ b/src/main/java/DocumentIndexer.java @@ -107,62 +107,63 @@ private void linkUses(DefinitionMeta meta, String documentId) { } private class LSIFVisitor extends CtScanner { - // TODO - field access - // TODO - types to classes - // TODO - enums - // TODO - inner classes? + // TODO add support for more language constructs: + // https://github.com/INRIA/spoon/blob/master/src/main/java/spoon/reflect/visitor/CtScanner.java @Override - public void visitCtParameter(CtParameter a) { - emitDefinition(mkRange(a.getPosition()), mkDoc(a.getType(), a.getDocComment())); + public void visitCtParameter(CtParameter el) { + emitDefinition(mkRange(el.getPosition()), mkDoc(el.getType(), el.getDocComment())); } @Override - public void visitCtVariableRead(CtVariableRead a) { - emitUse(mkRange(a.getPosition()), mkRange(a.getVariable().getDeclaration().getPosition()), a.getVariable().getDeclaration().getDocComment(), a.getVariable().getDeclaration().getPosition().getFile().getPath()); + public void visitCtVariableRead(CtVariableRead el) { + emitUse( + mkRange(el.getPosition()), + mkRange(el.getVariable().getDeclaration().getPosition()), + el.getVariable().getDeclaration().getDocComment(), + el.getVariable().getDeclaration().getPosition().getFile().getPath() + ); } @Override - public void visitCtMethod(CtMethod a) { - super.visitCtMethod(a); - emitDefinition(nameRange(a), mkDoc(a.getType(), a.getDocComment())); + public void visitCtMethod(CtMethod el) { + super.visitCtMethod(el); + emitDefinition(nameRange(el), mkDoc(el.getType(), el.getDocComment())); } @Override - public void visitCtInvocation(CtInvocation a) { - super.visitCtInvocation(a); - if (a.getPosition() instanceof NoSourcePosition) { + public void visitCtInvocation(CtInvocation el) { + super.visitCtInvocation(el); + if (el.getPosition() instanceof NoSourcePosition) { return; } - if (a.getExecutable() == null || a.getExecutable().getDeclaration() == null) { + if (el.getExecutable() == null || el.getExecutable().getDeclaration() == null) { return; } - Range useRange = identifierRange(a, a.getExecutable().getSimpleName()); + Range useRange = identifierRange(el, el.getExecutable().getSimpleName()); emitUse( useRange, - nameRange(a.getExecutable().getDeclaration()), - a.getExecutable().getDeclaration().getDocComment(), - a.getExecutable().getDeclaration().getPosition().getFile().getPath() + nameRange(el.getExecutable().getDeclaration()), + el.getExecutable().getDeclaration().getDocComment(), + el.getExecutable().getDeclaration().getPosition().getFile().getPath() ); } @Override - public void visitCtField(CtField a) { - super.visitCtField(a); - emitDefinition(nameRange(a), mkDoc(a.getType(), a.getDocComment())); + public void visitCtField(CtField el) { + super.visitCtField(el); + emitDefinition(nameRange(el), mkDoc(el.getType(), el.getDocComment())); } @Override - public void visitCtFieldRead(CtFieldRead a) { - super.visitCtFieldRead(a); - if (a.getPosition() instanceof NoSourcePosition) { - System.out.println("Skipping CtFieldRead (NoSourcePosition) " + a); + public void visitCtFieldRead(CtFieldRead el) { + super.visitCtFieldRead(el); + if (el.getPosition() instanceof NoSourcePosition) { return; } - Range useRange = identifierRange(a, a.getVariable().getSimpleName()); - CtField decl = a.getVariable().getDeclaration(); + Range useRange = identifierRange(el, el.getVariable().getSimpleName()); + CtField decl = el.getVariable().getDeclaration(); if (decl == null) { - System.out.println("Skipping CtFieldRead (null declaration) " + a); return; } emitUse( @@ -174,25 +175,23 @@ public void visitCtFieldRead(CtFieldRead a) { } @Override - public void visitCtClass(CtClass a) { - super.visitCtClass(a); - emitDefinition(nameRange(a), a.getDocComment()); + public void visitCtClass(CtClass el) { + super.visitCtClass(el); + emitDefinition(nameRange(el), el.getDocComment()); } @Override - public void visitCtTypeReference(CtTypeReference a) { - super.visitCtTypeReference(a); - if (a.getPosition() instanceof NoSourcePosition) { - System.out.println("Skipping CtTypeReference (NoSourcePosition) " + a); + public void visitCtTypeReference(CtTypeReference el) { + super.visitCtTypeReference(el); + if (el.getPosition() instanceof NoSourcePosition) { return; } - CtType decl = a.getDeclaration(); + CtType decl = el.getDeclaration(); if (decl == null) { - System.out.println("Skipping CtFieldRead (null declaration) " + a); return; } emitUse( - mkRange(a.getPosition()), + mkRange(el.getPosition()), nameRange(decl), decl.getDocComment(), decl.getPosition().getFile().getPath() @@ -305,7 +304,7 @@ private Map createPosition(Position position) { private Range identifierRange(CtTargetedExpression a, String b) { // Ugh, we only want the range for the identifier, not the whole expression. // This will probably break on fields/methods that are spread across lines. - // +1 for `.`, +1 because (I'm guessing) end is inclusive + // +1 for `.`, +1 because (I'm guessing) end is exclusive SourcePosition p = a.getTarget().getPosition(); return p instanceof NoSourcePosition ? mkRange(a.getPosition()) :