Skip to content

Commit 9e9451d

Browse files
committed
Add -no-relative-path: flag to control indexing of generated files
Previously, the SemanticDB compiler plugin errored when indexing auto-generated files outside of the configured `-sourceroot` directory (which is automatically inferred for Bazel builds). This behavior was undesirable because: - There's no good workaround for the issue - The error message was cryptic making it difficult to understand what went wrong For some cases, we were able to detect this situation for Bazel and ignore the indexed file while printing an informative message, but this behavior was also undesirable because we skipping these files means that we can't render hover messages for symbols in those generated files. This PR fixes the issue by adding a configurable `-no-relative-path:` flag with the following valid options: - `index_anyways` (default): indexes the file but with no guarantee that it's possible to recover the location of the original generated file. This allows us to display accurate hover tooltips for symbols in these files even if "Go to definition" won't work. - `skip`: silently ignored these files. - `warning`: ignore these files and print a message explaining it was skipped. - `error`: fail the compilation process (old default).
1 parent 5028089 commit 9e9451d

File tree

5 files changed

+117
-40
lines changed

5 files changed

+117
-40
lines changed

semanticdb-javac/defs.bzl

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
"""Java rules that automatically register the SemanticDB compiler plugin based on the presence of a string flag."""
2-
load("@rules_java//java:defs.bzl", native_java_library="java_library", native_java_binary="java_binary")
32

4-
def java_library(javacopts=[], plugins=[],**kwargs):
5-
native_java_library(
6-
javacopts=_actual_javacopts(javacopts),
7-
plugins=_actual_plugins(plugins),
8-
**kwargs)
3+
load("@rules_java//java:defs.bzl", native_java_binary = "java_binary", native_java_library = "java_library")
94

5+
def java_library(javacopts = [], plugins = [], **kwargs):
6+
native_java_library(
7+
javacopts = _actual_javacopts(javacopts),
8+
plugins = _actual_plugins(plugins),
9+
**kwargs
10+
)
1011

11-
def java_binary(javacopts=[], plugins=[],**kwargs):
12+
def java_binary(javacopts = [], plugins = [], **kwargs):
1213
native_java_binary(
13-
javacopts=_actual_javacopts(javacopts),
14-
plugins=_actual_plugins(plugins),
15-
**kwargs)
14+
javacopts = _actual_javacopts(javacopts),
15+
plugins = _actual_plugins(plugins),
16+
**kwargs
17+
)
1618

