Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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 @@ -31,6 +31,7 @@

import java.io.IOException;
import java.io.StringReader;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand All @@ -40,6 +41,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down Expand Up @@ -130,17 +132,98 @@
this.workspaceService = workspaceService;
}

public InterruptibleFuture<@Nullable IConstructor> getSummary(ISourceLocation occ, PathConfig pcfg) {
static String pathToModuleName(ISourceLocation l) throws URISyntaxException {

Check warning on line 135 in rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of thrown exception 'java.net.URISyntaxException', as it cannot be thrown from method's body.

See more on https://sonarcloud.io/project/issues?id=usethesource_rascal-language-servers&issues=AZ2L6P1S7NPnFptr7nj7&open=AZ2L6P1S7NPnFptr7nj7&pullRequest=1053
var p = l.getPath();
if ("jar+file".equals(l.getScheme())) {

Check failure on line 137 in rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "jar+file" 5 times.

See more on https://sonarcloud.io/project/issues?id=usethesource_rascal-language-servers&issues=AZ2L4oktmeQWhAvRq5yq&open=AZ2L4oktmeQWhAvRq5yq&pullRequest=1053
p = jarFilePath(l);
}
return p.substring(1, p.lastIndexOf('.')).replace("/", "::");
}

static @Nullable ISourceLocation libraryTplLocation(ISourceLocation modPath) {
try {
IString moduleName = VF.string(pcfg.getModuleName(occ));
return runEvaluator("Rascal makeSummary", semanticEvaluator, eval -> {
IConstructor result = (IConstructor) eval.call("makeSummary", moduleName, pcfg.asConstructor());
return result != null && result.asWithKeywordParameters().hasParameters() ? result : null;
}, null, exec, false, client);
} catch (IOException e) {
logger.error("Error looking up module name from source location {}", occ, e);
return InterruptibleFuture.completedFuture(null, exec);
var tplFolder = libraryTplRoot(modPath);
Comment thread
toinehartman marked this conversation as resolved.
if (tplFolder != null) {
var modPrefix = libraryModulePrefix(modPath);
var tplFileName = "$" + URIUtil.getLocationName(URIUtil.changeExtension(modPath, "tpl"));
return URIUtil.getChildLocation(URIUtil.getChildLocation(tplFolder, modPrefix), tplFileName);
}
} catch (URISyntaxException e) {
logger.error("Error while finding TPL for {}", modPath, e);
}
return null;
}

private static @Nullable ISourceLocation libraryTplRoot(ISourceLocation modPath) throws URISyntaxException {
modPath = Locations.toPhysicalIfPossible(modPath); // resolve logical paths like `std:///`
switch (modPath.getScheme()) {
case "jar+file": {
return URIUtil.getChildLocation(jarBasePath(modPath), "rascal");
}
case "mvn": {
return URIUtil.changePath(modPath, "rascal");
}
default: return null;
}
}

private static String libraryModulePrefix(ISourceLocation modPath) throws URISyntaxException {

Check warning on line 170 in rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of thrown exception 'java.net.URISyntaxException', as it cannot be thrown from method's body.

See more on https://sonarcloud.io/project/issues?id=usethesource_rascal-language-servers&issues=AZ2L6P1S7NPnFptr7nj8&open=AZ2L6P1S7NPnFptr7nj8&pullRequest=1053
modPath = URIUtil.getParentLocation(modPath);
if ("jar+file".equals(modPath.getScheme())) {
// For a file within a JAR, return the sub-path within the JAR
return jarFilePath(modPath);
}
// Otherwise, return just the sub-path
return modPath.getPath();
}

private static ISourceLocation jarBasePath(ISourceLocation l) throws URISyntaxException {
if (!"jar+file".equals(l.getScheme())) {
Comment thread
toinehartman marked this conversation as resolved.
Outdated
throw new IllegalArgumentException("Location should have scheme jar+file: " + l);
}

var path = l.getPath();
return URIUtil.changePath(l, path.substring(0, path.lastIndexOf('!') + 1));
}

private static String jarFilePath(ISourceLocation l) {
if (!"jar+file".equals(l.getScheme())) {
throw new IllegalArgumentException("Location should have scheme jar+file: " + l);
}

var path = l.getPath();
return path.substring(path.lastIndexOf('!') + 1);
}

public InterruptibleFuture<@Nullable IConstructor> getSummary(ISourceLocation occ, Function<ISourceLocation, PathConfig> computePathConfig) {
Function<Evaluator, @Nullable IConstructor> computeSummary;
var tplLoc = libraryTplLocation(occ);
if (tplLoc != null) {
computeSummary = eval -> {
try {
return (IConstructor) eval.call("makeSummary", VF.string(pathToModuleName(occ)), tplLoc);
} catch (URISyntaxException e) {
logger.error("Error looking up module name for source location {}", occ, e);
return null;
}
};
} else {
computeSummary = eval -> {
try {
var pcfg = computePathConfig.apply(occ);
var moduleName = VF.string(pcfg.getModuleName(occ));

Check warning on line 214 in rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "getModuleName"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=usethesource_rascal-language-servers&issues=AZ2G5hBX1InwAbV1eQ17&open=AZ2G5hBX1InwAbV1eQ17&pullRequest=1053
return (IConstructor) eval.call("makeSummary", moduleName, pcfg.asConstructor());
} catch (IOException e) {
logger.error("Error looking up module name for source location {}", occ, e);
return null;
}
};
}

return runEvaluator("Rascal makeSummary", semanticEvaluator, eval -> {
var result = computeSummary.apply(eval);
return result != null && result.asWithKeywordParameters().hasParameters() ? result : null;
}, null, exec, false, client);
}

private static Map<ISourceLocation, ISet> translateCheckResults(IMap messages) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public ActualFileFact(ISourceLocation file, Executor exec) {
// only run get summary after the typechecker for this file is done running, because it needs the TPL
// (we cannot now global running type checkers, that is a different subject)
return InterruptibleFuture.flatten(typeCheckResults.get()
.<InterruptibleFuture<@Nullable IConstructor>>thenApply(o -> rascal.getSummary(file, confs.lookupConfig(file))), exec)
.<InterruptibleFuture<@Nullable IConstructor>>thenApply(o -> rascal.getSummary(file, confs::lookupConfig)), exec)
.<@Nullable SummaryBridge>thenApply(s -> s == null ? null : new SummaryBridge(file, s, cm));
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2018-2025, NWO-I CWI and Swat.engineering
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.rascalmpl.vscode.lsp.rascal;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.net.URISyntaxException;
import org.junit.Test;
import org.rascalmpl.uri.URIUtil;
import org.rascalmpl.values.IRascalValueFactory;

import io.usethesource.vallang.ISourceLocation;

public class RascalLanguageServicesTest {

private static final IRascalValueFactory VF = IRascalValueFactory.getInstance();

private void moduleNameTest(ISourceLocation sourcePath, String modPath, String modName) throws URISyntaxException {
var actualName = RascalLanguageServices.pathToModuleName(URIUtil.getChildLocation(sourcePath, modPath));
assertEquals(modName, actualName);
}

@Test
public void stdModuleName() throws URISyntaxException {
moduleNameTest(URIUtil.rootLocation("std"), "IO.rsc", "IO");
moduleNameTest(URIUtil.rootLocation("std"), "util/Maybe.rsc", "util::Maybe");
}

@Test
public void mvnModuleName() throws URISyntaxException {
moduleNameTest(VF.sourceLocation("mvn", "org.rascalmpl--rascal--0.42.2", ""), "String.rsc", "String");
moduleNameTest(VF.sourceLocation("mvn", "org.rascalmpl--typepal--0.16.6", ""), "analysis/typepal/Collector.rsc", "analysis::typepal::Collector");
}

@Test
public void jarModuleName() throws URISyntaxException {
moduleNameTest(VF.sourceLocation("jar+file", "", "rascal-lsp.jar!/"), "util/LanguageServer.rsc", "util::LanguageServer");
}

@Test
public void stdTplLoc() throws URISyntaxException {
var src = VF.sourceLocation("std", "", "util/Maybe.rsc");
var actualTpl = RascalLanguageServices.libraryTplLocation(src);
assertEquals("jar+file", actualTpl.getScheme());
assertTrue(actualTpl.getPath().endsWith(".jar!/rascal/util/$Maybe.tpl"));
}

@Test
public void mvnTplLoc() throws URISyntaxException {
var src = VF.sourceLocation("mvn", "org.rascalmpl--typepal--0.16.6", "analysis/typepal/Collector.rsc");
var actualTpl = RascalLanguageServices.libraryTplLocation(src);
assertEquals(VF.sourceLocation("mvn", "org.rascalmpl--typepal--0.16.6", "rascal/analysis/typepal/$Collector.tpl"), actualTpl);
}

@Test
public void jarFileTplLoc() throws URISyntaxException {
var src = VF.sourceLocation("jar+file", "", "some/path/to/rascal-lsp.jar!/util/LanguageServer.rsc");
var actualTpl = RascalLanguageServices.libraryTplLocation(src);
assertEquals(VF.sourceLocation("jar+file", "", "some/path/to/rascal-lsp.jar!/rascal/util/$LanguageServer.tpl"), actualTpl);
}

}
Loading