Skip to content
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
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ jobs:
with:
name: ci-build-log
path: log
- name: "🏋️‍♂️ Run unit tests"
run: ./bazel test //test/scip:unit_tests --config=dbg
- name: "🏋️‍♂️ Run snapshot tests"
run: rm log && ./bazel test //test/scip --config=dbg
run: ./bazel test //test/scip --config=dbg
# Repo tests are kinda' broken right now because the bundle cache step needs synchronization
#
# - name: "🏋️‍♂️ Build repo tests"
Expand Down Expand Up @@ -74,4 +76,4 @@ jobs:
# count = current_dependencies.count {|dep| current_specs[dep.name].first.metadata.key?("funding_uri") }
# ^ boom!
# We should probably file a bug against bundler but I haven't been able to repro
# the issue outside of a Bazel sandbox yet.
# the issue outside of a Bazel sandbox yet.
18 changes: 5 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,28 +54,20 @@ to download and install fetch `scip-ruby`.

Run `scip-ruby` along with some information about your gem.

<!-- TODO: Add support for defaulting. -->

- If you have a `sorbet/config` file, that will be picked up
automatically to determine which files to index.
```bash
# Uses the latest revision as the version - prefer this if you will index every commit
bundle exec scip-ruby --gem-metadata "my-gem-name@$(git rev-parse HEAD)"

# Uses the latest tag as the version - prefer this if you're only indexing specific tags
bundle exec scip-ruby --gem-metadata "my-gem-name@$(git describe --tags --abbrev=0)"
bundle exec scip-ruby
```
- If you don't have a `sorbet/config` file, add an extra path argument
to index all files in the project.
```bash
# Uses the latest revision as the version - prefer this if you will index every commit
bundle exec scip-ruby . --gem-metadata "my-gem-name@$(git rev-parse HEAD)"

# Uses the latest tag as the version - prefer this if you're only indexing specific tags
bundle exec scip-ruby . --gem-metadata "my-gem-name@$(git describe --tags --abbrev=0)"
bundle exec scip-ruby .
```

These commands will output a SCIP index to `index.scip` (overridable via `--index-file`).
The gem name and version will be inferred from config files (overridable via `--gem-metadata`).

