Skip to content

feat: Add flag to indicate gem name + version. #22

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

Merged
merged 1 commit into from
Jul 18, 2022
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
74 changes: 63 additions & 11 deletions scip_indexer/SCIPIndexer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/synchronization/mutex.h"
#include "spdlog/fmt/fmt.h"

Expand Down Expand Up @@ -127,6 +128,32 @@ struct OwnedLocal {
}
};

class GemMetadata final {
string _name;
string _version;

GemMetadata(string name, string version) : _name(name), _version(version) {}

public:
GemMetadata &operator=(const GemMetadata &) = default;

static GemMetadata tryParseOrDefault(string metadata) {
vector<string> v = absl::StrSplit(metadata, '@');
if (v.size() != 2 || v[0].empty() || v[1].empty()) {
return GemMetadata{"TODO", "TODO"};
}
return GemMetadata{v[0], v[1]};
}

const std::string &name() const {
return this->_name;
}

const std::string &version() const {
return this->_version;
}
};

// A wrapper type to handle both top-level symbols (like classes) as well as
// "inner symbols" like fields (@x). In a statically typed language, field
// symbols are like any other symbols, but in Ruby, they aren't declared
Expand Down Expand Up @@ -212,12 +239,12 @@ class NamedSymbolRef final {
}

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

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

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

