mirror of
https://github.com/github/codeql.git
synced 2026-01-25 12:22:57 +01:00
Merge branch 'main' into add-activerecord-annotate
This commit is contained in:
@@ -3,7 +3,9 @@ CodeQL query help for C and C++
|
||||
|
||||
.. include:: ../reusables/query-help-overview.rst
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/main/cpp/ql/examples>`__.
|
||||
These queries are published in the CodeQL query pack ``codeql/cpp-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/cpp/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/cpp/ql/src>`__).
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/codeql-cli/latest/cpp/ql/examples>`__.
|
||||
|
||||
.. include:: toc-cpp.rst
|
||||
|
||||
@@ -3,6 +3,8 @@ CodeQL query help for C#
|
||||
|
||||
.. include:: ../reusables/query-help-overview.rst
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/main/csharp/ql/examples>`__.
|
||||
These queries are published in the CodeQL query pack ``codeql/csharp-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/csharp/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/csharp/ql/src>`__).
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/codeql-cli/latest/csharp/ql/examples>`__.
|
||||
|
||||
.. include:: toc-csharp.rst
|
||||
@@ -3,6 +3,8 @@ CodeQL query help for Go
|
||||
|
||||
.. include:: ../reusables/query-help-overview.rst
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/main/go/ql/examples>`__.
|
||||
These queries are published in the CodeQL query pack ``codeql/go-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/go/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/go/ql/src>`__).
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/codeql-cli/latest/go/ql/examples>`__.
|
||||
|
||||
.. include:: toc-go.rst
|
||||
|
||||
@@ -3,6 +3,8 @@ CodeQL query help for Java
|
||||
|
||||
.. include:: ../reusables/query-help-overview.rst
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/main/java/ql/examples>`__.
|
||||
These queries are published in the CodeQL query pack ``codeql/java-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/java/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/java/ql/src>`__).
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/codeql-cli/latest/java/ql/examples>`__.
|
||||
|
||||
.. include:: toc-java.rst
|
||||
|
||||
@@ -3,6 +3,8 @@ CodeQL query help for JavaScript
|
||||
|
||||
.. include:: ../reusables/query-help-overview.rst
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/main/javascript/ql/examples>`__.
|
||||
These queries are published in the CodeQL query pack ``codeql/javascript-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/javascript/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/javascript/ql/src>`__).
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/codeql-cli/latest/javascript/ql/examples>`__.
|
||||
|
||||
.. include:: toc-javascript.rst
|
||||
@@ -3,6 +3,8 @@ CodeQL query help for Python
|
||||
|
||||
.. include:: ../reusables/query-help-overview.rst
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/main/python/ql/examples>`__.
|
||||
These queries are published in the CodeQL query pack ``codeql/python-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/python/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/python/ql/src>`__).
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/codeql-cli/latest/python/ql/examples>`__.
|
||||
|
||||
.. include:: toc-python.rst
|
||||
@@ -3,6 +3,8 @@ CodeQL query help for Ruby
|
||||
|
||||
.. include:: ../reusables/query-help-overview.rst
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/main/ruby/ql/examples>`__.
|
||||
These queries are published in the CodeQL query pack ``codeql/ruby-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/ruby/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/ruby/ql/src>`__).
|
||||
|
||||
For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository <https://github.com/github/codeql/tree/codeql-cli/latest/ruby/ql/examples>`__.
|
||||
|
||||
.. include:: toc-ruby.rst
|
||||
|
||||
@@ -323,6 +323,7 @@ AppliedPropertyWrapperExpr:
|
||||
_extends: Expr
|
||||
|
||||
Argument:
|
||||
_extends: Locatable
|
||||
label: string
|
||||
_children:
|
||||
expr: Expr
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
#include "SwiftExtractor.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <unistd.h>
|
||||
#include <unordered_set>
|
||||
#include <queue>
|
||||
|
||||
@@ -16,8 +12,9 @@
|
||||
#include <llvm/Support/Path.h>
|
||||
|
||||
#include "swift/extractor/trap/generated/TrapClasses.h"
|
||||
#include "swift/extractor/trap/TrapOutput.h"
|
||||
#include "swift/extractor/trap/TrapDomain.h"
|
||||
#include "swift/extractor/visitors/SwiftVisitor.h"
|
||||
#include "swift/extractor/infra/TargetFile.h"
|
||||
|
||||
using namespace codeql;
|
||||
|
||||
@@ -52,7 +49,7 @@ static void archiveFile(const SwiftExtractorConfiguration& config, swift::Source
|
||||
}
|
||||
}
|
||||
|
||||
static std::string getTrapFilename(swift::ModuleDecl& module, swift::SourceFile* primaryFile) {
|
||||
static std::string getFilename(swift::ModuleDecl& module, swift::SourceFile* primaryFile) {
|
||||
if (primaryFile) {
|
||||
return primaryFile->getFilename().str();
|
||||
}
|
||||
@@ -76,60 +73,45 @@ static llvm::SmallVector<swift::Decl*> getTopLevelDecls(swift::ModuleDecl& modul
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dumpArgs(TargetFile& out, const SwiftExtractorConfiguration& config) {
|
||||
out << "/* extractor-args:\n";
|
||||
for (const auto& opt : config.frontendOptions) {
|
||||
out << " " << std::quoted(opt) << " \\\n";
|
||||
}
|
||||
out << "\n*/\n";
|
||||
|
||||
out << "/* swift-frontend-args:\n";
|
||||
for (const auto& opt : config.patchedFrontendOptions) {
|
||||
out << " " << std::quoted(opt) << " \\\n";
|
||||
}
|
||||
out << "\n*/\n";
|
||||
}
|
||||
|
||||
static void extractDeclarations(const SwiftExtractorConfiguration& config,
|
||||
swift::CompilerInstance& compiler,
|
||||
swift::ModuleDecl& module,
|
||||
swift::SourceFile* primaryFile = nullptr) {
|
||||
auto filename = getFilename(module, primaryFile);
|
||||
|
||||
// The extractor can be called several times from different processes with
|
||||
// the same input file(s)
|
||||
// We are using PID to avoid concurrent access
|
||||
// TODO: find a more robust approach to avoid collisions?
|
||||
auto name = getTrapFilename(module, primaryFile);
|
||||
llvm::StringRef filename(name);
|
||||
std::string tempTrapName = filename.str() + '.' + std::to_string(getpid()) + ".trap";
|
||||
llvm::SmallString<PATH_MAX> tempTrapPath(config.getTempTrapDir());
|
||||
llvm::sys::path::append(tempTrapPath, tempTrapName);
|
||||
|
||||
llvm::StringRef tempTrapParent = llvm::sys::path::parent_path(tempTrapPath);
|
||||
if (std::error_code ec = llvm::sys::fs::create_directories(tempTrapParent)) {
|
||||
std::cerr << "Cannot create temp trap directory '" << tempTrapParent.str()
|
||||
<< "': " << ec.message() << "\n";
|
||||
// the same input file(s). Using `TargetFile` the first process will win, and the following
|
||||
// will just skip the work
|
||||
auto trapTarget = TargetFile::create(filename + ".trap", config.trapDir, config.getTempTrapDir());
|
||||
if (!trapTarget) {
|
||||
// another process arrived first, nothing to do for us
|
||||
return;
|
||||
}
|
||||
|
||||
std::ofstream trapStream(tempTrapPath.str().str());
|
||||
if (!trapStream) {
|
||||
std::error_code ec;
|
||||
ec.assign(errno, std::generic_category());
|
||||
std::cerr << "Cannot create temp trap file '" << tempTrapPath.str().str()
|
||||
<< "': " << ec.message() << "\n";
|
||||
return;
|
||||
}
|
||||
trapStream << "/* extractor-args:\n";
|
||||
for (auto opt : config.frontendOptions) {
|
||||
trapStream << " " << std::quoted(opt) << " \\\n";
|
||||
}
|
||||
trapStream << "\n*/\n";
|
||||
|
||||
trapStream << "/* swift-frontend-args:\n";
|
||||
for (auto opt : config.patchedFrontendOptions) {
|
||||
trapStream << " " << std::quoted(opt) << " \\\n";
|
||||
}
|
||||
trapStream << "\n*/\n";
|
||||
|
||||
TrapOutput trap{trapStream};
|
||||
TrapArena arena{};
|
||||
dumpArgs(*trapTarget, config);
|
||||
TrapDomain trap{*trapTarget};
|
||||
|
||||
// TODO: move default location emission elsewhere, possibly in a separate global trap file
|
||||
auto unknownFileLabel = arena.allocateLabel<FileTag>();
|
||||
// the following cannot conflict with actual files as those have an absolute path starting with /
|
||||
trap.assignKey(unknownFileLabel, "unknown");
|
||||
auto unknownFileLabel = trap.createLabel<FileTag>("unknown");
|
||||
auto unknownLocationLabel = trap.createLabel<LocationTag>("unknown");
|
||||
trap.emit(FilesTrap{unknownFileLabel});
|
||||
auto unknownLocationLabel = arena.allocateLabel<LocationTag>();
|
||||
trap.assignKey(unknownLocationLabel, "unknown");
|
||||
trap.emit(LocationsTrap{unknownLocationLabel, unknownFileLabel});
|
||||
|
||||
SwiftVisitor visitor(compiler.getSourceMgr(), arena, trap, module, primaryFile);
|
||||
SwiftVisitor visitor(compiler.getSourceMgr(), trap, module, primaryFile);
|
||||
auto topLevelDecls = getTopLevelDecls(module, primaryFile);
|
||||
for (auto decl : topLevelDecls) {
|
||||
visitor.extract(decl);
|
||||
@@ -142,28 +124,9 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config,
|
||||
// fact that the file was extracted
|
||||
llvm::SmallString<PATH_MAX> name(filename);
|
||||
llvm::sys::fs::make_absolute(name);
|
||||
auto fileLabel = arena.allocateLabel<FileTag>();
|
||||
trap.assignKey(fileLabel, name.str().str());
|
||||
auto fileLabel = trap.createLabel<FileTag>(name.str().str());
|
||||
trap.emit(FilesTrap{fileLabel, name.str().str()});
|
||||
}
|
||||
|
||||
// TODO: Pick a better name to avoid collisions
|
||||
std::string trapName = filename.str() + ".trap";
|
||||
llvm::SmallString<PATH_MAX> trapPath(config.trapDir);
|
||||
llvm::sys::path::append(trapPath, trapName);
|
||||
|
||||
llvm::StringRef trapParent = llvm::sys::path::parent_path(trapPath);
|
||||
if (std::error_code ec = llvm::sys::fs::create_directories(trapParent)) {
|
||||
std::cerr << "Cannot create trap directory '" << trapParent.str() << "': " << ec.message()
|
||||
<< "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: The last process wins. Should we do better than that?
|
||||
if (std::error_code ec = llvm::sys::fs::rename(tempTrapPath, trapPath)) {
|
||||
std::cerr << "Cannot rename temp trap file '" << tempTrapPath.str().str() << "' -> '"
|
||||
<< trapPath.str().str() << "': " << ec.message() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
static std::unordered_set<std::string> collectInputFilenames(swift::CompilerInstance& compiler) {
|
||||
|
||||
@@ -2,9 +2,11 @@ load("//swift:rules.bzl", "swift_cc_library")
|
||||
|
||||
swift_cc_library(
|
||||
name = "infra",
|
||||
srcs = glob(["*.cpp"]),
|
||||
hdrs = glob(["*.h"]),
|
||||
visibility = ["//swift:__subpackages__"],
|
||||
deps = [
|
||||
"//swift/extractor/trap",
|
||||
"//swift/tools/prebuilt:swift-llvm-support",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
#include <swift/Basic/SourceManager.h>
|
||||
#include <llvm/Support/FileSystem.h>
|
||||
|
||||
#include "swift/extractor/trap/TrapArena.h"
|
||||
#include "swift/extractor/trap/TrapLabelStore.h"
|
||||
#include "swift/extractor/trap/TrapOutput.h"
|
||||
#include "swift/extractor/trap/TrapDomain.h"
|
||||
#include "swift/extractor/infra/SwiftTagTraits.h"
|
||||
#include "swift/extractor/trap/generated/TrapClasses.h"
|
||||
|
||||
@@ -22,12 +21,10 @@ class SwiftDispatcher {
|
||||
// all references and pointers passed as parameters to this constructor are supposed to outlive
|
||||
// the SwiftDispatcher
|
||||
SwiftDispatcher(const swift::SourceManager& sourceManager,
|
||||
TrapArena& arena,
|
||||
TrapOutput& trap,
|
||||
TrapDomain& trap,
|
||||
swift::ModuleDecl& currentModule,
|
||||
swift::SourceFile* currentPrimarySourceFile = nullptr)
|
||||
: sourceManager{sourceManager},
|
||||
arena{arena},
|
||||
trap{trap},
|
||||
currentModule{currentModule},
|
||||
currentPrimarySourceFile{currentPrimarySourceFile} {}
|
||||
@@ -77,6 +74,7 @@ class SwiftDispatcher {
|
||||
}
|
||||
waitingForNewLabel = e;
|
||||
visit(e);
|
||||
// TODO when everything is moved to structured C++ classes, this should be moved to createEntry
|
||||
if (auto l = store.get(e)) {
|
||||
if constexpr (!std::is_base_of_v<swift::TypeBase, E>) {
|
||||
attachLocation(e, *l);
|
||||
@@ -95,13 +93,17 @@ class SwiftDispatcher {
|
||||
return fetchLabelFromUnion<AstNodeTag>(node);
|
||||
}
|
||||
|
||||
TrapLabel<ConditionElementTag> fetchLabel(const swift::StmtConditionElement& element) {
|
||||
return fetchLabel(&element);
|
||||
}
|
||||
|
||||
// Due to the lazy emission approach, we must assign a label to a corresponding AST node before
|
||||
// it actually gets emitted to handle recursive cases such as recursive calls, or recursive type
|
||||
// declarations
|
||||
template <typename E, typename... Args>
|
||||
TrapLabelOf<E> assignNewLabel(E* e, Args&&... args) {
|
||||
assert(waitingForNewLabel == Store::Handle{e} && "assignNewLabel called on wrong entity");
|
||||
auto label = createLabel<TrapTagOf<E>>(std::forward<Args>(args)...);
|
||||
auto label = trap.createLabel<TrapTagOf<E>>(std::forward<Args>(args)...);
|
||||
store.insert(e, label);
|
||||
waitingForNewLabel = std::monostate{};
|
||||
return label;
|
||||
@@ -118,18 +120,13 @@ class SwiftDispatcher {
|
||||
return TrapClassOf<E>{assignNewLabel(&e, std::forward<Args>(args)...)};
|
||||
}
|
||||
|
||||
template <typename Tag>
|
||||
TrapLabel<Tag> createLabel() {
|
||||
auto ret = arena.allocateLabel<Tag>();
|
||||
trap.assignStar(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename Tag, typename... Args>
|
||||
TrapLabel<Tag> createLabel(Args&&... args) {
|
||||
auto ret = arena.allocateLabel<Tag>();
|
||||
trap.assignKey(ret, std::forward<Args>(args)...);
|
||||
return ret;
|
||||
// used to create a new entry for entities that should not be cached
|
||||
// an example is swift::Argument, that are created on the fly and thus have no stable pointer
|
||||
template <typename E, typename... Args, std::enable_if_t<!std::is_pointer_v<E>>* = nullptr>
|
||||
auto createUncachedEntry(const E& e, Args&&... args) {
|
||||
auto label = trap.createLabel<TrapTagOf<E>>(std::forward<Args>(args)...);
|
||||
attachLocation(&e, label);
|
||||
return TrapClassOf<E>{label};
|
||||
}
|
||||
|
||||
template <typename Locatable>
|
||||
@@ -213,6 +210,7 @@ class SwiftDispatcher {
|
||||
using Store = TrapLabelStore<swift::Decl,
|
||||
swift::Stmt,
|
||||
swift::StmtCondition,
|
||||
swift::StmtConditionElement,
|
||||
swift::CaseLabelItem,
|
||||
swift::Expr,
|
||||
swift::Pattern,
|
||||
@@ -227,13 +225,13 @@ class SwiftDispatcher {
|
||||
return;
|
||||
}
|
||||
std::string filepath = getFilepath(start);
|
||||
auto fileLabel = createLabel<FileTag>(filepath);
|
||||
auto fileLabel = trap.createLabel<FileTag>(filepath);
|
||||
// TODO: do not emit duplicate trap entries for Files
|
||||
trap.emit(FilesTrap{fileLabel, filepath});
|
||||
auto [startLine, startColumn] = sourceManager.getLineAndColumnInBuffer(start);
|
||||
auto [endLine, endColumn] = sourceManager.getLineAndColumnInBuffer(end);
|
||||
auto locLabel = createLabel<LocationTag>('{', fileLabel, "}:", startLine, ':', startColumn, ':',
|
||||
endLine, ':', endColumn);
|
||||
auto locLabel = trap.createLabel<LocationTag>('{', fileLabel, "}:", startLine, ':', startColumn,
|
||||
':', endLine, ':', endColumn);
|
||||
trap.emit(LocationsTrap{locLabel, fileLabel, startLine, startColumn, endLine, endColumn});
|
||||
trap.emit(LocatableLocationsTrap{locatableLabel, locLabel});
|
||||
}
|
||||
@@ -275,7 +273,8 @@ class SwiftDispatcher {
|
||||
// which are to be introduced in follow-up PRs
|
||||
virtual void visit(swift::Decl* decl) = 0;
|
||||
virtual void visit(swift::Stmt* stmt) = 0;
|
||||
virtual void visit(swift::StmtCondition* cond) = 0;
|
||||
virtual void visit(const swift::StmtCondition* cond) = 0;
|
||||
virtual void visit(const swift::StmtConditionElement* cond) = 0;
|
||||
virtual void visit(swift::CaseLabelItem* item) = 0;
|
||||
virtual void visit(swift::Expr* expr) = 0;
|
||||
virtual void visit(swift::Pattern* pattern) = 0;
|
||||
@@ -283,8 +282,7 @@ class SwiftDispatcher {
|
||||
virtual void visit(swift::TypeBase* type) = 0;
|
||||
|
||||
const swift::SourceManager& sourceManager;
|
||||
TrapArena& arena;
|
||||
TrapOutput& trap;
|
||||
TrapDomain& trap;
|
||||
Store store;
|
||||
Store::Handle waitingForNewLabel{std::monostate{}};
|
||||
swift::ModuleDecl& currentModule;
|
||||
|
||||
@@ -36,12 +36,14 @@ using SILBoxTypeReprTag = SilBoxTypeReprTag;
|
||||
|
||||
MAP_TAG(Stmt);
|
||||
MAP_TAG(StmtCondition);
|
||||
MAP_TYPE_TO_TAG(StmtConditionElement, ConditionElementTag);
|
||||
MAP_TAG(CaseLabelItem);
|
||||
#define ABSTRACT_STMT(CLASS, PARENT) MAP_SUBTAG(CLASS##Stmt, PARENT)
|
||||
#define STMT(CLASS, PARENT) ABSTRACT_STMT(CLASS, PARENT)
|
||||
#include <swift/AST/StmtNodes.def>
|
||||
|
||||
MAP_TAG(Expr);
|
||||
MAP_TAG(Argument);
|
||||
#define ABSTRACT_EXPR(CLASS, PARENT) MAP_SUBTAG(CLASS##Expr, PARENT)
|
||||
#define EXPR(CLASS, PARENT) ABSTRACT_EXPR(CLASS, PARENT)
|
||||
#include <swift/AST/ExprNodes.def>
|
||||
|
||||
85
swift/extractor/infra/TargetFile.cpp
Normal file
85
swift/extractor/infra/TargetFile.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "swift/extractor/infra/TargetFile.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cerrno>
|
||||
#include <system_error>
|
||||
|
||||
#include <llvm/Support/FileSystem.h>
|
||||
#include <llvm/Support/Path.h>
|
||||
|
||||
namespace codeql {
|
||||
namespace {
|
||||
[[noreturn]] void error(const char* action, const std::string& arg, std::error_code ec) {
|
||||
std::cerr << "Unable to " << action << ": " << arg << " (" << ec.message() << ")\n";
|
||||
std::abort();
|
||||
}
|
||||
|
||||
[[noreturn]] void error(const char* action, const std::string& arg) {
|
||||
error(action, arg, {errno, std::system_category()});
|
||||
}
|
||||
|
||||
void ensureParentDir(const std::string& path) {
|
||||
auto parent = llvm::sys::path::parent_path(path);
|
||||
if (auto ec = llvm::sys::fs::create_directories(parent)) {
|
||||
error("create directory", parent.str(), ec);
|
||||
}
|
||||
}
|
||||
|
||||
std::string initPath(std::string_view target, std::string_view dir) {
|
||||
std::string ret{dir};
|
||||
assert(!target.empty() && "target must be a non-empty path");
|
||||
if (target[0] != '/') {
|
||||
ret += '/';
|
||||
}
|
||||
ret.append(target);
|
||||
ensureParentDir(ret);
|
||||
return ret;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TargetFile::TargetFile(std::string_view target,
|
||||
std::string_view targetDir,
|
||||
std::string_view workingDir)
|
||||
: workingPath{initPath(target, workingDir)}, targetPath{initPath(target, targetDir)} {}
|
||||
|
||||
bool TargetFile::init() {
|
||||
errno = 0;
|
||||
// since C++17 "x" mode opens with O_EXCL (fails if file already exists)
|
||||
if (auto f = std::fopen(targetPath.c_str(), "wx")) {
|
||||
std::fclose(f);
|
||||
out.open(workingPath);
|
||||
checkOutput("open file for writing");
|
||||
return true;
|
||||
}
|
||||
if (errno != EEXIST) {
|
||||
error("open file for writing", targetPath);
|
||||
}
|
||||
// else we just lost the race
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<TargetFile> TargetFile::create(std::string_view target,
|
||||
std::string_view targetDir,
|
||||
std::string_view workingDir) {
|
||||
TargetFile ret{target, targetDir, workingDir};
|
||||
if (ret.init()) return ret;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void TargetFile::commit() {
|
||||
if (out.is_open()) {
|
||||
out.close();
|
||||
errno = 0;
|
||||
if (std::rename(workingPath.c_str(), targetPath.c_str()) != 0) {
|
||||
error("rename file", targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TargetFile::checkOutput(const char* action) {
|
||||
if (!out) {
|
||||
error(action, workingPath);
|
||||
}
|
||||
}
|
||||
} // namespace codeql
|
||||
46
swift/extractor/infra/TargetFile.h
Normal file
46
swift/extractor/infra/TargetFile.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <cerrno>
|
||||
|
||||
namespace codeql {
|
||||
|
||||
// Only the first process trying to create a `TargetFile` for a given `target` is allowed to do
|
||||
// so, all others will have `create` return `std::nullopt`.
|
||||
// The content streamed to the `TargetFile` is written to `workingDir/target`, and is moved onto
|
||||
// `targetDir/target` on destruction.
|
||||
class TargetFile {
|
||||
std::string workingPath;
|
||||
std::string targetPath;
|
||||
std::ofstream out;
|
||||
|
||||
public:
|
||||
static std::optional<TargetFile> create(std::string_view target,
|
||||
std::string_view targetDir,
|
||||
std::string_view workingDir);
|
||||
|
||||
~TargetFile() { commit(); }
|
||||
|
||||
TargetFile(TargetFile&& other) = default;
|
||||
// move assignment deleted as non-trivial and not needed
|
||||
TargetFile& operator=(TargetFile&& other) = delete;
|
||||
|
||||
template <typename T>
|
||||
TargetFile& operator<<(T&& value) {
|
||||
errno = 0;
|
||||
out << value;
|
||||
checkOutput("write to file");
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
TargetFile(std::string_view target, std::string_view targetDir, std::string_view workingDir);
|
||||
|
||||
bool init();
|
||||
void checkOutput(const char* action);
|
||||
void commit();
|
||||
};
|
||||
|
||||
} // namespace codeql
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "swift/extractor/trap/TrapLabel.h"
|
||||
|
||||
namespace codeql {
|
||||
|
||||
// TrapArena has the responsibilities to allocate distinct trap #-labels
|
||||
// TODO this is now a small functionality that will be moved to code upcoming from other PRs
|
||||
class TrapArena {
|
||||
uint64_t id_{0};
|
||||
|
||||
public:
|
||||
template <typename Tag>
|
||||
TrapLabel<Tag> allocateLabel() {
|
||||
return TrapLabel<Tag>::unsafeCreateFromExplicitId(id_++);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace codeql
|
||||
@@ -1,19 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "swift/extractor/trap/TrapLabel.h"
|
||||
#include "swift/extractor/infra/TargetFile.h"
|
||||
|
||||
namespace codeql {
|
||||
|
||||
// Sink for trap emissions and label assignments. This abstracts away `ofstream` operations
|
||||
// like `ofstream`, an explicit bool operator is provided, that return false if something
|
||||
// went wrong
|
||||
// TODO better error handling
|
||||
class TrapOutput {
|
||||
std::ostream& out_;
|
||||
// Abstracts a given trap output file, with its own universe of trap labels
|
||||
class TrapDomain {
|
||||
TargetFile& out_;
|
||||
|
||||
public:
|
||||
explicit TrapOutput(std::ostream& out) : out_{out} {}
|
||||
explicit TrapDomain(TargetFile& out) : out_{out} {}
|
||||
|
||||
template <typename Entry>
|
||||
void emit(const Entry& e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void debug(const Args&... args) {
|
||||
print("/* DEBUG:");
|
||||
print(args...);
|
||||
print("*/");
|
||||
}
|
||||
|
||||
template <typename Tag>
|
||||
TrapLabel<Tag> createLabel() {
|
||||
auto ret = allocateLabel<Tag>();
|
||||
assignStar(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename Tag, typename... Args>
|
||||
TrapLabel<Tag> createLabel(Args&&... args) {
|
||||
auto ret = allocateLabel<Tag>();
|
||||
assignKey(ret, std::forward<Args>(args)...);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t id_{0};
|
||||
|
||||
template <typename Tag>
|
||||
TrapLabel<Tag> allocateLabel() {
|
||||
return TrapLabel<Tag>::unsafeCreateFromExplicitId(id_++);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void print(const Args&... args) {
|
||||
(out_ << ... << args) << '\n';
|
||||
}
|
||||
|
||||
template <typename Tag>
|
||||
void assignStar(TrapLabel<Tag> label) {
|
||||
@@ -33,23 +72,6 @@ class TrapOutput {
|
||||
(oss << ... << keyParts);
|
||||
assignKey(label, oss.str());
|
||||
}
|
||||
|
||||
template <typename Entry>
|
||||
void emit(const Entry& e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void debug(const Args&... args) {
|
||||
out_ << "/* DEBUG:\n";
|
||||
(out_ << ... << args) << "\n*/\n";
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename... Args>
|
||||
void print(const Args&... args) {
|
||||
(out_ << ... << args) << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace codeql
|
||||
@@ -611,11 +611,11 @@ void ExprVisitor::fillAbstractClosureExpr(const swift::AbstractClosureExpr& expr
|
||||
}
|
||||
|
||||
TrapLabel<ArgumentTag> ExprVisitor::emitArgument(const swift::Argument& arg) {
|
||||
auto argLabel = dispatcher_.createLabel<ArgumentTag>();
|
||||
assert(arg.getExpr() && "Argument has getExpr");
|
||||
dispatcher_.emit(
|
||||
ArgumentsTrap{argLabel, arg.getLabel().str().str(), dispatcher_.fetchLabel(arg.getExpr())});
|
||||
return argLabel;
|
||||
auto entry = dispatcher_.createUncachedEntry(arg);
|
||||
entry.label = arg.getLabel().str().str();
|
||||
entry.expr = dispatcher_.fetchLabel(arg.getExpr());
|
||||
dispatcher_.emit(entry);
|
||||
return entry.id;
|
||||
}
|
||||
|
||||
void ExprVisitor::emitImplicitConversionExpr(swift::ImplicitConversionExpr* expr,
|
||||
|
||||
@@ -7,26 +7,22 @@ void StmtVisitor::visitLabeledStmt(swift::LabeledStmt* stmt) {
|
||||
emitLabeledStmt(stmt, label);
|
||||
}
|
||||
|
||||
void StmtVisitor::visitStmtCondition(swift::StmtCondition* cond) {
|
||||
auto label = dispatcher_.assignNewLabel(cond);
|
||||
dispatcher_.emit(StmtConditionsTrap{label});
|
||||
unsigned index = 0;
|
||||
for (const auto& cond : *cond) {
|
||||
auto condLabel = dispatcher_.createLabel<ConditionElementTag>();
|
||||
dispatcher_.attachLocation(cond, condLabel);
|
||||
dispatcher_.emit(ConditionElementsTrap{condLabel});
|
||||
dispatcher_.emit(StmtConditionElementsTrap{label, index++, condLabel});
|
||||
if (auto boolean = cond.getBooleanOrNull()) {
|
||||
auto elementLabel = dispatcher_.fetchLabel(boolean);
|
||||
dispatcher_.emit(ConditionElementBooleansTrap{condLabel, elementLabel});
|
||||
} else if (auto pattern = cond.getPatternOrNull()) {
|
||||
auto patternLabel = dispatcher_.fetchLabel(pattern);
|
||||
auto initilizerLabel = dispatcher_.fetchLabel(cond.getInitializer());
|
||||
dispatcher_.emit(ConditionElementPatternsTrap{condLabel, patternLabel});
|
||||
dispatcher_.emit(ConditionElementInitializersTrap{condLabel, initilizerLabel});
|
||||
}
|
||||
/// TODO: Implement availability
|
||||
codeql::StmtCondition StmtVisitor::translateStmtCondition(const swift::StmtCondition& cond) {
|
||||
auto entry = dispatcher_.createEntry(cond);
|
||||
entry.elements = dispatcher_.fetchRepeatedLabels(cond);
|
||||
return entry;
|
||||
}
|
||||
|
||||
codeql::ConditionElement StmtVisitor::translateStmtConditionElement(
|
||||
const swift::StmtConditionElement& element) {
|
||||
auto entry = dispatcher_.createEntry(element);
|
||||
if (auto boolean = element.getBooleanOrNull()) {
|
||||
entry.boolean = dispatcher_.fetchLabel(boolean);
|
||||
} else if (auto pattern = element.getPatternOrNull()) {
|
||||
entry.pattern = dispatcher_.fetchLabel(pattern);
|
||||
entry.initializer = dispatcher_.fetchLabel(element.getInitializer());
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
void StmtVisitor::visitLabeledConditionalStmt(swift::LabeledConditionalStmt* stmt) {
|
||||
|
||||
@@ -10,7 +10,9 @@ class StmtVisitor : public AstVisitorBase<StmtVisitor> {
|
||||
using AstVisitorBase<StmtVisitor>::AstVisitorBase;
|
||||
|
||||
void visitLabeledStmt(swift::LabeledStmt* stmt);
|
||||
void visitStmtCondition(swift::StmtCondition* cond);
|
||||
codeql::StmtCondition translateStmtCondition(const swift::StmtCondition& cond);
|
||||
codeql::ConditionElement translateStmtConditionElement(
|
||||
const swift::StmtConditionElement& element);
|
||||
void visitLabeledConditionalStmt(swift::LabeledConditionalStmt* stmt);
|
||||
void visitCaseLabelItem(swift::CaseLabelItem* labelItem);
|
||||
void visitBraceStmt(swift::BraceStmt* stmt);
|
||||
|
||||
@@ -22,7 +22,12 @@ class SwiftVisitor : private SwiftDispatcher {
|
||||
private:
|
||||
void visit(swift::Decl* decl) override { declVisitor.visit(decl); }
|
||||
void visit(swift::Stmt* stmt) override { stmtVisitor.visit(stmt); }
|
||||
void visit(swift::StmtCondition* cond) override { stmtVisitor.visitStmtCondition(cond); }
|
||||
void visit(const swift::StmtCondition* cond) override {
|
||||
emit(stmtVisitor.translateStmtCondition(*cond));
|
||||
}
|
||||
void visit(const swift::StmtConditionElement* element) override {
|
||||
emit(stmtVisitor.translateStmtConditionElement(*element));
|
||||
}
|
||||
void visit(swift::CaseLabelItem* item) override { stmtVisitor.visitCaseLabelItem(item); }
|
||||
void visit(swift::Expr* expr) override { exprVisitor.visit(expr); }
|
||||
void visit(swift::Pattern* pattern) override { patternVisitor.visit(pattern); }
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// generated by codegen/codegen.py
|
||||
import codeql.swift.elements.Element
|
||||
import codeql.swift.elements.expr.Expr
|
||||
import codeql.swift.elements.Locatable
|
||||
|
||||
class ArgumentBase extends @argument, Element {
|
||||
class ArgumentBase extends @argument, Locatable {
|
||||
override string getAPrimaryQlClass() { result = "Argument" }
|
||||
|
||||
string getLabel() { arguments(this, result, _) }
|
||||
|
||||
@@ -13,8 +13,7 @@ sourceLocationPrefix(
|
||||
// from codegen/schema.yml
|
||||
|
||||
@element =
|
||||
@argument
|
||||
| @callable
|
||||
@callable
|
||||
| @file
|
||||
| @generic_context
|
||||
| @iterable_decl_context
|
||||
@@ -34,7 +33,8 @@ files(
|
||||
);
|
||||
|
||||
@locatable =
|
||||
@ast_node
|
||||
@argument
|
||||
| @ast_node
|
||||
| @condition_element
|
||||
;
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| dot_syntax_call.swift:6:1:6:3 | call to foo(_:_:) | 0 | : X.Type |
|
||||
| dot_syntax_call.swift:7:1:7:3 | call to bar() | 0 | : X.Type |
|
||||
| dot_syntax_call.swift:6:1:6:3 | call to foo(_:_:) | 0 | dot_syntax_call.swift:6:1:6:1 | : X.Type |
|
||||
| dot_syntax_call.swift:7:1:7:3 | call to bar() | 0 | dot_syntax_call.swift:7:1:7:1 | : X.Type |
|
||||
|
||||
Reference in New Issue
Block a user