The SCIP index can be uploaded to a Sourcegraph instance
using the [Sourcegraph CLI](https://github.com/sourcegraph/src-cli)'s
[upload command](https://docs.sourcegraph.com/cli/references/code-intel/upload).
Expand All @@ -97,7 +89,7 @@ curl -L "https://github.com/sourcegraph/scip-ruby/releases/latest/download/scip-
# If using in CI with 'set -e', make sure to wrap the
# scip-ruby invocation in 'set +e' followed by 'set -e'
# so that indexing failures are non-blocking.
./scip-ruby --index-file index.scip --gem-metadata "my-gem-name@M.N.P"
./scip-ruby
```

The generated index can be uploaded to a Sourcegraph instance
Expand Down
2 changes: 2 additions & 0 deletions common/FileOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class FileOps final {
// NOTE: This does not create parent directories if they exist.
static bool ensureDir(std::string_view path);

static std::string getCurrentDir();

// NOTE: this is a minimal wrapper around rmdir, and as such will raise an exception if the directory is not empty
// when it's removed.
static void removeDir(std::string_view path);
Expand Down
4 changes: 4 additions & 0 deletions common/FileSystem.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ void OSFileSystem::writeFile(string_view filename, string_view text) {
return FileOps::write(filename, text);
}

std::string OSFileSystem::getCurrentDir() const {
return FileOps::getCurrentDir();
}

vector<string> OSFileSystem::listFilesInDir(string_view path, const UnorderedSet<std::string> &extensions,
bool recursive, const std::vector<std::string> &absoluteIgnorePatterns,
const std::vector<std::string> &relativeIgnorePatterns) const {
Expand Down
4 changes: 4 additions & 0 deletions common/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class FileSystem {
/** Writes the specified data to the given file. */
virtual void writeFile(std::string_view filename, std::string_view text) = 0;

/** Gets the path for the current directory. */
virtual std::string getCurrentDir() const = 0;

/**
* Returns a list of all files in the given directory. Returns paths that include the path to directory.
*
Expand All @@ -45,6 +48,7 @@ class OSFileSystem final : public FileSystem {

std::string readFile(std::string_view path) const override;
void writeFile(std::string_view filename, std::string_view text) override;
std::string getCurrentDir() const override;
std::vector<std::string> listFilesInDir(std::string_view path, const UnorderedSet<std::string> &extensions,
bool recursive, const std::vector<std::string> &absoluteIgnorePatterns,
const std::vector<std::string> &relativeIgnorePatterns) const override;
Expand Down
7 changes: 7 additions & 0 deletions common/common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <dirent.h>
#include <exception>
#include <memory>
#include <sys/param.h>
#include <vector>

#include <sys/stat.h>
Expand Down Expand Up @@ -87,6 +88,12 @@ bool sorbet::FileOps::ensureDir(string_view path) {
return true;
}

std::string sorbet::FileOps::getCurrentDir() {
char buf[MAXPATHLEN + 1];
getcwd(buf, sizeof(buf));
return std::string(buf);
}

void sorbet::FileOps::removeDir(string_view path) {
auto err = rmdir(string(path).c_str());
if (err) {
Expand Down
19 changes: 18 additions & 1 deletion docs/scip-ruby/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,13 @@ scip-ruby myfile.rb --index-file index.scip

### Running tests

Run snapshot tests, which are self-contained:
There are currently 3 kinds of tests:
- Snapshot tests: These cover indexer output.
- Unit tests: These cover some internals which are not possible
to test via snapshots.
- Repo/Integration tests: These try to index an OSS repo using scip-ruby.

To run snapshot tests, which are self-contained:

```
./bazel test //test/scip --config=dbg
Expand All @@ -159,6 +165,12 @@ Updating snapshots:
./bazel test //test/scip:update --config=dbg
```

You can run unit tests using:

```
./bazel test //test/scip:unit_tests --config=dbg
```

WARNING: Repo tests are kinda' broken right now; they're disabled
in CI (see ci.yml), and may or may not work on your machine.

Expand All @@ -182,6 +194,11 @@ you add a new test, you should create matching `.snapshot.rb` files
since those are used as inputs to Bazel.
If you know of a way to get rid of that annoyance, submit a PR.

### Writing a new unit test

See the existing unit tests in `scip_test_runner.cc`
and follow the same structure.

### Writing a new repo test

First, clone the repo using Sorbet locally
Expand Down
3 changes: 3 additions & 0 deletions main/pipeline/pipeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,9 @@ void typecheck(const core::GlobalState &gs, vector<ast::ParsedFile> what, const

{
ProgressIndicator cfgInferProgress(opts.showProgress, "CFG+Inference", what.size());
for (auto &extension : gs.semanticExtensions) {
extension->prepareForTypechecking(gs);
}
workers.multiplexJob("typecheck", [&gs, &opts, epoch, &epochManager, &preemptionManager, fileq, outputq,
cancelable, intentionallyLeakASTs]() {
vector<core::FileRef> processedFiles;
Expand Down
1 change: 1 addition & 0 deletions main/pipeline/semantic_extension/SemanticExtension.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class CFG;
namespace pipeline::semantic_extension {
class SemanticExtension {
public:
virtual void prepareForTypechecking(const core::GlobalState &) = 0;
virtual void finishTypecheckFile(const core::GlobalState &, const core::FileRef &) const = 0;
virtual void finishTypecheck(const core::GlobalState &) const = 0;
virtual void typecheck(const core::GlobalState &, core::FileRef file, cfg::CFG &, ast::MethodDef &) const = 0;
Expand Down
2 changes: 2 additions & 0 deletions plugin_injector/plugin_injector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ class LLVMSemanticExtension : public SemanticExtension {

virtual void run(core::MutableContext &ctx, ast::ClassDef *klass) const override{};

virtual void prepareForTypechecking(const core::GlobalState &gs) override{};

virtual void typecheck(const core::GlobalState &gs, core::FileRef file, cfg::CFG &cfg,
ast::MethodDef &md) const override {
if (!shouldCompile(gs, file)) {
Expand Down
2 changes: 2 additions & 0 deletions scip_indexer/Debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ template <typename V, typename Fn> std::string showVec(const V &v, Fn f) {
return out.str();
}

extern const sorbet::core::ErrorClass SCIPRubyDebug;

void _log_debug(const sorbet::core::GlobalState &gs, sorbet::core::Loc loc, std::string s);
} // namespace sorbet::scip_indexer

Expand Down
39 changes: 30 additions & 9 deletions scip_indexer/SCIPIndexer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,7 @@ using LocalSymbolTable = UnorderedMap<core::LocalVariable, core::Loc>;
class SCIPSemanticExtension : public SemanticExtension {
public:
string indexFilePath;
string gemMetadataString;
scip_indexer::GemMetadata gemMetadata;

using SCIPState = sorbet::scip_indexer::SCIPState;
Expand Down Expand Up @@ -1149,6 +1150,24 @@ class SCIPSemanticExtension : public SemanticExtension {

void run(core::MutableContext &ctx, ast::ClassDef *cd) const override {}

virtual void prepareForTypechecking(const core::GlobalState &gs) override {
auto maybeMetadata = scip_indexer::GemMetadata::tryParse(this->gemMetadataString);
if (maybeMetadata.has_value()) {
this->gemMetadata = maybeMetadata.value();
} // TODO: Issue error for incorrect format in string...
if (this->gemMetadata.name().empty() || this->gemMetadata.version().empty()) {
auto [metadata, errors] = scip_indexer::GemMetadata::readFromConfig(OSFileSystem());
this->gemMetadata = metadata;
for (auto &error : errors) {
if (auto e = gs.beginError(core::Loc(), scip_indexer::SCIPRubyDebug)) {
e.setHeader("{}: {}",
error.kind == scip_indexer::GemMetadataError::Kind::Error ? "error" : "warning",
error.message);
}
}
}
};

virtual void finishTypecheckFile(const core::GlobalState &gs, const core::FileRef &file) const override {
if (this->doNothing()) {
return;
Expand Down Expand Up @@ -1251,12 +1270,13 @@ class SCIPSemanticExtension : public SemanticExtension {
}

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

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

Expand All @@ -1265,7 +1285,9 @@ 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>())(
"gem-metadata", "Metadata in 'name@version' format to be used for cross-repository code navigation.",
"gem-metadata",
"Metadata in 'name@version' format to be used for cross-repository code navigation. For repositories which "
"index every commit, the SHA should be used for the version instead of a git tag (or equivalent).",
cxxopts::value<string>());
};
unique_ptr<SemanticExtension> readOptions(cxxopts::ParseResult &providedOptions) const override {
Expand All @@ -1285,13 +1307,12 @@ class SCIPSemanticExtensionProvider : public SemanticExtensionProvider {
} else {
indexFilePath = "index.scip";
}
return make_unique<SCIPSemanticExtension>(
indexFilePath,
scip_indexer::GemMetadata::tryParseOrDefault(
providedOptions.count("gem-metadata") > 0 ? providedOptions["gem-metadata"].as<string>() : ""));
auto gemMetadataString =
providedOptions.count("gem-metadata") > 0 ? providedOptions["gem-metadata"].as<string>() : "";
return make_unique<SCIPSemanticExtension>(indexFilePath, gemMetadataString, scip_indexer::GemMetadata());
};
virtual unique_ptr<SemanticExtension> defaultInstance() const override {
return make_unique<SCIPSemanticExtension>("index.scip", scip_indexer::GemMetadata::tryParseOrDefault(""));
return make_unique<SCIPSemanticExtension>("index.scip", "", scip_indexer::GemMetadata());
};
static vector<SemanticExtensionProvider *> getProviders();
virtual ~SCIPSemanticExtensionProvider() = default;
Expand Down
Loading