Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix rascal hovers showing random types #262

Merged
merged 2 commits into from
Jun 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public FileFact(ISourceLocation file, Executor exec) {
r -> {
r.interrupt();
InterruptibleFuture<@Nullable SummaryBridge> summaryCalc = rascal.getSummary(file, confs.lookupConfig(file))
.thenApply(s -> s == null ? null : new SummaryBridge(s, cm));
.thenApply(s -> s == null ? null : new SummaryBridge(file, s, cm));
// only run get summary after the typechecker for this file is done running
// (we cannot now global running type checkers, that is a different subject)
CompletableFuture<@Nullable SummaryBridge> mergedCalc = typeCheckResults.get().thenCompose(o -> summaryCalc.get());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,23 @@ public SummaryBridge() {
this.typeNames = TreeMapLookup::new;
}

public SummaryBridge(IConstructor summary, ColumnMaps cm) {
public SummaryBridge(ISourceLocation self, IConstructor summary, ColumnMaps cm) {
this.data = summary.asWithKeywordParameters();
definitions = Lazy.defer(() -> translateRelation(getKWFieldSet(data, "useDef"), v -> Locations.toLSPLocation((ISourceLocation)v, cm), cm));
typeNames = Lazy.defer(() -> translateMap(getKWFieldMap(data, "locationTypes"), v -> ((IString)v).getValue(), cm));
definitions = Lazy.defer(() -> translateRelation(getKWFieldSet(data, "useDef"), self, v -> Locations.toLSPLocation((ISourceLocation)v, cm), cm));
typeNames = Lazy.defer(() -> translateMap(getKWFieldMap(data, "locationTypes"), self, v -> ((IString)v).getValue(), cm));

}

private static <T> IRangeMap<List<T>> translateRelation(ISet binaryRel, Function<IValue, T> valueMapper, ColumnMaps cm) {
private static <T> IRangeMap<List<T>> translateRelation(ISet binaryRel, ISourceLocation self, Function<IValue, T> valueMapper, ColumnMaps cm) {
TreeMapLookup<List<T>> result = new TreeMapLookup<>();
for (IValue v: binaryRel) {
ITuple row = (ITuple)v;
Range from = Locations.toRange((ISourceLocation)row.get(0), cm);
ISourceLocation fromLoc = (ISourceLocation)row.get(0);
if (!fromLoc.top().equals(self)) {
// ignore rascal-core giving us entries that are not starting with our own base
continue;
}
Range from = Locations.toRange(fromLoc, cm);
T to = valueMapper.apply(row.get(1));
List<T> existing = result.getExact(from);
if (existing == null) {
Expand All @@ -109,12 +114,16 @@ else if (existing.size() == 1) {
return result;
}

private static <T> IRangeMap<T> translateMap(IMap binaryMap, Function<IValue, T> valueMapper, ColumnMaps cm) {
private static <T> IRangeMap<T> translateMap(IMap binaryMap, ISourceLocation self, Function<IValue, T> valueMapper, ColumnMaps cm) {
TreeMapLookup<T> result = new TreeMapLookup<>();
binaryMap.entryIterator().forEachRemaining(e -> {
Range from = Locations.toRange((ISourceLocation)e.getKey(), cm);
T to = valueMapper.apply(e.getValue());
result.put(from, to);
var fromLoc = (ISourceLocation)e.getKey();
if (fromLoc.top().equals(self)) {
// only build a map for our own entries (rascal-core reports too much)
Range from = Locations.toRange(fromLoc, cm);
T to = valueMapper.apply(e.getValue());
result.put(from, to);
}
});
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private static int compareRanges(Range a, Range b) {
}
return Integer.compare(aEnd.getLine(), bEnd.getLine());
}
return Integer.compare(aStart.getLine(), aStart.getLine());
return Integer.compare(aStart.getLine(), bStart.getLine());
}

private static boolean rangeContains(Range a, Range b) {
Expand Down Expand Up @@ -101,12 +101,18 @@ private static boolean rangeContains(Range a, Range b) {

@Override
public @Nullable T lookup(Range from) {
T result = contains(data.floorEntry(from), from);
if (result == null) {
// could be that it's at the start of the entry
result = contains(data.ceilingEntry(from), from);
// since we allow for overlapping ranges, it might be that we have to
// search all the way to the "bottom" of the tree to see if we are
// contained in something larger than the closest key
var previousKeys = data.headMap(from, true).descendingMap();
for (var candidate : previousKeys.entrySet()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather read the type of candidate here instead of var. I'm getting lost. Same for previousKeys.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't really see the value, descendingMap becomes a NavigatableMap<Range, T> and candidated Entry<Range, T>. But for me the readability doesn't improve by it, but that might be because I wrote the code. I would prefer to give better names to the variables.

T result = contains(candidate, from);
if (result != null) {
return result;
}
}
return result;
// could be that it's at the start of the entry (so the entry it not in the head map)
return contains(data.ceilingEntry(from), from);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
package engineering.swat.rascal.lsp.util;

import static org.junit.jupiter.api.Assertions.assertSame;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -121,4 +123,42 @@ public void testSimpleLookupEnd2() {
assertSame("hit2", target.lookup(cursor(1,12)));
}

@Test
public void testOverlappingRanges() {
TreeMapLookup<String> target = new TreeMapLookup<>();
target.put(new Range(new Position(0,1), new Position(4,5)), "big1");
target.put(new Range(new Position(1,1), new Position(1,5)), "small1");
target.put(new Range(new Position(4,1), new Position(4,4)), "small2");
assertSame("big1", target.lookup(cursor(0,12)));
assertSame("big1", target.lookup(cursor(1,12)));
assertSame("small1", target.lookup(cursor(1,4)));
assertSame("small2", target.lookup(cursor(4,4)));
assertSame("big1", target.lookup(cursor(4,5)));
}

@Test
public void randomRanges() {
TreeMapLookup<String> target = new TreeMapLookup<>();
Map<Range, String> ranges = new HashMap<>();
Random r = new Random();
for (int i = 0; i < 1000; i++) {
int startLine = r.nextInt(10);
int startColumn = r.nextInt(10);
int endLine = startLine + r.nextInt(3);
int endColumn;
if (endLine == startLine) {
endColumn = startColumn + r.nextInt(3);
}
else {
endColumn = r.nextInt(10);
}
ranges.put(range(startLine, startColumn, endLine, endColumn), "" + i);
}
ranges.forEach(target::put);
for (var e: ranges.entrySet()) {
var found = target.lookup(e.getKey());
assertSame(e.getValue(), found, "Entry " + e + "should be found");
}
}

}