public:
SCIPState() = default;
SCIPState(GemMetadata metadata) : symbolScratchBuffer(), symbolStringCache(), gemMetadata(metadata) {}
~SCIPState() = default;
SCIPState(SCIPState &&) = default;
SCIPState &operator=(SCIPState &&other) = default;
Expand All @@ -362,7 +390,7 @@ class SCIPState {
status = scip::utils::emitSymbolString(*symbol, this->symbolScratchBuffer);
} else {
scip::Symbol symbol;
status = symRef.symbolForExpr(gs, symbol);
status = symRef.symbolForExpr(gs, this->gemMetadata, symbol);
if (!status.ok()) {
return status;
}
Expand Down Expand Up @@ -459,7 +487,7 @@ class SCIPState {
std::optional<core::LocOffsets> loc = std::nullopt) {
// TODO:(varun) Should we cache here too to avoid emitting duplicate definitions?
scip::Symbol symbol;
auto status = symRef.symbolForExpr(gs, symbol);
auto status = symRef.symbolForExpr(gs, this->gemMetadata, symbol);
if (!status.ok()) {
return status;
}
Expand Down Expand Up @@ -954,6 +982,7 @@ using LocalSymbolTable = UnorderedMap<core::LocalVariable, core::Loc>;
class SCIPSemanticExtension : public SemanticExtension {
public:
string indexFilePath;
scip_indexer::GemMetadata gemMetadata;

using SCIPState = sorbet::scip_indexer::SCIPState;

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

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

bool doNothing() const {
return this->indexFilePath.empty();
}

void run(core::MutableContext &ctx, ast::ClassDef *cd) const override {
if (this->doNothing()) {
return;
}
// FIXME:(varun) This is a no-op???
emitSymbol(ctx.state, ctx.file, cd);
};
virtual void finishTypecheckFile(const core::GlobalState &gs, const core::FileRef &file) const override {
if (this->doNothing()) {
return;
}
getSCIPState()->saveDocument(gs, file);
};
virtual void finishTypecheck(const core::GlobalState &gs) const override {
if (this->doNothing()) {
return;
}
scip::ToolInfo toolInfo;
toolInfo.set_name("scip-ruby");
toolInfo.set_version(sorbet_version);
Expand Down Expand Up @@ -1045,6 +1087,9 @@ class SCIPSemanticExtension : public SemanticExtension {

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

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

SCIPSemanticExtension(string indexFilePath) : indexFilePath(indexFilePath), mutableState() {}
SCIPSemanticExtension(string indexFilePath, scip_indexer::GemMetadata metadata)
: indexFilePath(indexFilePath), gemMetadata(metadata), mutableState() {}
~SCIPSemanticExtension() {}
};

Expand All @@ -1080,15 +1126,21 @@ class SCIPSemanticExtensionProvider : public SemanticExtensionProvider {
void injectOptions(cxxopts::Options &optsBuilder) const override {
optsBuilder.add_options("indexer")("index-file", "Output SCIP index to a directory, which must already exist",
cxxopts::value<string>());
optsBuilder.add_options("name@version")(
"gem-metadata", "Name and version pair to be used for cross-repository code navigation.",
cxxopts::value<string>());
};
unique_ptr<SemanticExtension> readOptions(cxxopts::ParseResult &providedOptions) const override {
if (providedOptions.count("index-file") > 0) {
return make_unique<SCIPSemanticExtension>(providedOptions["index-file"].as<string>());
return make_unique<SCIPSemanticExtension>(
providedOptions["index-file"].as<string>(),
scip_indexer::GemMetadata::tryParseOrDefault(
providedOptions.count("gem-metadata") > 0 ? providedOptions["gem-metadata"].as<string>() : ""));
}
return this->defaultInstance();
};
virtual unique_ptr<SemanticExtension> defaultInstance() const override {
return make_unique<SCIPSemanticExtension>("index.scip");
return make_unique<SCIPSemanticExtension>("", scip_indexer::GemMetadata::tryParseOrDefault(""));
};
static vector<SemanticExtensionProvider *> getProviders();
virtual ~SCIPSemanticExtensionProvider() = default;
Expand Down
11 changes: 11 additions & 0 deletions test/scip/testdata/gem_metadata.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# typed: true
# gem-metadata: [email protected]

class C
def m
n
end
def n
m
end
end
16 changes: 16 additions & 0 deletions test/scip/testdata/gem_metadata.snapshot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# typed: true
# gem-metadata: [email protected]

class C
# ^ definition scip-ruby gem leet 1.3.3.7 C#
def m
# ^^^^^ definition scip-ruby gem leet 1.3.3.7 C#m().
n
# ^ reference scip-ruby gem leet 1.3.3.7 C#n().
end
def n
# ^^^^^ definition scip-ruby gem leet 1.3.3.7 C#n().
m
# ^ reference scip-ruby gem leet 1.3.3.7 C#m().
end
end
25 changes: 23 additions & 2 deletions test/scip_test_runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "absl/strings/match.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/str_split.h"
#include "spdlog/sinks/stdout_color_sinks.h"

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

optional<string> readGemMetadataFromComment(string_view path) {
ifstream input(path);
for (string line; getline(input, line);) {
if (absl::StrContains(line, "# gem-metadata: ")) {
auto s = absl::StripPrefix(line, "# gem-metadata: ");
ENFORCE(!s.empty());
return string(s);
}
}
return nullopt;
}

TEST_CASE("SCIPTest") {
// FIXME(varun): Add support for multifile tests.
ENFORCE(inputs.size() == 1);
Expectations test = Expectations::getExpectations(inputs[0]);

optional<string> gemMetadata = readGemMetadataFromComment(inputs[0]);

vector<unique_ptr<core::Error>> errors;
auto inputPath = test.folder + test.basename;

Expand Down Expand Up @@ -315,8 +330,14 @@ TEST_CASE("SCIPTest") {

cxxopts::Options options{"scip-ruby-snapshot-test"};
scipProvider->injectOptions(options);
std::vector<const char *> argv = {"scip-ruby-snapshot-test", "--index-file", indexFilePath.c_str(), nullptr};
auto parseResult = options.parse(3, argv.data());
std::vector<const char *> argv = {"scip-ruby-snapshot-test", "--index-file", indexFilePath.c_str()};
if (gemMetadata.has_value()) {
argv.push_back("--gem-metadata");
ENFORCE(!gemMetadata.value().empty());
argv.push_back(gemMetadata.value().data());
}
argv.push_back(nullptr);
auto parseResult = options.parse(argv.size() - 1, argv.data());

gs.semanticExtensions.push_back(scipProvider->readOptions(parseResult));
{
Expand Down