Skip to content

Commit 2b21f9f

Browse files
feat: Add flag to indicate gem name + version.
1 parent 81d9de2 commit 2b21f9f

File tree

4 files changed

+113
-13
lines changed

4 files changed

+113
-13
lines changed

scip_indexer/SCIPIndexer.cc

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "absl/status/status.h"
1414
#include "absl/status/statusor.h"
1515
#include "absl/strings/str_cat.h"
16+
#include "absl/strings/str_split.h"
1617
#include "absl/synchronization/mutex.h"
1718
#include "spdlog/fmt/fmt.h"
1819

@@ -127,6 +128,32 @@ struct OwnedLocal {
127128
}
128129
};
129130

131+
class GemMetadata final {
132+
string _name;
133+
string _version;
134+
135+
GemMetadata(string name, string version) : _name(name), _version(version) {}
136+
137+
public:
138+
GemMetadata &operator=(const GemMetadata &) = default;
139+
140+
static GemMetadata tryParseOrDefault(string metadata) {
141+
vector<string> v = absl::StrSplit(metadata, '@');
142+
if (v.size() != 2 || v[0].empty() || v[1].empty()) {
143+
return GemMetadata{"TODO", "TODO"};
144+
}
145+
return GemMetadata{v[0], v[1]};
146+
}
147+
148+
const std::string &name() const {
149+
return this->_name;
150+
}
151+
152+
const std::string &version() const {
153+
return this->_version;
154+
}
155+
};
156+
130157
// A wrapper type to handle both top-level symbols (like classes) as well as
131158
// "inner symbols" like fields (@x). In a statically typed language, field
132159
// symbols are like any other symbols, but in Ruby, they aren't declared
@@ -212,12 +239,12 @@ class NamedSymbolRef final {
212239
}
213240