1719
def _actual_javacopts(javacopts):
1820
return select({
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.sourcegraph.semanticdb_javac;
2+
3+
/**
4+
* Configuration options to determine how semanticdb-javac should handle files that have no good
5+
* relative paths.
6+
*
7+
* <p>When indexing a file at an absolute path /project/src/main/example/Foo.java, SemanticDB needs
8+
* to know the "sourceroot" path /project in order to relativize the path of the source file into
9+
* src/main/example/Foo.java. This configuration option determines what the compiler plugin should
10+
* do when it's not able to find a good relative path.
11+
*/
12+
public enum NoRelativePathMode {
13+
14+
/**
15+
* Come up with a unique relative path for the SemanticDB path with no guarantee that this path
16+
* corresponds to the original path of the generated source file.
17+
*/
18+
INDEX_ANYWAY,
19+
20+
/** Silently ignore the file, don't index it. */
21+
SKIP,
22+
23+
/** Report an error message and fail the compilation. */
24+
ERROR,
25+
26+
/** Ignore the file, but print a message explaining it's ignored. */
27+
WARNING
28+
}

semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbJavacOptions.java

+28
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import java.nio.file.Path;
66
import java.nio.file.Paths;
77
import java.util.ArrayList;
8+
import java.util.Arrays;
9+
import java.util.stream.Collectors;
810
import javax.tools.FileObject;
911
import javax.tools.JavaFileManager;
1012
import javax.tools.JavaFileObject;
@@ -26,6 +28,7 @@ public class SemanticdbJavacOptions {
2628
public final ArrayList<String> errors;
2729
public boolean alreadyReportedErrors = false;
2830
public UriScheme uriScheme = UriScheme.DEFAULT;
31+
public NoRelativePathMode noRelativePath = NoRelativePathMode.INDEX_ANYWAY;
2932
public Path generatedTargetRoot;
3033

3134
public static String stubClassName = "META-INF-semanticdb-stub";
@@ -60,6 +63,31 @@ public static SemanticdbJavacOptions parse(String[] args, Context ctx) {
6063
result.sourceroot = Paths.get(arg.substring("-sourceroot:".length())).normalize();
6164
} else if (arg.equals("-build-tool:sbt") || arg.equals("-build-tool:mill")) {
6265
result.uriScheme = UriScheme.ZINC;
66+
} else if (arg.startsWith("-no-relative-path:")) {
67+
String value = arg.substring("-no-relative-path:".length());
68+
switch (value) {
69+
case "index_anyway":
70+
result.noRelativePath = NoRelativePathMode.INDEX_ANYWAY;
71+
break;
72+
case "skip":
73+
result.noRelativePath = NoRelativePathMode.SKIP;
74+
break;
75+
case "error":
76+
result.noRelativePath = NoRelativePathMode.ERROR;
77+
break;
78+
case "warning":
79+
result.noRelativePath = NoRelativePathMode.WARNING;
80+
break;
81+
default:
82+
String validValues =
83+
Arrays.stream(NoRelativePathMode.values())
84+
.map(NoRelativePathMode::toString)
85+
.collect(Collectors.joining(", "));
86+
result.errors.add(
87+
String.format(
88+
"unknown -no-relative-path mode '%s'. Valid values are %s.",
89+
value, validValues));
90+
}
6391
} else if (arg.equals("-build-tool:bazel")) {
6492
result.uriScheme = UriScheme.BAZEL;
6593
useJavacClassesDir = true;

semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbTaskListener.java

+40-28
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import com.sun.source.util.Trees;
77
import com.sun.tools.javac.model.JavacTypes;
88

9-
import javax.tools.Diagnostic;
109
import javax.tools.JavaFileObject;
1110
import java.io.IOException;
1211
import java.net.URI;
@@ -25,7 +24,7 @@ public final class SemanticdbTaskListener implements TaskListener {
2524
private final SemanticdbReporter reporter;
2625
private final JavacTypes javacTypes;
2726
private final Trees trees;
28-
private boolean sourceGeneratorsMessageIsLogged = false;
27+
private int noRelativePathCounter = 0;
2928

3029
public SemanticdbTaskListener(
3130
SemanticdbJavacOptions options,
@@ -130,7 +129,11 @@ public static Path absolutePathFromUri(SemanticdbJavacOptions options, JavaFileO
130129
new String[] {"SimpleFileObject[", "DirectoryFileObject["};
131130
for (String pattern : knownBazelToStringPatterns) {
132131
if (toString.startsWith(pattern) && toString.endsWith("]")) {
133-
return Paths.get(toString.substring(pattern.length(), toString.length() - 1));
132+
Path path = Paths.get(toString.substring(pattern.length(), toString.length() - 1));
133+
if (path.isAbsolute()) {
134+
return path;
135+
}
136+
return options.sourceroot.resolve(path);
134137
}
135138
}
136139
throw new IllegalArgumentException("unsupported source file: " + toString);
@@ -190,32 +193,41 @@ private Result<Path, String> semanticdbOutputPath(SemanticdbJavacOptions options
190193
.resolve(relativePath)
191194
.resolveSibling(filename);
192195
return Result.ok(semanticdbOutputPath);
193-
} else {
194-
195-
if (options.uriScheme == UriScheme.BAZEL && options.generatedTargetRoot != null) {
196-
try {
197-
if (absolutePath.toRealPath().startsWith(options.generatedTargetRoot)) {
198-
if (!sourceGeneratorsMessageIsLogged) {
199-
sourceGeneratorsMessageIsLogged = true;
200-
reporter.info(
201-
"Usage of source generators detected - scip-java does not produce SemanticDB files for generated files.\n"
202-
+ "This message is logged only once",
203-
e);
204-
}
205-
206-
return null;
207-
}
208-
} catch (IOException exc) {
209-
reporter.exception(exc, e);
210-
return null;
211-
}
212-
}
196+
}
213197

214-
return Result.error(
215-
String.format(
216-
"sourceroot '%s does not contain path '%s'. To fix this problem, update the -sourceroot flag to "
217-
+ "be a parent directory of this source file.",
218-
options.sourceroot, absolutePath));
198+
switch (options.noRelativePath) {
199+
case INDEX_ANYWAY:
200+
// Come up with a unique relative path for this file even if it's not under the sourceroot.
201+
// By indexing auto-generated files, we collect SymbolInformation for auto-generated symbol,
202+
// which results in more useful hover tooltips in the editor.
203+
// In the future, we may want to additionally embed the full text contents of these files
204+
// so that it's possible to browse generated files with precise code navigation.
205+
String uniqueFilename =
206+
String.format("%d.%s.semanticdb", ++noRelativePathCounter, absolutePath.getFileName());
207+
Path semanticdbOutputPath =
208+
options
209+
.targetroot
210+
.resolve("META-INF")
211+
.resolve("semanticdb")
212+
.resolve("no-relative-path")
213+
.resolve(uniqueFilename);
214+
return Result.ok(semanticdbOutputPath);
215+
case WARNING:
216+
reporter.info(
217+
String.format(
218+
"Skipping file '%s' because it is not under the sourceroot '%s'",
219+
absolutePath, options.sourceroot),
220+
e);
221+
case SKIP:
222+
return null;
223+
case ERROR:
224+
default:
225+
return Result.error(
226+
String.format(
227+
"Unable to detect the relative path of '%s'. A common reason for this error is that the file is that this file is auto-generated. "
228+
+ "To fix this problem, either configure the -sourceroot:PATH flag to be the parent directory of all indexed files, or "
229+
+ "configure -no-relative-path:VALUE flag to have one of the following values: index_anyway, skip, warning.",
230+
absolutePath));
219231
}
220232
}
221233
}

semanticdb-javac/src/main/java/com/sourcegraph/semanticdb_javac/SemanticdbVisitor.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -436,9 +436,12 @@ private Semanticdb.Access semanticdbAccess(Symbol sym) {
436436

437437
private String semanticdbUri() {
438438
Path absolutePath = SemanticdbTaskListener.absolutePathFromUri(options, event.getSourceFile());
439-
Path relativePath = options.sourceroot.relativize(absolutePath);
439+
Path uriPath =
440+
absolutePath.startsWith(options.sourceroot)
441+
? options.sourceroot.relativize(absolutePath)
442+
: absolutePath;
440443
StringBuilder out = new StringBuilder();
441-
Iterator<Path> it = relativePath.iterator();
444+
Iterator<Path> it = uriPath.iterator();
442445
if (it.hasNext()) out.append(it.next().getFileName().toString());
443446
while (it.hasNext()) {
444447
Path part = it.next();
@@ -447,6 +450,10 @@ private String semanticdbUri() {
447450
return out.toString();
448451
}
449452

453+
private Path semanticdbPath() {
454+
return SemanticdbTaskListener.absolutePathFromUri(options, event.getSourceFile());
455+
}
456+
450457
private Semanticdb.Documentation semanticdbDocumentation(Symbol sym) {
451458
try {
452459
Elements elements = task.getElements();

0 commit comments

Comments
 (0)