214241
// Returns OK if we were able to compute a symbol for the expression.
215-
absl::Status symbolForExpr(const core::GlobalState &gs, scip::Symbol &symbol) const {
242+
absl::Status symbolForExpr(const core::GlobalState &gs, const GemMetadata &metadata, scip::Symbol &symbol) const {
216243
// Don't set symbol.scheme and package.manager here because those are hard-coded to 'scip-ruby' and 'gem'
217244
// anyways.
218245
scip::Package package;
219-
package.set_name("TODO");
220-
package.set_version("TODO");
246+
package.set_name(metadata.name());
247+
package.set_version(metadata.version());
221248
*symbol.mutable_package() = move(package);
222249

223250
InlinedVector<scip::Descriptor, 4> descriptors;
@@ -319,6 +346,7 @@ core::Loc trimColonColonPrefix(const core::GlobalState &gs, core::Loc baseLoc) {
319346
class SCIPState {
320347
string symbolScratchBuffer;
321348
UnorderedMap<NamedSymbolRef, string> symbolStringCache;
349+
GemMetadata gemMetadata;
322350

323351
public:
324352
UnorderedMap<core::FileRef, vector<scip::Occurrence>> occurrenceMap;
@@ -337,7 +365,7 @@ class SCIPState {
337365
vector<scip::SymbolInformation> externalSymbols;
338366

339367
public:
340-
SCIPState() = default;
368+
SCIPState(GemMetadata metadata) : symbolScratchBuffer(), symbolStringCache(), gemMetadata(metadata) {}
341369
~SCIPState() = default;
342370
SCIPState(SCIPState &&) = default;
343371
SCIPState &operator=(SCIPState &&other) = default;
@@ -362,7 +390,7 @@ class SCIPState {
362390
status = scip::utils::emitSymbolString(*symbol, this->symbolScratchBuffer);
363391
} else {
364392
scip::Symbol symbol;
365-
status = symRef.symbolForExpr(gs, symbol);
393+
status = symRef.symbolForExpr(gs, this->gemMetadata, symbol);
366394
if (!status.ok()) {
367395
return status;
368396
}
@@ -459,7 +487,7 @@ class SCIPState {
459487
std::optional<core::LocOffsets> loc = std::nullopt) {
460488
// TODO:(varun) Should we cache here too to avoid emitting duplicate definitions?
461489
scip::Symbol symbol;
462-
auto status = symRef.symbolForExpr(gs, symbol);
490+
auto status = symRef.symbolForExpr(gs, this->gemMetadata, symbol);
463491
if (!status.ok()) {
464492
return status;
465493
}
@@ -954,6 +982,7 @@ using LocalSymbolTable = UnorderedMap<core::LocalVariable, core::Loc>;
954982
class SCIPSemanticExtension : public SemanticExtension {
955983
public:
956984
string indexFilePath;
985+
scip_indexer::GemMetadata gemMetadata;
957986

958987
using SCIPState = sorbet::scip_indexer::SCIPState;
959988

@@ -977,22 +1006,35 @@ class SCIPSemanticExtension : public SemanticExtension {
9771006
//
9781007
// We will move the state out later, so use a no-op deleter.
9791008
return mutableState.states[this_thread::get_id()] =
980-
shared_ptr<SCIPState>(new SCIPState(), [](SCIPState *) {});
1009+
shared_ptr<SCIPState>(new SCIPState(gemMetadata), [](SCIPState *) {});
9811010
}
9821011
}
9831012

9841013
void emitSymbol(const core::GlobalState &gs, core::FileRef file, ast::ClassDef *cd) const {
9851014
auto classLoc = core::Loc(file, cd->name.loc());
9861015
}
9871016

1017+
bool doNothing() const {
1018+
return this->indexFilePath.empty();
1019+
}
1020+
9881021
void run(core::MutableContext &ctx, ast::ClassDef *cd) const override {
1022+
if (this->doNothing()) {
1023+
return;
1024+
}
9891025
// FIXME:(varun) This is a no-op???
9901026
emitSymbol(ctx.state, ctx.file, cd);
9911027
};
9921028
virtual void finishTypecheckFile(const core::GlobalState &gs, const core::FileRef &file) const override {
1029+
if (this->doNothing()) {
1030+
return;
1031+
}
9931032
getSCIPState()->saveDocument(gs, file);
9941033
};
9951034
virtual void finishTypecheck(const core::GlobalState &gs) const override {
1035+
if (this->doNothing()) {
1036+
return;
1037+
}
9961038
scip::ToolInfo toolInfo;
9971039
toolInfo.set_name("scip-ruby");
9981040
toolInfo.set_version(sorbet_version);
@@ -1045,6 +1087,9 @@ class SCIPSemanticExtension : public SemanticExtension {
10451087

10461088
virtual void typecheck(const core::GlobalState &gs, core::FileRef file, cfg::CFG &cfg,
10471089
ast::MethodDef &methodDef) const override {
1090+
if (this->doNothing()) {
1091+
return;
1092+
}
10481093
auto scipState = this->getSCIPState();
10491094
if (methodDef.name != core::Names::staticInit()) {
10501095
auto status = scipState->saveDefinition(gs, file, scip_indexer::NamedSymbolRef::method(methodDef.symbol));
@@ -1067,11 +1112,12 @@ class SCIPSemanticExtension : public SemanticExtension {
10671112
}
10681113

10691114
virtual unique_ptr<SemanticExtension> deepCopy(const core::GlobalState &from, core::GlobalState &to) override {
1070-
return make_unique<SCIPSemanticExtension>(this->indexFilePath);
1115+
return make_unique<SCIPSemanticExtension>(this->indexFilePath, this->gemMetadata);
10711116
};
10721117
virtual void merge(const core::GlobalState &from, core::GlobalState &to, core::NameSubstitution &subst) override{};
10731118

1074-
SCIPSemanticExtension(string indexFilePath) : indexFilePath(indexFilePath), mutableState() {}
1119+
SCIPSemanticExtension(string indexFilePath, scip_indexer::GemMetadata metadata)
1120+
: indexFilePath(indexFilePath), gemMetadata(metadata), mutableState() {}
10751121
~SCIPSemanticExtension() {}
10761122
};
10771123

@@ -1080,15 +1126,21 @@ class SCIPSemanticExtensionProvider : public SemanticExtensionProvider {
10801126
void injectOptions(cxxopts::Options &optsBuilder) const override {
10811127
optsBuilder.add_options("indexer")("index-file", "Output SCIP index to a directory, which must already exist",
10821128
cxxopts::value<string>());
1129+
optsBuilder.add_options("name@version")(
1130+
"gem-metadata", "Name and version pair to be used for cross-repository code navigation.",
1131+
cxxopts::value<string>());
10831132
};
10841133
unique_ptr<SemanticExtension> readOptions(cxxopts::ParseResult &providedOptions) const override {
10851134
if (providedOptions.count("index-file") > 0) {
1086-
return make_unique<SCIPSemanticExtension>(providedOptions["index-file"].as<string>());
1135+
return make_unique<SCIPSemanticExtension>(
1136+
providedOptions["index-file"].as<string>(),
1137+
scip_indexer::GemMetadata::tryParseOrDefault(
1138+
providedOptions.count("gem-metadata") > 0 ? providedOptions["gem-metadata"].as<string>() : ""));
10871139
}
10881140
return this->defaultInstance();
10891141
};
10901142
virtual unique_ptr<SemanticExtension> defaultInstance() const override {
1091-
return make_unique<SCIPSemanticExtension>("index.scip");
1143+
return make_unique<SCIPSemanticExtension>("", scip_indexer::GemMetadata::tryParseOrDefault(""));
10921144
};
10931145
static vector<SemanticExtensionProvider *> getProviders();
10941146
virtual ~SCIPSemanticExtensionProvider() = default;

test/scip/testdata/gem_metadata.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# typed: true
2+
# gem-metadata: [email protected]
3+
4+
class C
5+
def m
6+
n
7+
end
8+
def n
9+
m
10+
end
11+
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# typed: true
2+
# gem-metadata: [email protected]
3+
4+
class C
5+
# ^ definition scip-ruby gem leet 1.3.3.7 C#
6+
def m
7+
# ^^^^^ definition scip-ruby gem leet 1.3.3.7 C#m().
8+
n
9+
# ^ reference scip-ruby gem leet 1.3.3.7 C#n().
10+
end
11+
def n
12+
# ^^^^^ definition scip-ruby gem leet 1.3.3.7 C#n().
13+
m
14+
# ^ reference scip-ruby gem leet 1.3.3.7 C#m().
15+
end
16+
end

test/scip_test_runner.cc

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "absl/strings/match.h"
1717
#include "absl/strings/str_replace.h"
18+
#include "absl/strings/str_split.h"
1819
#include "spdlog/sinks/stdout_color_sinks.h"
1920

2021
#include "ast/ast.h"
@@ -278,11 +279,25 @@ void compareSnapshots(const scip::Index &index, const std::filesystem::path &sna
278279
}
279280
}
280281

282+
optional<string> readGemMetadataFromComment(string_view path) {
283+
ifstream input(path);
284+
for (string line; getline(input, line);) {
285+
if (absl::StrContains(line, "# gem-metadata: ")) {
286+
auto s = absl::StripPrefix(line, "# gem-metadata: ");
287+
ENFORCE(!s.empty());
288+
return string(s);
289+
}
290+
}
291+
return nullopt;
292+
}
293+
281294
TEST_CASE("SCIPTest") {
282295
// FIXME(varun): Add support for multifile tests.
283296
ENFORCE(inputs.size() == 1);
284297
Expectations test = Expectations::getExpectations(inputs[0]);
285298

299+
optional<string> gemMetadata = readGemMetadataFromComment(inputs[0]);
300+
286301
vector<unique_ptr<core::Error>> errors;
287302
auto inputPath = test.folder + test.basename;
288303

@@ -315,8 +330,14 @@ TEST_CASE("SCIPTest") {
315330

316331
cxxopts::Options options{"scip-ruby-snapshot-test"};
317332
scipProvider->injectOptions(options);
318-
std::vector<const char *> argv = {"scip-ruby-snapshot-test", "--index-file", indexFilePath.c_str(), nullptr};
319-
auto parseResult = options.parse(3, argv.data());
333+
std::vector<const char *> argv = {"scip-ruby-snapshot-test", "--index-file", indexFilePath.c_str()};
334+
if (gemMetadata.has_value()) {
335+
argv.push_back("--gem-metadata");
336+
ENFORCE(!gemMetadata.value().empty());
337+
argv.push_back(gemMetadata.value().data());
338+
}
339+
argv.push_back(nullptr);
340+
auto parseResult = options.parse(argv.size() - 1, argv.data());
320341

321342
gs.semanticExtensions.push_back(scipProvider->readOptions(parseResult));
322343
{

0 commit comments

Comments
 (0)