Clear Project (#40)
This commit is contained in:
9
.github/workflows/cmake.yml
vendored
9
.github/workflows/cmake.yml
vendored
@@ -55,7 +55,7 @@ jobs:
|
||||
if: matrix.os == 'ubuntu-24.04'
|
||||
run: |
|
||||
mkdir -p ./.llvm
|
||||
curl -L "https://github.com/clice-project/llvm-binary/releases/download/20.0.0/x86_64-linux-gnu-release.tar.xz" | tar -xJ -C ./.llvm
|
||||
curl -L "https://github.com/clice-project/llvm-binary/releases/download/20.0.0/x86_64-linux-gnu-debug.tar.xz" | tar -xJ -C ./.llvm
|
||||
|
||||
- name: Setup llvm binary
|
||||
if: matrix.os == 'windows-2022'
|
||||
@@ -66,8 +66,15 @@ jobs:
|
||||
- name: Setup msvc sysroot for cmake
|
||||
if: matrix.os == 'windows-2022'
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Build clice
|
||||
if: matrix.os == 'ubuntu-24.04'
|
||||
run: |
|
||||
cmake --preset debug
|
||||
cmake --build --preset debug
|
||||
|
||||
- name: Build clice
|
||||
if: matrix.os == 'windows-2022'
|
||||
run: |
|
||||
cmake --preset release
|
||||
cmake --build --preset release
|
||||
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -31,14 +31,11 @@
|
||||
*.out
|
||||
*.app
|
||||
|
||||
.txt
|
||||
.cache/
|
||||
note.md
|
||||
temp*
|
||||
/build*
|
||||
notes
|
||||
build*
|
||||
notes/
|
||||
temp/
|
||||
.llvm/
|
||||
.cache/
|
||||
.llvm*/
|
||||
.xmake/
|
||||
|
||||
.vscode/
|
||||
|
||||
|
||||
59
.vscode/launch.json
vendored
59
.vscode/launch.json
vendored
@@ -1,59 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug",
|
||||
"program": "${workspaceFolder}/build/bin/clice",
|
||||
"args": [
|
||||
"--config=${workspaceFolder}/docs/clice.toml"
|
||||
],
|
||||
"preLaunchTask": "Build Dev"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Release",
|
||||
"program": "${workspaceFolder}/build-release/bin/clice",
|
||||
"args": [
|
||||
"--config=${workspaceFolder}/docs/clice.toml"
|
||||
],
|
||||
"preLaunchTask": "Build Release"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Integration Test",
|
||||
"program": "${workspaceFolder}/build/bin/integration_tests",
|
||||
"args": [
|
||||
"--execute=${workspaceFolder}/build/bin/clice",
|
||||
"--source=${workspaceFolder}/tests",
|
||||
],
|
||||
"preLaunchTask": "Build Dev"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Test",
|
||||
"program": "${workspaceFolder}/build/bin/unit_tests",
|
||||
"args": [
|
||||
"--test-dir=${workspaceFolder}/tests",
|
||||
"--resource-dir=${workspaceFolder}/.llvm/lib/clang/20",
|
||||
"--gtest_filter=${input:filter}"
|
||||
],
|
||||
"preLaunchTask": "Build Dev Test"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "filter",
|
||||
"type": "promptString",
|
||||
"description": "Filter for test names",
|
||||
"default": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
26
.vscode/tasks.json
vendored
26
.vscode/tasks.json
vendored
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build Dev",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/scripts/build-dev.sh && cmake --build ${workspaceFolder}/build -j",
|
||||
"problemMatcher": [],
|
||||
"group": "build",
|
||||
},
|
||||
{
|
||||
"label": "Build Release",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/scripts/build-release.sh && cmake --build ${workspaceFolder}/build -j",
|
||||
"problemMatcher": [],
|
||||
"group": "build",
|
||||
},
|
||||
{
|
||||
"label": "Build Dev Test",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}/scripts/build-dev-test.sh && cmake --build ${workspaceFolder}/build -j",
|
||||
"problemMatcher": [],
|
||||
"group": "build",
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -82,6 +82,7 @@ target_link_directories(clice-core PUBLIC "${LLVM_INSTALL_PATH}/lib")
|
||||
if(NOT WIN32)
|
||||
target_link_libraries(clice-core PUBLIC
|
||||
LLVMSupport
|
||||
LLVMFrontendOpenMP
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
|
||||
@@ -28,12 +28,25 @@
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"displayName": "Config Debug",
|
||||
"description": "Sets debug build type",
|
||||
"inherits": "config-base",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "release",
|
||||
"configurePreset": "release"
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"configurePreset": "debug"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -47,23 +47,15 @@ struct TextDocumentPositionParams {
|
||||
Position position;
|
||||
};
|
||||
|
||||
|
||||
struct MarkupKind {
|
||||
std::string_view m_value;
|
||||
|
||||
constexpr MarkupKind(std::string_view value) : m_value(value) {}
|
||||
|
||||
constexpr static std::string_view PlainText = "plaintext";
|
||||
constexpr static std::string_view Markdown = "markdown";
|
||||
};
|
||||
using MarkupKind = string;
|
||||
|
||||
struct MarkupContent {
|
||||
/// The type of the Markup.
|
||||
MarkupKind kind;
|
||||
MarkupKind kind = "markdown";
|
||||
|
||||
/// The content itself.
|
||||
string value;
|
||||
};
|
||||
};
|
||||
|
||||
struct DidOpenTextDocumentParams {
|
||||
/// The document that was opened.
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "Feature/SignatureHelp.h"
|
||||
#include "Feature/CodeAction.h"
|
||||
#include "Feature/Formatting.h"
|
||||
#include "Support/Support.h"
|
||||
|
||||
namespace clice::proto {
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
#include <Basic/Location.h>
|
||||
#include <clang/Basic/SourceLocation.h>
|
||||
#include "Basic/Location.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
@@ -15,7 +13,8 @@ public:
|
||||
using SourceDirMapping = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
/// Construct a `SourceConverter` with the specified encoding kind and empty source map.
|
||||
explicit SourceConverter(proto::PositionEncodingKind kind) : kind(kind), sourceMap() {}
|
||||
explicit SourceConverter(proto::PositionEncodingKind kind = proto::PositionEncodingKind::UTF8) :
|
||||
kind(kind), sourceMap() {}
|
||||
|
||||
SourceConverter(proto::PositionEncodingKind kind, SourceDirMapping sourceMap) :
|
||||
kind(kind), sourceMap(std::move(sourceMap)) {}
|
||||
@@ -30,7 +29,8 @@ public:
|
||||
/// Convert a clang::SourceLocation to a proto::Position according to the
|
||||
/// specified encoding kind. Note that `SourceLocation` in clang is 1-based and
|
||||
/// is always encoded in UTF-8.
|
||||
proto::Position toPosition(llvm::StringRef content, clang::SourceLocation location,
|
||||
proto::Position toPosition(llvm::StringRef content,
|
||||
clang::SourceLocation location,
|
||||
const clang::SourceManager& SM) const;
|
||||
|
||||
/// Same as above, but content is retrieved from the `SourceManager`.
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
/// Convert a real path of a file to URI. Crash if failed.
|
||||
static proto::DocumentUri toURI(llvm::StringRef fspath);
|
||||
|
||||
/// Convert a file URI to real path with `clice::fs::real_path`. Crash if failed.
|
||||
/// Convert a file URI to real path with `clice::fs::real_path`. Crash if failed.
|
||||
static std::string toPath(llvm::StringRef uri);
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <clang/Lex/Preprocessor.h>
|
||||
#include <clang/AST/ASTTypeTraits.h>
|
||||
#include <clang/AST/RecursiveASTVisitor.h>
|
||||
#include <clang/Frontend/CompilerInstance.h>
|
||||
#include <clang/Frontend/FrontendActions.h>
|
||||
#include <clang/Tooling/Syntax/Tokens.h>
|
||||
#include <clang/Sema/Sema.h>
|
||||
#include "Support/Support.h"
|
||||
|
||||
#include <Support/Support.h>
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "clang/AST/ASTTypeTraits.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Tooling/Syntax/Tokens.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
|
||||
namespace std {
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include "Resolver.h"
|
||||
#include "Directive.h"
|
||||
|
||||
#include "Support/Error.h"
|
||||
#include "Basic/Location.h"
|
||||
#include "Support/Error.h"
|
||||
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Compiler/Clang.h>
|
||||
#include "Clang.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
@@ -8,7 +8,8 @@ class DiagnosticCollector : public clang::DiagnosticConsumer {
|
||||
public:
|
||||
void BeginSourceFile(const clang::LangOptions& Opts, const clang::Preprocessor* PP) override;
|
||||
|
||||
void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic& Info) override;
|
||||
void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel,
|
||||
const clang::Diagnostic& Info) override;
|
||||
|
||||
void EndSourceFile() override;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Compiler/Clang.h>
|
||||
#include "Clang.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Compiler/Clang.h>
|
||||
#include "Clang.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Compiler/Clang.h>
|
||||
#include "Clang.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <Compiler/Clang.h>
|
||||
#include "Clang.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Basic/Document.h>
|
||||
#include <Support/JSON.h>
|
||||
#include "Basic/Document.h"
|
||||
#include "Support/JSON.h"
|
||||
|
||||
namespace clice {
|
||||
struct CompilationParams;
|
||||
|
||||
@@ -137,7 +137,8 @@ namespace feature {
|
||||
json::Value inlayHintCapability(json::Value InlayHintClientCapabilities);
|
||||
|
||||
/// Compute inlay hints for a document in given range and config.
|
||||
proto::InlayHintsResult inlayHints(proto::InlayHintParams param, ASTInfo& info,
|
||||
proto::InlayHintsResult inlayHints(proto::InlayHintParams param,
|
||||
ASTInfo& info,
|
||||
const SourceConverter& converter,
|
||||
const config::InlayHintOption& config);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Basic/Location.h>
|
||||
#include "Basic/Location.h"
|
||||
|
||||
namespace clice::proto {
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Support/Format.h>
|
||||
#include <Support/FileSystem.h>
|
||||
#include <source_location>
|
||||
#include "Support/Format.h"
|
||||
#include "Support/FileSystem.h"
|
||||
|
||||
namespace clice::log {
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
#include <compare>
|
||||
#include <ranges>
|
||||
|
||||
#include <llvm/ADT/ArrayRef.h>
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
#include <llvm/ADT/SmallVector.h>
|
||||
#include <llvm/ADT/SmallString.h>
|
||||
#include <llvm/ADT/StringMap.h>
|
||||
#include <llvm/ADT/StringExtras.h>
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Support/ADT.h"
|
||||
#include "ADT.h"
|
||||
#include "Struct.h"
|
||||
|
||||
namespace clice::refl {
|
||||
@@ -38,7 +38,7 @@ struct Equal<std::vector<T>> {
|
||||
}
|
||||
};
|
||||
|
||||
template <reflectable T>
|
||||
template <reflectable_struct T>
|
||||
requires (!requires(T lhs, T rhs) {
|
||||
{ lhs == rhs } -> std::convertible_to<bool>;
|
||||
})
|
||||
@@ -83,7 +83,7 @@ struct Less<std::vector<T>> {
|
||||
}
|
||||
};
|
||||
|
||||
template <reflectable T>
|
||||
template <reflectable_struct T>
|
||||
requires (!requires(T lhs, T rhs) {
|
||||
{ lhs < rhs } -> std::convertible_to<bool>;
|
||||
})
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <tuple>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
#include <source_location>
|
||||
|
||||
#include "TypeTraits.h"
|
||||
#include "Support/TypeTraits.h"
|
||||
|
||||
namespace clice::refl {
|
||||
|
||||
@@ -64,7 +62,7 @@ template <typename Derived, bool is_bitmask = false, typename underlying = uint8
|
||||
class Enum {
|
||||
public:
|
||||
/// Tag to indicate this is a special enum.
|
||||
constexpr inline static bool is_special_enum_v = true;
|
||||
constexpr inline static bool reflectable_enum = true;
|
||||
|
||||
using underlying_type = underlying;
|
||||
|
||||
@@ -89,12 +87,22 @@ public:
|
||||
return m_Value;
|
||||
}
|
||||
|
||||
/// Get the enum value.
|
||||
constexpr auto kind() const {
|
||||
return static_cast<typename Derived::Kind>(m_Value);
|
||||
}
|
||||
|
||||
/// Get the name of the enum.
|
||||
constexpr std::string_view name() const {
|
||||
using E = typename Derived::Kind;
|
||||
return refl::enum_name<E, begin(), end()>(static_cast<E>(m_Value));
|
||||
}
|
||||
|
||||
template <std::same_as<typename Derived::Kind>... Kinds>
|
||||
constexpr bool is_one_of(Kinds... kinds) const {
|
||||
return ((m_Value == underlying_value(kinds)) || ...);
|
||||
}
|
||||
|
||||
constexpr explicit operator bool () const {
|
||||
return m_Value != invalid();
|
||||
}
|
||||
@@ -138,7 +146,7 @@ template <typename Derived, typename underlying>
|
||||
class Enum<Derived, true, underlying> {
|
||||
public:
|
||||
/// Tag to indicate this is a special enum.
|
||||
constexpr inline static bool is_special_enum_v = true;
|
||||
constexpr inline static bool reflectable_enum = true;
|
||||
|
||||
using underlying_type = underlying;
|
||||
|
||||
@@ -250,7 +258,7 @@ template <typename Derived, typename underlying>
|
||||
class Enum<Derived, false, underlying> {
|
||||
public:
|
||||
/// Tag to indicate this is a special enum.
|
||||
constexpr inline static bool is_special_enum_v = true;
|
||||
constexpr inline static bool reflectable_enum = true;
|
||||
|
||||
using underlying_type = underlying;
|
||||
|
||||
@@ -259,8 +267,16 @@ public:
|
||||
requires { Derived::All; },
|
||||
"Derived enum must define all possible enum values.");
|
||||
|
||||
assert(std::ranges::any_of(Derived::All, [&](auto v) { return v == value; }) &&
|
||||
"Invalid enum value.");
|
||||
auto check = [](auto value) {
|
||||
for(auto kind: Derived::All) {
|
||||
if(kind == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
assert(check(value) && "Invalid enum value.");
|
||||
}
|
||||
|
||||
constexpr Enum(const Enum&) = default;
|
||||
@@ -276,9 +292,9 @@ private:
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept special_enum = requires {
|
||||
T::is_special_enum_v;
|
||||
requires T::is_special_enum_v;
|
||||
concept reflectable_enum = requires {
|
||||
T::reflectable_enum;
|
||||
requires T::reflectable_enum;
|
||||
};
|
||||
|
||||
} // namespace clice::refl
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <llvm/Support/Error.h>
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <llvm/Support/Path.h>
|
||||
#include <llvm/Support/FileSystem.h>
|
||||
#include <llvm/Support/VirtualFileSystem.h>
|
||||
#include <llvm/Support/MemoryBuffer.h>
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/VirtualFileSystem.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "Support/Error.h"
|
||||
#include "Support/JSON.h"
|
||||
#include "Support/Ranges.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
@@ -12,30 +13,11 @@ void print(std::format_string<Args...> fmt, Args&&... args) {
|
||||
llvm::outs() << std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
constexpr inline bool is_formattable_v = false;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
constexpr inline bool is_formattable_v<T, Args...> =
|
||||
std::is_convertible_v<T, std::format_string<Args...>>;
|
||||
|
||||
template <typename... Args>
|
||||
requires (!is_formattable_v<Args...>)
|
||||
void print(Args&&... args) {
|
||||
((llvm::outs() << std::format("{}", std::make_format_args(args)) << " "), ...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void println(std::format_string<Args...> fmt, Args&&... args) {
|
||||
llvm::outs() << std::vformat(fmt.get(), std::make_format_args(args...)) << '\n';
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
requires (!is_formattable_v<Args...>)
|
||||
void println(Args&&... args) {
|
||||
((llvm::outs() << std::format("{}", std::make_format_args(args)) << " "), ...) << '\n';
|
||||
}
|
||||
|
||||
} // namespace clice
|
||||
|
||||
template <>
|
||||
@@ -53,6 +35,21 @@ struct std::formatter<llvm::StringRef> : std::formatter<std::string_view> {
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
struct std::formatter<llvm::SmallString<N>> : std::formatter<llvm::StringRef> {
|
||||
using Base = std::formatter<llvm::StringRef>;
|
||||
|
||||
template <typename ParseContext>
|
||||
constexpr auto parse(ParseContext& ctx) {
|
||||
return Base::parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const llvm::SmallString<N>& s, FormatContext& ctx) const {
|
||||
return Base::format(llvm::StringRef(s), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<llvm::Error> : std::formatter<llvm::StringRef> {
|
||||
using Base = std::formatter<llvm::StringRef>;
|
||||
@@ -71,21 +68,6 @@ struct std::formatter<llvm::Error> : std::formatter<llvm::StringRef> {
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
struct std::formatter<llvm::SmallString<N>> : std::formatter<llvm::StringRef> {
|
||||
using Base = std::formatter<llvm::StringRef>;
|
||||
|
||||
template <typename ParseContext>
|
||||
constexpr auto parse(ParseContext& ctx) {
|
||||
return Base::parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const llvm::SmallString<N>& s, FormatContext& ctx) const {
|
||||
return Base::format(llvm::StringRef(s), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<clice::json::Value> : std::formatter<llvm::StringRef> {
|
||||
using Base = std::formatter<llvm::StringRef>;
|
||||
@@ -104,8 +86,7 @@ struct std::formatter<clice::json::Value> : std::formatter<llvm::StringRef> {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
requires clice::refl::special_enum<E>
|
||||
template <clice::refl::reflectable_enum E>
|
||||
struct std::formatter<E> : std::formatter<std::string_view> {
|
||||
using Base = std::formatter<std::string_view>;
|
||||
|
||||
@@ -119,3 +100,66 @@ struct std::formatter<E> : std::formatter<std::string_view> {
|
||||
return Base::format(e.name(), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
namespace clice {
|
||||
|
||||
/// Dump object to string for debugging. Note that it is not efficient
|
||||
/// and should not be used except for debugging.
|
||||
template <typename Object>
|
||||
std::string dump(const Object& object) {
|
||||
if constexpr(std::is_fundamental_v<Object>) {
|
||||
return std::format("{}", object);
|
||||
} else if constexpr(std::is_same_v<Object, std::string> ||
|
||||
std::is_same_v<Object, std::string_view> ||
|
||||
std::is_same_v<Object, llvm::StringRef>) {
|
||||
return std::format("\"{}\"", object);
|
||||
} else if constexpr(ranges::range<Object>) {
|
||||
constexpr bool is_sequence = sequence_range<Object>;
|
||||
std::string result = is_sequence ? "[" : "{";
|
||||
if constexpr(map_range<Object>) {
|
||||
for(auto&& [key, value]: object) {
|
||||
result += std::format("\"{}\": {}, ", dump(key), dump(value));
|
||||
}
|
||||
} else {
|
||||
for(auto&& value: object) {
|
||||
result += std::format("{}, ", dump(value));
|
||||
}
|
||||
}
|
||||
if(!object.empty()) {
|
||||
result.pop_back();
|
||||
result.pop_back();
|
||||
}
|
||||
result += is_sequence ? "]" : "}";
|
||||
return result;
|
||||
} else if constexpr(std::is_enum_v<Object>) {
|
||||
return std::format("{}", refl::enum_name(object));
|
||||
} else if constexpr(refl::reflectable_enum<Object>) {
|
||||
return std::format("\"{}\"", object);
|
||||
} else if constexpr(refl::reflectable_struct<Object>) {
|
||||
std::string result = "{";
|
||||
refl::foreach(object, [&](auto name, auto value) {
|
||||
result += std::format("\"{}\": {}, ", name, dump(value));
|
||||
});
|
||||
if(refl::member_count<Object>() != 0) {
|
||||
result.pop_back();
|
||||
result.pop_back();
|
||||
}
|
||||
result += "}";
|
||||
return result;
|
||||
} else {
|
||||
static_assert(dependent_false<Object>, "Cannot dump object");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Object>
|
||||
std::string pretty_dump(const Object& object, std::size_t indent = 2) {
|
||||
auto repr = dump(object);
|
||||
auto json = json::parse(repr);
|
||||
if(!json) {
|
||||
std::terminate();
|
||||
}
|
||||
llvm::SmallString<128> buffer = {std::format("{{0:{}}}", indent)};
|
||||
return llvm::formatv(buffer.c_str(), *json);
|
||||
}
|
||||
|
||||
} // namespace clice
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "llvm/Support/HashBuilder.h"
|
||||
|
||||
#include "Struct.h"
|
||||
#include "llvm/Support/HashBuilder.h"
|
||||
|
||||
namespace clice::refl {
|
||||
|
||||
@@ -30,12 +29,11 @@ struct Hash<std::vector<T>> {
|
||||
};
|
||||
};
|
||||
|
||||
template <reflectable T>
|
||||
template <reflectable_struct T>
|
||||
struct Hash<T> {
|
||||
static llvm::hash_code hash(const T& value) {
|
||||
llvm::SmallVector<llvm::hash_code, 8> hashes;
|
||||
foreach(value,
|
||||
[&](auto, const auto& member) { hashes.emplace_back(refl::hash(member)); });
|
||||
foreach(value, [&](auto, const auto& member) { hashes.emplace_back(refl::hash(member)); });
|
||||
return llvm::hash_combine_range(hashes.begin(), hashes.end());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <ranges>
|
||||
#include <string_view>
|
||||
|
||||
#include "ADT.h"
|
||||
#include "Ranges.h"
|
||||
#include "TypeTraits.h"
|
||||
#include "Enum.h"
|
||||
#include "Struct.h"
|
||||
@@ -86,6 +87,11 @@ T deserialize(const json::Value& value, Serdes&&... serdes) {
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
struct Serde<json::Value> {
|
||||
/// Never use json::Value as a serde.
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Serde<std::nullptr_t> {
|
||||
static json::Value serialize(std::nullptr_t) {
|
||||
@@ -99,16 +105,14 @@ struct Serde<std::nullptr_t> {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Serde<std::string> {
|
||||
using V = std::string;
|
||||
|
||||
static json::Value serialize(const V& v) {
|
||||
return json::Value(v);
|
||||
struct Serde<std::nullopt_t> {
|
||||
static json::Value serialize(std::nullopt_t) {
|
||||
return json::Value(nullptr);
|
||||
}
|
||||
|
||||
static V deserialize(const json::Value& value) {
|
||||
assert(value.kind() == json::Value::String && "Expect a string");
|
||||
return value.getAsString().value().str();
|
||||
static std::nullopt_t deserialize(const json::Value& value) {
|
||||
assert(value.kind() == json::Value::Null && "Expect null");
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -162,14 +166,9 @@ struct Serde<F> {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Serde<double> {
|
||||
static json::Value serialize(float v) {
|
||||
return json::Value(v);
|
||||
}
|
||||
|
||||
static float deserialize(const json::Value& value) {
|
||||
assert(value.kind() == json::Value::Number && "Expect number");
|
||||
return value.getAsNumber().value();
|
||||
struct Serde<const char*> {
|
||||
static json::Value serialize(const char* v) {
|
||||
return json::Value(llvm::StringRef(v));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -181,16 +180,16 @@ struct Serde<char[N]> {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Serde<llvm::StringRef> {
|
||||
using V = llvm::StringRef;
|
||||
struct Serde<std::string> {
|
||||
using V = std::string;
|
||||
|
||||
static json::Value serialize(const V& v) {
|
||||
return json::Value(v.str());
|
||||
return json::Value(v);
|
||||
}
|
||||
|
||||
static V deserialize(const json::Value& value) {
|
||||
assert(value.kind() == json::Value::String && "Expect string");
|
||||
return value.getAsString().value();
|
||||
assert(value.kind() == json::Value::String && "Expect a string");
|
||||
return value.getAsString().value().str();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -208,78 +207,132 @@ struct Serde<std::string_view> {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct Serde<std::array<T, N>> {
|
||||
using V = std::array<T, N>;
|
||||
template <>
|
||||
struct Serde<llvm::StringRef> {
|
||||
using V = llvm::StringRef;
|
||||
|
||||
constexpr inline static bool stateful = stateful_serde<T>;
|
||||
|
||||
template <typename... Serdes>
|
||||
static json::Value serialize(const V& v, Serdes&&... serdes) {
|
||||
json::Array array;
|
||||
for(const auto& element: v) {
|
||||
array.push_back(json::serialize(element, std::forward<Serdes>(serdes)...));
|
||||
}
|
||||
return array;
|
||||
static json::Value serialize(const V& v) {
|
||||
return json::Value(v.str());
|
||||
}
|
||||
|
||||
template <typename... Serdes>
|
||||
static V deserialize(const json::Value& value, Serdes&&... serdes) {
|
||||
assert(value.kind() == json::Value::Array && "Expect array");
|
||||
V array;
|
||||
for(std::size_t i = 0; i < N; ++i) {
|
||||
array[i] =
|
||||
json::deserialize<T>((*value.getAsArray())[i], std::forward<Serdes>(serdes)...);
|
||||
}
|
||||
return array;
|
||||
static V deserialize(const json::Value& value) {
|
||||
assert(value.kind() == json::Value::String && "Expect string");
|
||||
return value.getAsString().value();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Serde<std::vector<T>> {
|
||||
using V = std::vector<T>;
|
||||
template <std::size_t N>
|
||||
struct Serde<llvm::SmallString<N>> {
|
||||
using V = llvm::SmallString<N>;
|
||||
|
||||
constexpr inline static bool stateful = stateful_serde<T>;
|
||||
|
||||
template <typename... Serdes>
|
||||
static json::Value serialize(const V& v, Serdes&&... serdes) {
|
||||
json::Array array;
|
||||
for(const auto& element: v) {
|
||||
array.push_back(json::serialize(element, std::forward<Serdes>(serdes)...));
|
||||
}
|
||||
return array;
|
||||
static json::Value serialize(const V& v) {
|
||||
return json::Value(v.str());
|
||||
}
|
||||
|
||||
template <typename... Serdes>
|
||||
static V deserialize(const json::Value& value, Serdes&&... serdes) {
|
||||
assert(value.kind() == json::Value::Array && "Expect array");
|
||||
V array;
|
||||
for(const auto& element: *value.getAsArray()) {
|
||||
array.emplace_back(json::deserialize<T>(element, std::forward<Serdes>(serdes)...));
|
||||
}
|
||||
return array;
|
||||
static V deserialize(const json::Value& value) {
|
||||
assert(value.kind() == json::Value::String && "Expect string");
|
||||
return V{value.getAsString().value().str()};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Serde<llvm::ArrayRef<T>> {
|
||||
using V = llvm::ArrayRef<T>;
|
||||
template <map_range Range>
|
||||
struct Serde<Range> {
|
||||
using key_type = typename Range::key_type;
|
||||
using mapped_type = typename Range::mapped_type;
|
||||
|
||||
constexpr inline static bool stateful = stateful_serde<T>;
|
||||
constexpr inline static bool stateful = stateful_serde<key_type> || stateful_serde<mapped_type>;
|
||||
|
||||
/// Only refl serialization.
|
||||
template <typename... Serdes>
|
||||
static json::Value serialize(const V& v, Serdes&&... serdes) {
|
||||
json::Array array;
|
||||
for(const auto& element: v) {
|
||||
array.push_back(json::serialize(element, std::forward<Serdes>(serdes)...));
|
||||
static json::Value serialize(const Range& range, Serdes&&... serdes) {
|
||||
json::Object object;
|
||||
for(const auto& [key, value]: range) {
|
||||
if constexpr(std::is_constructible_v<json::Object::key_type, decltype(key)>) {
|
||||
object.try_emplace(key, json::serialize(value, std::forward<Serdes>(serdes)...));
|
||||
} else {
|
||||
object.try_emplace(
|
||||
llvm::formatv("{}", json::serialize(key, std::forward<Serdes>(serdes)...)),
|
||||
json::serialize(value, std::forward<Serdes>(serdes)...));
|
||||
}
|
||||
}
|
||||
return array;
|
||||
return object;
|
||||
}
|
||||
|
||||
template <typename... Serdes>
|
||||
static Range deserialize(const json::Value& value, Serdes&&... serdes) {
|
||||
assert(value.kind() == json::Value::Object && "JSON must be object");
|
||||
Range range;
|
||||
for(auto& [name, value]: *value.getAsObject()) {
|
||||
if constexpr(std::is_constructible_v<key_type, decltype(name)>) {
|
||||
range.try_emplace(
|
||||
name,
|
||||
json::deserialize<mapped_type>(value, std::forward<Serdes>(serdes)...));
|
||||
} else {
|
||||
if(auto key = json::parse(name)) {
|
||||
range.try_emplace(
|
||||
json::deserialize<key_type>(std::move(*key),
|
||||
std::forward<Serdes>(serdes)...),
|
||||
json::deserialize<mapped_type>(value, std::forward<Serdes>(serdes)...));
|
||||
}
|
||||
}
|
||||
}
|
||||
return range;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
requires refl::special_enum<E>
|
||||
template <set_range Range>
|
||||
struct Serde<Range> {
|
||||
using key_type = typename Range::key_type;
|
||||
|
||||
constexpr inline static bool stateful = stateful_serde<key_type>;
|
||||
|
||||
template <typename... Serdes>
|
||||
static json::Value serialize(const Range& range, Serdes&&... serdes) {
|
||||
json::Array array;
|
||||
for(const auto& element: range) {
|
||||
array.emplace_back(json::serialize(element, std::forward<Serdes>(serdes)...));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
template <typename... Serdes>
|
||||
static Range deserialize(const json::Value& value, Serdes&&... serdes) {
|
||||
assert(value.kind() == json::Value::Array && "JSON must be array");
|
||||
Range range;
|
||||
for(auto& element: *value.getAsArray()) {
|
||||
range.emplace(json::deserialize<key_type>(element, std::forward<Serdes>(serdes)...));
|
||||
}
|
||||
return range;
|
||||
}
|
||||
};
|
||||
|
||||
template <sequence_range Range>
|
||||
struct Serde<Range> {
|
||||
using value_type = typename Range::value_type;
|
||||
|
||||
constexpr inline static bool stateful = stateful_serde<value_type>;
|
||||
|
||||
template <typename... Serdes>
|
||||
static json::Value serialize(const Range& range, Serdes&&... serdes) {
|
||||
json::Array array;
|
||||
for(const auto& element: range) {
|
||||
array.emplace_back(json::serialize(element, std::forward<Serdes>(serdes)...));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
template <typename... Serdes>
|
||||
static Range deserialize(const json::Value& value, Serdes&&... serdes) {
|
||||
assert(value.kind() == json::Value::Array && "JSON must be array");
|
||||
Range range;
|
||||
for(auto& element: *value.getAsArray()) {
|
||||
range.emplace_back(
|
||||
json::deserialize<value_type>(element, std::forward<Serdes>(serdes)...));
|
||||
}
|
||||
return range;
|
||||
}
|
||||
};
|
||||
|
||||
template <refl::reflectable_enum E>
|
||||
struct Serde<E> {
|
||||
static json::Value serialize(const E& e) {
|
||||
return json::Value(e.value());
|
||||
@@ -290,7 +343,7 @@ struct Serde<E> {
|
||||
}
|
||||
};
|
||||
|
||||
template <refl::reflectable T>
|
||||
template <refl::reflectable_struct T>
|
||||
struct Serde<T> {
|
||||
constexpr inline static bool stateful =
|
||||
refl::member_types<T>::apply([]<typename... Ts> { return (stateful_serde<Ts> || ...); });
|
||||
|
||||
42
include/Support/Ranges.h
Normal file
42
include/Support/Ranges.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <ranges>
|
||||
#include <concepts>
|
||||
|
||||
namespace clice {
|
||||
|
||||
namespace ranges = std::ranges;
|
||||
namespace views = std::views;
|
||||
|
||||
enum class RangeKind {
|
||||
Map = 0,
|
||||
Set,
|
||||
Sequence,
|
||||
Invalid,
|
||||
};
|
||||
|
||||
template <typename Range>
|
||||
constexpr inline RangeKind range_kind = [] {
|
||||
if constexpr(std::same_as<Range, std::remove_cvref_t<ranges::range_reference_t<Range>>>) {
|
||||
return RangeKind::Invalid;
|
||||
} else if constexpr(requires { typename Range::key_type; }) {
|
||||
if constexpr(requires { typename Range::mapped_type; }) {
|
||||
return RangeKind::Map;
|
||||
} else {
|
||||
return RangeKind::Set;
|
||||
}
|
||||
} else {
|
||||
return RangeKind::Sequence;
|
||||
}
|
||||
}();
|
||||
|
||||
template <typename Range>
|
||||
concept map_range = ranges::input_range<Range> && range_kind<Range> == RangeKind::Map;
|
||||
|
||||
template <typename Range>
|
||||
concept set_range = ranges::input_range<Range> && range_kind<Range> == RangeKind::Set;
|
||||
|
||||
template <typename Range>
|
||||
concept sequence_range = ranges::input_range<Range> && range_kind<Range> == RangeKind::Sequence;
|
||||
|
||||
} // namespace clice
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <string_view>
|
||||
#include <source_location>
|
||||
|
||||
#include "TypeTraits.h"
|
||||
#include "Support/TypeTraits.h"
|
||||
|
||||
namespace clice::refl {
|
||||
|
||||
@@ -42,10 +42,18 @@ struct wrapper {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct storage {
|
||||
inline static T value;
|
||||
union storage_t {
|
||||
char dummy;
|
||||
T value;
|
||||
|
||||
storage_t() {}
|
||||
|
||||
~storage_t() {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline storage_t<T> storage;
|
||||
|
||||
template <wrapper T>
|
||||
consteval auto member_name() {
|
||||
std::string_view name = std::source_location::current().function_name();
|
||||
@@ -70,133 +78,212 @@ consteval auto member_name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
constexpr inline auto to_string_literal_impl = [] {
|
||||
if constexpr(N == 0) {
|
||||
return std::array<char, 2>{'0', '\0'};
|
||||
} else {
|
||||
constexpr auto length = [] {
|
||||
std::size_t result = 0;
|
||||
for(std::size_t n = N; n; n /= 10) {
|
||||
++result;
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
|
||||
std::array<char, length + 1> result = {};
|
||||
for(std::size_t i = length; i; i /= 10) {
|
||||
result[i - 1] = '0' + N % 10;
|
||||
}
|
||||
result[length] = '\0';
|
||||
return result;
|
||||
}
|
||||
}();
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template <std::size_t N>
|
||||
constexpr std::string_view to_string_literal() {
|
||||
return {impl::to_string_literal_impl<N>.data()};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Struct {
|
||||
constexpr inline static bool reflectable =
|
||||
std::is_aggregate_v<std::remove_cvref_t<T>> &&
|
||||
std::is_default_constructible_v<std::remove_cvref_t<T>>;
|
||||
struct Struct;
|
||||
|
||||
constexpr static std::size_t member_count() {
|
||||
return impl::member_count<T>();
|
||||
}
|
||||
/// To check if the type is reflectable_struct.
|
||||
template <typename T>
|
||||
concept reflectable_struct = Struct<std::remove_cvref_t<T>>::reflectable_struct;
|
||||
|
||||
constexpr static T& instance() {
|
||||
return impl::storage<T>::value;
|
||||
}
|
||||
/// Get the member count of the type.
|
||||
template <typename T>
|
||||
constexpr static std::size_t member_count() {
|
||||
return Struct<std::remove_cvref_t<T>>::member_count;
|
||||
}
|
||||
|
||||
/// Get the all member names of the type.
|
||||
template <typename T>
|
||||
constexpr static auto& member_names() {
|
||||
return Struct<std::remove_cvref_t<T>>::member_names;
|
||||
}
|
||||
|
||||
/// Get the member name of the type at index N.
|
||||
template <typename T, std::size_t N>
|
||||
constexpr static std::string_view member_name() {
|
||||
return member_names<T>()[N];
|
||||
}
|
||||
|
||||
/// Get the member types of the type.
|
||||
template <std::size_t N, typename T>
|
||||
constexpr decltype(auto) member_value(T&& object) {
|
||||
return *std::get<N>(Struct<std::remove_cvref_t<T>>::collect_members(object));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using member_types =
|
||||
tuple_to_list_t<decltype(Struct<T>::collect_members(std::declval<T>())), std::remove_pointer_t>;
|
||||
|
||||
/// Specialize for aggregate class.
|
||||
template <typename T>
|
||||
requires std::is_aggregate_v<T>
|
||||
struct Struct<T> {
|
||||
constexpr inline static bool reflectable_struct = true;
|
||||
|
||||
constexpr inline static auto member_count = impl::member_count<T>();
|
||||
|
||||
template <typename Object>
|
||||
constexpr static auto collcet_members(Object&& object) {
|
||||
constexpr static auto collect_members(Object&& object) {
|
||||
// clang-format off
|
||||
constexpr std::size_t count = member_count();
|
||||
if constexpr(count == 0) {
|
||||
if constexpr (member_count == 0) {
|
||||
return std::tuple{};
|
||||
} else if constexpr(count == 1) {
|
||||
auto&& [a] = object;
|
||||
return std::tuple{&a};
|
||||
} else if constexpr(count == 2) {
|
||||
auto&& [a, b] = object;
|
||||
return std::tuple{&a, &b};
|
||||
} else if constexpr(count == 3) {
|
||||
auto&& [a, b, c] = object;
|
||||
return std::tuple{&a, &b, &c};
|
||||
} else if constexpr(count == 4) {
|
||||
auto&& [a, b, c, d] = object;
|
||||
return std::tuple{&a, &b, &c, &d};
|
||||
} else if constexpr(count == 5) {
|
||||
auto&& [a, b, c, d, e] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e};
|
||||
} else if constexpr(count == 6) {
|
||||
auto&& [a, b, c, d, e, f] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f};
|
||||
} else if constexpr(count == 7) {
|
||||
auto&& [a, b, c, d, e, f, g] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g};
|
||||
} else if constexpr(count == 8) {
|
||||
auto&& [a, b, c, d, e, f, g, h] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h};
|
||||
} else if constexpr(count == 9) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i};
|
||||
} else if constexpr(count == 10) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j};
|
||||
} else if constexpr(count == 11) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k};
|
||||
} else if constexpr(count == 12) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l};
|
||||
} else if constexpr(count == 13) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m};
|
||||
} else if constexpr(count == 14) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n};
|
||||
} else if constexpr(count == 15) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o};
|
||||
} else if constexpr(count == 16) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p};
|
||||
} else if constexpr(count == 17) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q};
|
||||
} else if constexpr(count == 18) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r};
|
||||
} else if constexpr(count == 19) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s};
|
||||
} else if constexpr(count == 20) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t};
|
||||
} else if constexpr(count == 21) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u};
|
||||
} else if constexpr(count == 22) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v};
|
||||
} else if constexpr(count == 23) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w};
|
||||
} else if constexpr(count == 24) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x};
|
||||
} else if constexpr(count == 25) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y};
|
||||
} else if constexpr(count == 26) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z};
|
||||
} else if constexpr(count == 27) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0};
|
||||
} else if constexpr(count == 28) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1};
|
||||
} else if constexpr(count == 29) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1, _2] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1, &_2};
|
||||
} else if constexpr(count == 30) {
|
||||
auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1, _2, _3] = object;
|
||||
return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1, &_2, &_3};
|
||||
} else if constexpr (member_count == 1) {
|
||||
auto&& [e1] = object;
|
||||
return std::tuple{ &e1 };
|
||||
} else if constexpr (member_count == 2) {
|
||||
auto&& [e1, e2] = object;
|
||||
return std::tuple{ &e1, &e2 };
|
||||
} else if constexpr (member_count == 3) {
|
||||
auto&& [e1, e2, e3] = object;
|
||||
return std::tuple{ &e1, &e2, &e3 };
|
||||
} else if constexpr (member_count == 4) {
|
||||
auto&& [e1, e2, e3, e4] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4 };
|
||||
} else if constexpr (member_count == 5) {
|
||||
auto&& [e1, e2, e3, e4, e5] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5 };
|
||||
} else if constexpr (member_count == 6) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6 };
|
||||
} else if constexpr (member_count == 7) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7 };
|
||||
} else if constexpr (member_count == 8) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8 };
|
||||
} else if constexpr (member_count == 9) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9 };
|
||||
} else if constexpr (member_count == 10) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10 };
|
||||
} else if constexpr (member_count == 11) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11 };
|
||||
} else if constexpr (member_count == 12) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12 };
|
||||
} else if constexpr (member_count == 13) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13 };
|
||||
} else if constexpr (member_count == 14) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14 };
|
||||
} else if constexpr (member_count == 15) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15 };
|
||||
} else if constexpr (member_count == 16) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16 };
|
||||
} else if constexpr (member_count == 17) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17 };
|
||||
} else if constexpr (member_count == 18) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18 };
|
||||
} else if constexpr (member_count == 19) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19 };
|
||||
} else if constexpr (member_count == 20) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20 };
|
||||
} else if constexpr (member_count == 21) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21 };
|
||||
} else if constexpr (member_count == 22) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22 };
|
||||
} else if constexpr (member_count == 23) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23 };
|
||||
} else if constexpr (member_count == 24) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24 };
|
||||
} else if constexpr (member_count == 25) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25 };
|
||||
} else if constexpr (member_count == 26) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26 };
|
||||
} else if constexpr (member_count == 27) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27 };
|
||||
} else if constexpr (member_count == 28) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28 };
|
||||
} else if constexpr (member_count == 29) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29 };
|
||||
} else if constexpr (member_count == 30) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30 };
|
||||
} else if constexpr (member_count == 31) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31 };
|
||||
} else if constexpr (member_count == 32) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31, &e32 };
|
||||
} else if constexpr (member_count == 33) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31, &e32, &e33 };
|
||||
} else if constexpr (member_count == 34) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33, e34] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31, &e32, &e33, &e34 };
|
||||
} else if constexpr (member_count == 35) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33, e34, e35] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31, &e32, &e33, &e34, &e35 };
|
||||
} else if constexpr (member_count == 36) {
|
||||
auto&& [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32, e33, e34, e35, e36] = object;
|
||||
return std::tuple{ &e1, &e2, &e3, &e4, &e5, &e6, &e7, &e8, &e9, &e10, &e11, &e12, &e13, &e14, &e15, &e16, &e17, &e18, &e19, &e20, &e21, &e22, &e23, &e24, &e25, &e26, &e27, &e28, &e29, &e30, &e31, &e32, &e33, &e34, &e35, &e36 };
|
||||
} else {
|
||||
static_assert(count <= 30, "Not supported member count");
|
||||
// For counts greater than 36, trigger a compile-time error
|
||||
static_assert(member_count <= 36, "Not supported member count");
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
};
|
||||
|
||||
/// To check if the type is reflectable.
|
||||
template <typename T>
|
||||
concept reflectable = Struct<T>::reflectable;
|
||||
constexpr inline static auto member_names = []<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||
if constexpr(member_count == 0) {
|
||||
return std::array<std::string_view, 1>{};
|
||||
} else {
|
||||
constexpr auto members = collect_members(impl::storage<T>.value);
|
||||
return std::array{impl::member_name<std::get<Is>(members)>()...};
|
||||
}
|
||||
}(std::make_index_sequence<member_count>{});
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
struct Inheritance : Ts... {};
|
||||
|
||||
/// Use to define a reflectable struct with inheritance.
|
||||
/// Use to define a reflectable_struct struct with inheritance.
|
||||
#define inherited_struct(name, ...) \
|
||||
struct name##Body; \
|
||||
using name = clice::refl::Inheritance<__VA_ARGS__, name##Body>; \
|
||||
@@ -204,29 +291,41 @@ struct Inheritance : Ts... {};
|
||||
|
||||
template <typename... Ts>
|
||||
struct Struct<Inheritance<Ts...>> {
|
||||
constexpr inline static bool reflectable = (refl::reflectable<Ts> && ...);
|
||||
constexpr inline static bool reflectable_struct = (refl::reflectable_struct<Ts> && ...);
|
||||
|
||||
constexpr static std::size_t member_count() {
|
||||
return (Struct<Ts>::member_count() + ...);
|
||||
}
|
||||
|
||||
constexpr static auto& instance() {
|
||||
return impl::storage<Inheritance<Ts...>>::value;
|
||||
}
|
||||
constexpr static std::size_t member_count = (Struct<Ts>::member_count + ...);
|
||||
|
||||
template <typename Object>
|
||||
constexpr static auto collcet_members(Object&& object) {
|
||||
constexpr static auto collect_members(Object&& object) {
|
||||
if constexpr(std::is_const_v<std::remove_reference_t<Object>>) {
|
||||
return std::tuple_cat(Struct<Ts>::collcet_members(static_cast<const Ts&>(object))...);
|
||||
return std::tuple_cat(Struct<Ts>::collect_members(static_cast<const Ts&>(object))...);
|
||||
} else {
|
||||
return std::tuple_cat(Struct<Ts>::collcet_members(static_cast<Ts&>(object))...);
|
||||
return std::tuple_cat(Struct<Ts>::collect_members(static_cast<Ts&>(object))...);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline static auto member_names = []<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||
constexpr auto members = collect_members(impl::storage<Inheritance<Ts...>>.value);
|
||||
return std::array{impl::member_name<std::get<Is>(members)>()...};
|
||||
}(std::make_index_sequence<member_count>{});
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using member_types =
|
||||
tuple_to_list_t<decltype(Struct<T>::collcet_members(std::declval<T>())), std::remove_pointer_t>;
|
||||
template <typename TupleLike>
|
||||
requires requires { std::tuple_size<TupleLike>::value; }
|
||||
struct Struct<TupleLike> {
|
||||
constexpr inline static bool reflectable_struct = true;
|
||||
|
||||
constexpr inline static std::size_t member_count = std::tuple_size_v<TupleLike>;
|
||||
|
||||
template <typename Object>
|
||||
constexpr static auto collect_members(Object&& object) {
|
||||
return std::apply([](auto&&... args) { return std::tuple{&args...}; }, object);
|
||||
}
|
||||
|
||||
constexpr inline static auto member_names = []<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||
return std::array{to_string_literal<Is>()...};
|
||||
}(std::make_index_sequence<member_count>{});
|
||||
};
|
||||
|
||||
/// Turn the return value of the callable to bool.
|
||||
template <typename Callable>
|
||||
@@ -242,31 +341,23 @@ constexpr auto foldable(const Callable& callable) {
|
||||
};
|
||||
}
|
||||
|
||||
template <reflectable Object, typename Callback>
|
||||
template <reflectable_struct Object, typename Callback>
|
||||
constexpr bool foreach(Object&& object, const Callback& callback) {
|
||||
using S = Struct<std::remove_cvref_t<Object>>;
|
||||
constexpr auto count = S::member_count();
|
||||
auto members = S::collcet_members(object);
|
||||
constexpr auto static_members = S::collcet_members(S::instance());
|
||||
auto foldable = refl::foldable(callback);
|
||||
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||
return (foldable(callback)(impl::member_name<std::get<Is>(static_members)>(),
|
||||
*std::get<Is>(members)) &&
|
||||
...);
|
||||
}(std::make_index_sequence<count>{});
|
||||
return (foldable(refl::member_name<Object, Is>(), refl::member_value<Is>(object)) && ...);
|
||||
}(std::make_index_sequence<refl::member_count<Object>()>{});
|
||||
}
|
||||
|
||||
/// Invoke callback for each member of lhs and rhs, return false
|
||||
/// in callback to abort the iteration. Return true if all members are visited.
|
||||
template <reflectable LHS, reflectable RHS, typename Callback>
|
||||
template <reflectable_struct LHS, reflectable_struct RHS, typename Callback>
|
||||
constexpr bool foreach(LHS&& lhs, RHS&& rhs, const Callback& callback) {
|
||||
using L = Struct<std::remove_cvref_t<LHS>>;
|
||||
using R = Struct<std::remove_cvref_t<RHS>>;
|
||||
static_assert(L::member_count() == R::member_count(), "Member count mismatch");
|
||||
static_assert(member_count<LHS>() == member_count<RHS>(), "Member count mismatch");
|
||||
auto foldable = refl::foldable(callback);
|
||||
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||
return (foldable(callback)(*std::get<Is>(L::collcet_members(lhs)),
|
||||
*std::get<Is>(R::collcet_members(rhs))) &&
|
||||
...);
|
||||
}(std::make_index_sequence<L::member_count()>{});
|
||||
return (foldable(refl::member_value<Is>(lhs), refl::member_value<Is>(rhs)) && ...);
|
||||
}(std::make_index_sequence<refl::member_count<LHS>()>{});
|
||||
}
|
||||
|
||||
} // namespace clice::refl
|
||||
|
||||
61
include/Test/Annotation.h
Normal file
61
include/Test/Annotation.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "Basic/Location.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
class Annotation {
|
||||
public:
|
||||
Annotation(llvm::StringRef source) : m_source() {
|
||||
m_source.reserve(source.size());
|
||||
|
||||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
|
||||
for(uint32_t i = 0; i < source.size();) {
|
||||
auto c = source[i];
|
||||
|
||||
if(c == '@') {
|
||||
i += 1;
|
||||
auto key = source.substr(i).take_until([](char c) { return c == ' '; });
|
||||
assert(!locations.contains(key) && "duplicate key");
|
||||
locations.try_emplace(key, line, column);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c == '$') {
|
||||
assert(i + 1 < source.size() && source[i + 1] == '(' && "expect $(name)");
|
||||
i += 2;
|
||||
auto key = source.substr(i).take_until([](char c) { return c == ')'; });
|
||||
i += key.size() + 1;
|
||||
assert(!locations.contains(key) && "duplicate key");
|
||||
locations.try_emplace(key, line, column);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c == '\n') {
|
||||
line += 1;
|
||||
column = 0;
|
||||
} else {
|
||||
column += 1;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
m_source.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
llvm::StringRef source() const {
|
||||
return m_source;
|
||||
}
|
||||
|
||||
proto::Position position(llvm::StringRef key) const {
|
||||
return locations.lookup(key);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_source;
|
||||
llvm::StringMap<proto::Position> locations;
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
163
include/Test/CTest.h
Normal file
163
include/Test/CTest.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "Basic/Location.h"
|
||||
#include "Compiler/Compiler.h"
|
||||
#include "Support/Support.h"
|
||||
|
||||
namespace clice::test {
|
||||
|
||||
llvm::StringRef source_dir();
|
||||
|
||||
llvm::StringRef resource_dir();
|
||||
|
||||
} // namespace clice::test
|
||||
|
||||
namespace clice::testing {
|
||||
|
||||
#undef EXPECT_EQ
|
||||
#undef EXPECT_NE
|
||||
|
||||
inline void EXPECT_FAILURE(std::string msg,
|
||||
std::source_location current = std::source_location::current()) {
|
||||
::testing::internal::AssertHelper(::testing ::TestPartResult ::kNonFatalFailure,
|
||||
current.file_name(),
|
||||
current.line(),
|
||||
msg.c_str()) = ::testing ::Message();
|
||||
}
|
||||
|
||||
template <typename LHS, typename RHS>
|
||||
inline void EXPECT_EQ(const LHS& lhs,
|
||||
const RHS& rhs,
|
||||
std::source_location current = std::source_location::current()) {
|
||||
if(!refl::equal(lhs, rhs)) {
|
||||
std::string expect;
|
||||
if constexpr(requires { sizeof(json::Serde<LHS>); }) {
|
||||
llvm::raw_string_ostream(expect) << json::serialize(lhs);
|
||||
} else {
|
||||
expect = "cannot dump value";
|
||||
}
|
||||
|
||||
std::string actual;
|
||||
if constexpr(requires { sizeof(json::Serde<RHS>); }) {
|
||||
llvm::raw_string_ostream(actual) << json::serialize(rhs);
|
||||
} else {
|
||||
actual = "cannot dump value";
|
||||
}
|
||||
|
||||
EXPECT_FAILURE(std::format("expect: {}, actual: {}\n", expect, actual), current);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename LHS, typename RHS>
|
||||
inline void EXPECT_NE(const LHS& lhs,
|
||||
const RHS& rhs,
|
||||
std::source_location current = std::source_location::current()) {
|
||||
if(refl::equal(lhs, rhs)) {
|
||||
std::string expect;
|
||||
if constexpr(requires { sizeof(json::Serde<LHS>); }) {
|
||||
llvm::raw_string_ostream(expect) << json::serialize(lhs);
|
||||
} else {
|
||||
expect = "cannot dump value";
|
||||
}
|
||||
|
||||
std::string actual;
|
||||
if constexpr(requires { sizeof(json::Serde<RHS>); }) {
|
||||
llvm::raw_string_ostream(actual) << json::serialize(rhs);
|
||||
} else {
|
||||
actual = "cannot dump value";
|
||||
}
|
||||
|
||||
EXPECT_FAILURE(std::format("expect: {}, actual: {}\n", expect, actual), current);
|
||||
}
|
||||
}
|
||||
|
||||
class Tester {
|
||||
public:
|
||||
CompilationParams params;
|
||||
std::unique_ptr<llvm::vfs::InMemoryFileSystem> vfs;
|
||||
ASTInfo info;
|
||||
|
||||
/// Annoated locations.
|
||||
std::vector<std::string> sources;
|
||||
llvm::StringMap<proto::Position> locations;
|
||||
llvm::StringMap<std::uint32_t> offsets;
|
||||
|
||||
public:
|
||||
Tester(llvm::StringRef file, llvm::StringRef content) {
|
||||
params.srcPath = file;
|
||||
params.content = annoate(content);
|
||||
}
|
||||
|
||||
void addFile(llvm::StringRef name, llvm::StringRef content) {
|
||||
params.remappedFiles.emplace_back(name, content);
|
||||
}
|
||||
|
||||
llvm::StringRef annoate(llvm::StringRef content) {
|
||||
auto& source = sources.emplace_back();
|
||||
source.reserve(content.size());
|
||||
|
||||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
uint32_t offset = 0;
|
||||
|
||||
for(uint32_t i = 0; i < content.size();) {
|
||||
auto c = content[i];
|
||||
|
||||
if(c == '@') {
|
||||
i += 1;
|
||||
auto key = content.substr(i).take_until([](char c) { return c == ' '; });
|
||||
assert(!locations.contains(key) && "duplicate key");
|
||||
locations.try_emplace(key, line, column);
|
||||
offsets.try_emplace(key, offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c == '$') {
|
||||
assert(i + 1 < content.size() && content[i + 1] == '(' && "expect $(name)");
|
||||
i += 2;
|
||||
auto key = content.substr(i).take_until([](char c) { return c == ')'; });
|
||||
i += key.size() + 1;
|
||||
assert(!locations.contains(key) && "duplicate key");
|
||||
locations.try_emplace(key, line, column);
|
||||
offsets.try_emplace(key, offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c == '\n') {
|
||||
line += 1;
|
||||
column = 0;
|
||||
} else {
|
||||
column += 1;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
offset += 1;
|
||||
|
||||
source.push_back(c);
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
Tester& run(const char* standard = "-std=c++20") {
|
||||
params.vfs = std::move(vfs);
|
||||
params.command = std::format("clang++ {} {}", standard, params.srcPath);
|
||||
|
||||
auto info = compile(params);
|
||||
if(!info) {
|
||||
llvm::errs() << "Failed to build AST\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
this->info = std::move(*info);
|
||||
return *this;
|
||||
}
|
||||
|
||||
proto::Position pos(llvm::StringRef key) const {
|
||||
return locations.lookup(key);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clice::testing
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "Test/Test.h"
|
||||
#include "Test/CTest.h"
|
||||
#include "Index/SymbolIndex.h"
|
||||
#include "Basic/SourceConverter.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
struct IndexTester : Tester {
|
||||
std::vector<char> binary;
|
||||
@@ -37,4 +37,4 @@ struct IndexTester : Tester {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
@@ -1,263 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "Basic/Location.h"
|
||||
#include "Compiler/Compiler.h"
|
||||
#include "Support/Support.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace test {
|
||||
|
||||
llvm::StringRef source_dir();
|
||||
|
||||
llvm::StringRef resource_dir();
|
||||
|
||||
} // namespace test
|
||||
|
||||
class Annotation {
|
||||
public:
|
||||
Annotation(llvm::StringRef source) : m_source() {
|
||||
m_source.reserve(source.size());
|
||||
|
||||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
|
||||
for(uint32_t i = 0; i < source.size();) {
|
||||
auto c = source[i];
|
||||
|
||||
if(c == '@') {
|
||||
i += 1;
|
||||
auto key = source.substr(i).take_until([](char c) { return c == ' '; });
|
||||
assert(!locations.contains(key) && "duplicate key");
|
||||
locations.try_emplace(key, line, column);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c == '$') {
|
||||
assert(i + 1 < source.size() && source[i + 1] == '(' && "expect $(name)");
|
||||
i += 2;
|
||||
auto key = source.substr(i).take_until([](char c) { return c == ')'; });
|
||||
i += key.size() + 1;
|
||||
assert(!locations.contains(key) && "duplicate key");
|
||||
locations.try_emplace(key, line, column);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c == '\n') {
|
||||
line += 1;
|
||||
column = 0;
|
||||
} else {
|
||||
column += 1;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
m_source.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
llvm::StringRef source() const {
|
||||
return m_source;
|
||||
}
|
||||
|
||||
proto::Position position(llvm::StringRef key) const {
|
||||
return locations.lookup(key);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_source;
|
||||
llvm::StringMap<proto::Position> locations;
|
||||
};
|
||||
|
||||
template <typename Callback>
|
||||
inline void foreachFile(std::string name, const Callback& callback) {
|
||||
llvm::SmallString<128> path;
|
||||
path += test::source_dir();
|
||||
path::append(path, name);
|
||||
std::error_code error;
|
||||
fs::directory_iterator iter(path, error);
|
||||
fs::directory_iterator end;
|
||||
while(!error && iter != end) {
|
||||
auto file = iter->path();
|
||||
auto buffer = llvm::MemoryBuffer::getFile(file);
|
||||
if(!buffer) {
|
||||
llvm::outs() << "failed to open file: " << buffer.getError().message() << file << "\n";
|
||||
// TODO:
|
||||
}
|
||||
auto content = buffer.get()->getBuffer();
|
||||
callback(file, content);
|
||||
iter.increment(error);
|
||||
}
|
||||
}
|
||||
|
||||
#undef EXPECT_EQ
|
||||
#undef EXPECT_NE
|
||||
|
||||
inline void EXPECT_FAILURE(std::string msg,
|
||||
std::source_location current = std::source_location::current()) {
|
||||
::testing::internal::AssertHelper(::testing ::TestPartResult ::kNonFatalFailure,
|
||||
current.file_name(),
|
||||
current.line(),
|
||||
msg.c_str()) = ::testing ::Message();
|
||||
}
|
||||
|
||||
template <typename LHS, typename RHS>
|
||||
inline void EXPECT_EQ(const LHS& lhs,
|
||||
const RHS& rhs,
|
||||
std::source_location current = std::source_location::current()) {
|
||||
if(!refl::equal(lhs, rhs)) {
|
||||
std::string expect;
|
||||
if constexpr(requires { sizeof(json::Serde<LHS>); }) {
|
||||
llvm::raw_string_ostream(expect) << json::serialize(lhs);
|
||||
} else {
|
||||
expect = "cannot dump value";
|
||||
}
|
||||
|
||||
std::string actual;
|
||||
if constexpr(requires { sizeof(json::Serde<RHS>); }) {
|
||||
llvm::raw_string_ostream(actual) << json::serialize(rhs);
|
||||
} else {
|
||||
actual = "cannot dump value";
|
||||
}
|
||||
|
||||
EXPECT_FAILURE(std::format("expect: {}, actual: {}\n", expect, actual), current);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename LHS, typename RHS>
|
||||
inline void EXPECT_NE(const LHS& lhs,
|
||||
const RHS& rhs,
|
||||
std::source_location current = std::source_location::current()) {
|
||||
if(refl::equal(lhs, rhs)) {
|
||||
std::string expect;
|
||||
if constexpr(requires { sizeof(json::Serde<LHS>); }) {
|
||||
llvm::raw_string_ostream(expect) << json::serialize(lhs);
|
||||
} else {
|
||||
expect = "cannot dump value";
|
||||
}
|
||||
|
||||
std::string actual;
|
||||
if constexpr(requires { sizeof(json::Serde<RHS>); }) {
|
||||
llvm::raw_string_ostream(actual) << json::serialize(rhs);
|
||||
} else {
|
||||
actual = "cannot dump value";
|
||||
}
|
||||
|
||||
EXPECT_FAILURE(std::format("expect: {}, actual: {}\n", expect, actual), current);
|
||||
}
|
||||
}
|
||||
|
||||
class Tester {
|
||||
public:
|
||||
CompilationParams params;
|
||||
std::unique_ptr<llvm::vfs::InMemoryFileSystem> vfs;
|
||||
ASTInfo info;
|
||||
|
||||
/// Annoated locations.
|
||||
std::vector<std::string> sources;
|
||||
llvm::StringMap<proto::Position> locations;
|
||||
llvm::StringMap<std::uint32_t> offsets;
|
||||
|
||||
public:
|
||||
Tester(llvm::StringRef file, llvm::StringRef content) {
|
||||
params.srcPath = file;
|
||||
params.content = annoate(content);
|
||||
}
|
||||
|
||||
void addFile(llvm::StringRef name, llvm::StringRef content) {
|
||||
params.remappedFiles.emplace_back(name, content);
|
||||
}
|
||||
|
||||
llvm::StringRef annoate(llvm::StringRef content) {
|
||||
auto& source = sources.emplace_back();
|
||||
source.reserve(content.size());
|
||||
|
||||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
uint32_t offset = 0;
|
||||
|
||||
for(uint32_t i = 0; i < content.size();) {
|
||||
auto c = content[i];
|
||||
|
||||
if(c == '@') {
|
||||
i += 1;
|
||||
auto key = content.substr(i).take_until([](char c) { return c == ' '; });
|
||||
assert(!locations.contains(key) && "duplicate key");
|
||||
locations.try_emplace(key, line, column);
|
||||
offsets.try_emplace(key, offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c == '$') {
|
||||
assert(i + 1 < content.size() && content[i + 1] == '(' && "expect $(name)");
|
||||
i += 2;
|
||||
auto key = content.substr(i).take_until([](char c) { return c == ')'; });
|
||||
i += key.size() + 1;
|
||||
assert(!locations.contains(key) && "duplicate key");
|
||||
locations.try_emplace(key, line, column);
|
||||
offsets.try_emplace(key, offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c == '\n') {
|
||||
line += 1;
|
||||
column = 0;
|
||||
} else {
|
||||
column += 1;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
offset += 1;
|
||||
|
||||
source.push_back(c);
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
Tester& run(const char* standard = "-std=c++20") {
|
||||
params.vfs = std::move(vfs);
|
||||
params.command = std::format("clang++ {} {}", standard, params.srcPath);
|
||||
|
||||
auto info = compile(params);
|
||||
if(!info) {
|
||||
llvm::errs() << "Failed to build AST\n";
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
this->info = std::move(*info);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Tester& fail(const auto& lhs, const auto& rhs, std::source_location loc) {
|
||||
auto msg =
|
||||
std::format("left : {}\nright: {}\n", json::serialize(lhs), json::serialize(rhs));
|
||||
::testing::internal::AssertHelper(::testing ::TestPartResult ::kFatalFailure,
|
||||
loc.file_name(),
|
||||
loc.line(),
|
||||
msg.c_str()) = ::testing ::Message();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Tester& equal(const auto& lhs,
|
||||
const auto& rhs,
|
||||
std::source_location loc = std::source_location::current()) {
|
||||
if(!refl::equal(lhs, rhs)) {
|
||||
return fail(lhs, rhs, loc);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Tester& expect(llvm::StringRef name,
|
||||
clang::SourceLocation loc,
|
||||
std::source_location current = std::source_location::current()) {
|
||||
auto pos = locations.lookup(name);
|
||||
auto presumed = info.srcMgr().getPresumedLoc(loc);
|
||||
/// FIXME:
|
||||
equal(pos, proto::Position{presumed.getLine() - 1, presumed.getColumn() - 1}, current);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
|
||||
}
|
||||
@@ -33,7 +33,7 @@ llvm::StringRef resource_dir() {
|
||||
int main(int argc, char** argv) {
|
||||
using namespace clice;
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
llvm::cl::ParseCommandLineOptions(argc, argv, "clice test\n");
|
||||
|
||||
if(!cl::resource_dir.empty()) {
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace {
|
||||
/// Create a blank markup content as a place holder.
|
||||
proto::MarkupContent blank() {
|
||||
return {
|
||||
.kind = proto::MarkupKind::PlainText,
|
||||
.value = "",
|
||||
};
|
||||
}
|
||||
@@ -83,7 +82,8 @@ struct InlayHintCollector : clang::RecursiveASTVisitor<InlayHintCollector> {
|
||||
/// Collect hint for variable declared with `auto` keywords.
|
||||
/// The hint string wiil be placed at the right side of identifier, starting with ':' character.
|
||||
/// The `originDeclRange` will be used as the link of hint string.
|
||||
void collectAutoDeclHint(clang::QualType deduced, clang::SourceRange identRange,
|
||||
void collectAutoDeclHint(clang::QualType deduced,
|
||||
clang::SourceRange identRange,
|
||||
std::optional<clang::SourceRange> linkDeclRange) {
|
||||
|
||||
// For lambda expression, `getAsString` return a text like `(lambda at main.cpp:2:10)`
|
||||
@@ -404,7 +404,8 @@ struct InlayHintCollector : clang::RecursiveASTVisitor<InlayHintCollector> {
|
||||
return true;
|
||||
}
|
||||
|
||||
void collectReturnTypeHint(clang::SourceLocation hintLoc, clang::QualType retType,
|
||||
void collectReturnTypeHint(clang::SourceLocation hintLoc,
|
||||
clang::QualType retType,
|
||||
clang::SourceRange retTypeDeclRange) {
|
||||
proto::InlayHintLablePart lable{
|
||||
.value = shrinkText(std::format("-> {}", retType.getAsString(policy))),
|
||||
@@ -534,8 +535,10 @@ struct InlayHintCollector : clang::RecursiveASTVisitor<InlayHintCollector> {
|
||||
return remain.ltrim();
|
||||
}
|
||||
|
||||
void collectBlockEndHint(clang::SourceLocation location, std::string text,
|
||||
clang::SourceRange linkRange, bool checkDuplicatedHint) {
|
||||
void collectBlockEndHint(clang::SourceLocation location,
|
||||
std::string text,
|
||||
clang::SourceRange linkRange,
|
||||
bool checkDuplicatedHint) {
|
||||
// Already has a comment in that line.
|
||||
if(auto remain = remainTextOfThatLine(location);
|
||||
remain.starts_with("/*") || remain.starts_with("//"))
|
||||
@@ -663,7 +666,8 @@ json::Value inlayHintCapability(json::Value InlayHintClientCapabilities) {
|
||||
}
|
||||
|
||||
/// Compute inlay hints for a document in given range and config.
|
||||
proto::InlayHintsResult inlayHints(proto::InlayHintParams param, ASTInfo& info,
|
||||
proto::InlayHintsResult inlayHints(proto::InlayHintParams param,
|
||||
ASTInfo& info,
|
||||
const SourceConverter& converter,
|
||||
const config::InlayHintOption& config) {
|
||||
const clang::SourceManager& src = info.srcMgr();
|
||||
|
||||
@@ -1,91 +1,90 @@
|
||||
#include "Test/CTest.h"
|
||||
#include "Basic/SourceConverter.h"
|
||||
#include "Test/Test.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(SourceConverter, Remeasure) {
|
||||
using SC = SourceConverter;
|
||||
using SC = SourceConverter;
|
||||
|
||||
SourceConverter utf8{proto::PositionEncodingKind::UTF8};
|
||||
SourceConverter utf8{proto::PositionEncodingKind::UTF8};
|
||||
|
||||
EXPECT_EQ(utf8.remeasure(""), 0);
|
||||
EXPECT_EQ(utf8.remeasure("ascii"), 5);
|
||||
EXPECT_EQ(utf8.remeasure(""), 0);
|
||||
EXPECT_EQ(utf8.remeasure("ascii"), 5);
|
||||
|
||||
SourceConverter utf16{proto::PositionEncodingKind::UTF16};
|
||||
SourceConverter utf16{proto::PositionEncodingKind::UTF16};
|
||||
|
||||
EXPECT_EQ(utf16.remeasure("↓"), 1);
|
||||
EXPECT_EQ(utf16.remeasure("¥"), 1);
|
||||
EXPECT_EQ(utf16.remeasure("↓"), 1);
|
||||
EXPECT_EQ(utf16.remeasure("¥"), 1);
|
||||
|
||||
SourceConverter utf32{proto::PositionEncodingKind::UTF32};
|
||||
EXPECT_EQ(utf8.remeasure("😂"), 4);
|
||||
EXPECT_EQ(utf16.remeasure("😂"), 2);
|
||||
EXPECT_EQ(utf32.remeasure("😂"), 1);
|
||||
SourceConverter utf32{proto::PositionEncodingKind::UTF32};
|
||||
EXPECT_EQ(utf8.remeasure("😂"), 4);
|
||||
EXPECT_EQ(utf16.remeasure("😂"), 2);
|
||||
EXPECT_EQ(utf32.remeasure("😂"), 1);
|
||||
}
|
||||
|
||||
TEST(SourceConverter, Position) {
|
||||
const char *main = "int a /*😂*/ = 1;$(eof)";
|
||||
const char* main = "int a /*😂*/ = 1;$(eof)";
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run("-std=c++11");
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run("-std=c++11");
|
||||
|
||||
auto &src = txs.info.srcMgr();
|
||||
auto &tks = txs.info.tokBuf();
|
||||
auto& src = txs.info.srcMgr();
|
||||
auto& tks = txs.info.tokBuf();
|
||||
|
||||
auto mainid = src.getMainFileID();
|
||||
auto tokens = tks.expandedTokens(
|
||||
{src.getLocForStartOfFile(mainid), src.getLocForEndOfFile(mainid)});
|
||||
auto mainid = src.getMainFileID();
|
||||
auto tokens =
|
||||
tks.expandedTokens({src.getLocForStartOfFile(mainid), src.getLocForEndOfFile(mainid)});
|
||||
|
||||
auto eof = tokens.back().endLocation();
|
||||
txs.expect("eof", eof);
|
||||
auto eof = tokens.back().endLocation();
|
||||
|
||||
{
|
||||
SourceConverter cvtr{proto::PositionEncodingKind::UTF8};
|
||||
auto pos = cvtr.toPosition(eof, src);
|
||||
EXPECT_EQ(pos.line, 0);
|
||||
EXPECT_EQ(pos.character, 19);
|
||||
}
|
||||
{
|
||||
SourceConverter cvtr{proto::PositionEncodingKind::UTF8};
|
||||
auto pos = cvtr.toPosition(eof, src);
|
||||
EXPECT_EQ(pos.line, 0);
|
||||
EXPECT_EQ(pos.character, 19);
|
||||
}
|
||||
|
||||
{
|
||||
SourceConverter cvtr{proto::PositionEncodingKind::UTF16};
|
||||
auto pos = cvtr.toPosition(eof, src);
|
||||
EXPECT_EQ(pos.line, 0);
|
||||
EXPECT_EQ(pos.character, 19);
|
||||
}
|
||||
{
|
||||
SourceConverter cvtr{proto::PositionEncodingKind::UTF16};
|
||||
auto pos = cvtr.toPosition(eof, src);
|
||||
EXPECT_EQ(pos.line, 0);
|
||||
EXPECT_EQ(pos.character, 19);
|
||||
}
|
||||
|
||||
{
|
||||
SourceConverter cvtr{proto::PositionEncodingKind::UTF32};
|
||||
auto pos = cvtr.toPosition(eof, src);
|
||||
EXPECT_EQ(pos.line, 0);
|
||||
EXPECT_EQ(pos.character, 19);
|
||||
}
|
||||
{
|
||||
SourceConverter cvtr{proto::PositionEncodingKind::UTF32};
|
||||
auto pos = cvtr.toPosition(eof, src);
|
||||
EXPECT_EQ(pos.line, 0);
|
||||
EXPECT_EQ(pos.character, 19);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SourceConverter, UriAndFsPath) {
|
||||
using SC = SourceConverter;
|
||||
using SC = SourceConverter;
|
||||
|
||||
// It must be a existed file.
|
||||
// It must be a existed file.
|
||||
#if defined(__unix__)
|
||||
llvm::StringRef paths[] = {"/dev/null"};
|
||||
llvm::StringRef uris[] = {"file:///dev/null"};
|
||||
llvm::StringRef paths[] = {"/dev/null"};
|
||||
llvm::StringRef uris[] = {"file:///dev/null"};
|
||||
#elif defined(_WIN32)
|
||||
llvm::StringRef paths[] = {"C:\\Windows\\System32\\notepad.exe"};
|
||||
llvm::StringRef uris[] = {"file:///C%3A/Windows/System32/notepad.exe"};
|
||||
llvm::StringRef paths[] = {"C:\\Windows\\System32\\notepad.exe"};
|
||||
llvm::StringRef uris[] = {"file:///C%3A/Windows/System32/notepad.exe"};
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
EXPECT_EQ(std::size(paths), std::size(uris));
|
||||
EXPECT_EQ(std::size(paths), std::size(uris));
|
||||
|
||||
for (int i = 0; i < std::size(paths); ++i) {
|
||||
llvm::outs() << ": " << SC::toURI(paths[i])<< "\n";
|
||||
llvm::outs() << ": " << SC::toPath(uris[i])<< "\n";
|
||||
EXPECT_EQ(SC::toURI(paths[i]), uris[i]);
|
||||
EXPECT_EQ(paths[i], SC::toPath(uris[i]));
|
||||
}
|
||||
for(int i = 0; i < std::size(paths); ++i) {
|
||||
llvm::outs() << ": " << SC::toURI(paths[i]) << "\n";
|
||||
llvm::outs() << ": " << SC::toPath(uris[i]) << "\n";
|
||||
EXPECT_EQ(SC::toURI(paths[i]), uris[i]);
|
||||
EXPECT_EQ(paths[i], SC::toPath(uris[i]));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "Test/Test.h"
|
||||
|
||||
#include "Compiler/Command.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "Test/Test.h"
|
||||
#include "Compiler/Compiler.h"
|
||||
#include <Support/FileSystem.h>
|
||||
#include "Support/FileSystem.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -179,5 +179,5 @@ export int foo = 1;
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
#include "Test/Test.h"
|
||||
#include <Compiler/Compiler.h>
|
||||
#include <Compiler/Diagnostic.h>
|
||||
#include "Compiler/Diagnostic.h"
|
||||
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace clice;
|
||||
|
||||
TEST(clice, Diagnostic) {
|
||||
// foreachFile("Diagnostic", [](std::string file, llvm::StringRef content) {
|
||||
// std::vector<const char*> compileArgs = {
|
||||
// "clang++",
|
||||
// "-std=c++20",
|
||||
// file.c_str(),
|
||||
// "-resource-dir",
|
||||
// "/home/ykiko/C++/clice2/build/lib/clang/20",
|
||||
// };
|
||||
// Compiler compiler(file, content, compileArgs, new DiagnosticCollector());
|
||||
// compiler.buildAST();
|
||||
// });
|
||||
}
|
||||
TEST(clice, Diagnostic) {}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice::testing
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "Test/Test.h"
|
||||
#include "Compiler/Compiler.h"
|
||||
#include "Test/CTest.h"
|
||||
#include "Basic/SourceConverter.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -27,13 +27,19 @@ TEST(Directive, Include) {
|
||||
auto& info = tester.info;
|
||||
auto& includes = info.directive(info.srcMgr().getMainFileID()).includes;
|
||||
|
||||
tester.equal(includes.size(), 3)
|
||||
.expect("0", includes[0].loc)
|
||||
.equal("test.h", includes[0].path)
|
||||
.expect("1", includes[1].loc)
|
||||
.equal("test2.h", includes[1].path)
|
||||
.expect("2", includes[2].loc)
|
||||
.equal("test3.h", includes[2].path);
|
||||
auto EXPECT_INCLUDE = [&](std::size_t index,
|
||||
llvm::StringRef position,
|
||||
llvm::StringRef path,
|
||||
std::source_location current = std::source_location::current()) {
|
||||
auto& include = includes[index];
|
||||
EXPECT_EQ(SourceConverter().toPosition(include.loc, info.srcMgr()), tester.pos(position));
|
||||
EXPECT_EQ(include.path, path);
|
||||
};
|
||||
|
||||
EXPECT_EQ(includes.size(), 3);
|
||||
EXPECT_INCLUDE(0, "0", "test.h");
|
||||
EXPECT_INCLUDE(1, "1", "test2.h");
|
||||
EXPECT_INCLUDE(2, "2", "test3.h");
|
||||
}
|
||||
|
||||
TEST(Directive, Condition) {
|
||||
@@ -60,23 +66,26 @@ TEST(Directive, Condition) {
|
||||
auto& info = tester.info;
|
||||
auto& conditions = info.directive(info.srcMgr().getMainFileID()).conditions;
|
||||
|
||||
tester.equal(conditions.size(), 8)
|
||||
.equal(conditions[0].kind, Condition::BranchKind::If)
|
||||
.expect("0", conditions[0].loc)
|
||||
.equal(conditions[1].kind, Condition::BranchKind::Elif)
|
||||
.expect("1", conditions[1].loc)
|
||||
.equal(conditions[2].kind, Condition::BranchKind::Else)
|
||||
.expect("2", conditions[2].loc)
|
||||
.equal(conditions[3].kind, Condition::BranchKind::EndIf)
|
||||
.expect("3", conditions[3].loc)
|
||||
.equal(conditions[4].kind, Condition::BranchKind::Ifdef)
|
||||
.expect("4", conditions[4].loc)
|
||||
.equal(conditions[5].kind, Condition::BranchKind::Elifdef)
|
||||
.expect("5", conditions[5].loc)
|
||||
.equal(conditions[6].kind, Condition::BranchKind::Else)
|
||||
.expect("6", conditions[6].loc)
|
||||
.equal(conditions[7].kind, Condition::BranchKind::EndIf)
|
||||
.expect("7", conditions[7].loc);
|
||||
auto EPXECT_CON = [&](std::size_t index,
|
||||
Condition::BranchKind kind,
|
||||
llvm::StringRef position,
|
||||
std::source_location current = std::source_location::current()) {
|
||||
auto& condition = conditions[index];
|
||||
EXPECT_EQ(condition.kind, kind, current);
|
||||
EXPECT_EQ(SourceConverter().toPosition(condition.loc, info.srcMgr()),
|
||||
tester.pos(position),
|
||||
current);
|
||||
};
|
||||
|
||||
EXPECT_EQ(conditions.size(), 8);
|
||||
EPXECT_CON(0, Condition::BranchKind::If, "0");
|
||||
EPXECT_CON(1, Condition::BranchKind::Elif, "1");
|
||||
EPXECT_CON(2, Condition::BranchKind::Else, "2");
|
||||
EPXECT_CON(3, Condition::BranchKind::EndIf, "3");
|
||||
EPXECT_CON(4, Condition::BranchKind::Ifdef, "4");
|
||||
EPXECT_CON(5, Condition::BranchKind::Elifdef, "5");
|
||||
EPXECT_CON(6, Condition::BranchKind::Else, "6");
|
||||
EPXECT_CON(7, Condition::BranchKind::EndIf, "7");
|
||||
}
|
||||
|
||||
TEST(Directive, Macro) {
|
||||
@@ -104,27 +113,29 @@ int y = $(6)expr($(7)expr(1));
|
||||
auto& info = tester.info;
|
||||
auto& macros = info.directive(info.srcMgr().getMainFileID()).macros;
|
||||
|
||||
tester.equal(macros.size(), 9)
|
||||
.equal(macros[0].kind, MacroRef::Kind::Def)
|
||||
.expect("0", macros[0].loc)
|
||||
.equal(macros[1].kind, MacroRef::Kind::Ref)
|
||||
.expect("1", macros[1].loc)
|
||||
.equal(macros[2].kind, MacroRef::Kind::Ref)
|
||||
.expect("2", macros[2].loc)
|
||||
.equal(macros[3].kind, MacroRef::Kind::Undef)
|
||||
.expect("3", macros[3].loc)
|
||||
.equal(macros[4].kind, MacroRef::Kind::Def)
|
||||
.expect("4", macros[4].loc)
|
||||
.equal(macros[5].kind, MacroRef::Kind::Ref)
|
||||
.expect("5", macros[5].loc)
|
||||
.equal(macros[6].kind, MacroRef::Kind::Ref)
|
||||
.expect("6", macros[6].loc)
|
||||
.equal(macros[7].kind, MacroRef::Kind::Ref)
|
||||
.expect("7", macros[7].loc)
|
||||
.equal(macros[8].kind, MacroRef::Kind::Undef)
|
||||
.expect("8", macros[8].loc);
|
||||
auto EXPECT_MACRO = [&](std::size_t index,
|
||||
MacroRef::Kind kind,
|
||||
llvm::StringRef position,
|
||||
std::source_location current = std::source_location::current()) {
|
||||
auto& macro = macros[index];
|
||||
EXPECT_EQ(macro.kind, kind, current);
|
||||
EXPECT_EQ(SourceConverter().toPosition(macro.loc, info.srcMgr()),
|
||||
tester.pos(position),
|
||||
current);
|
||||
};
|
||||
|
||||
EXPECT_EQ(macros.size(), 9);
|
||||
EXPECT_MACRO(0, MacroRef::Kind::Def, "0");
|
||||
EXPECT_MACRO(1, MacroRef::Kind::Ref, "1");
|
||||
EXPECT_MACRO(2, MacroRef::Kind::Ref, "2");
|
||||
EXPECT_MACRO(3, MacroRef::Kind::Undef, "3");
|
||||
EXPECT_MACRO(4, MacroRef::Kind::Def, "4");
|
||||
EXPECT_MACRO(5, MacroRef::Kind::Ref, "5");
|
||||
EXPECT_MACRO(6, MacroRef::Kind::Ref, "6");
|
||||
EXPECT_MACRO(7, MacroRef::Kind::Ref, "7");
|
||||
EXPECT_MACRO(8, MacroRef::Kind::Undef, "8");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "Test/Test.h"
|
||||
#include "Compiler/Compiler.h"
|
||||
|
||||
#include "llvm/Support/ToolOutputFile.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "Test/Test.h"
|
||||
#include "Compiler/Preamble.h"
|
||||
|
||||
namespace {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {}
|
||||
|
||||
} // namespace clice::testing
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "Test/Test.h"
|
||||
#include <Compiler/Compiler.h>
|
||||
#include <Compiler/Resolver.h>
|
||||
#include "Test/CTest.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -47,10 +45,10 @@ void run(llvm::StringRef code, std::source_location current = std::source_locati
|
||||
auto input = tester.info.resolver().resolve(run.input);
|
||||
auto expect = run.expect;
|
||||
|
||||
EXPECT_EQ(input.isNull(), false, current);
|
||||
EXPECT_EQ(expect.isNull(), false, current);
|
||||
EXPECT_EQ(input.isNull(), false);
|
||||
EXPECT_EQ(expect.isNull(), false);
|
||||
|
||||
EXPECT_EQ(input.getCanonicalType(), expect.getCanonicalType(), current);
|
||||
EXPECT_EQ(input.getCanonicalType(), expect.getCanonicalType());
|
||||
}
|
||||
|
||||
TEST(TemplateResolver, TypeParameterType) {
|
||||
@@ -470,5 +468,5 @@ struct test {
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#include "Test/Test.h"
|
||||
#include "Compiler/Semantic.h"
|
||||
|
||||
#include "Test/Test.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {} // namespace
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "Test/Test.h"
|
||||
#include "Test/CTest.h"
|
||||
#include "Feature/CodeCompletion.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -32,5 +32,5 @@ int main() {
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
|
||||
@@ -1,32 +1,11 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <Feature/DocumentSymbol.h>
|
||||
#include <Basic/SourceConverter.h>
|
||||
#include "Test/CTest.h"
|
||||
#include "Basic/SourceConverter.h"
|
||||
#include "Feature/DocumentSymbol.h"
|
||||
|
||||
#include "Test/Test.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
void dbg(const proto::DocumentSymbolResult& result, size_t ident = 0) {
|
||||
for(auto& item: result) {
|
||||
for(size_t i = 0; i < ident; ++i)
|
||||
llvm::outs() << ' ';
|
||||
llvm::outs()
|
||||
<< std::format(
|
||||
"kind: {}, name:{}, detail:{}, deprecated:{}, range: {}, children_num:{}",
|
||||
item.kind.name(),
|
||||
item.name,
|
||||
item.detail,
|
||||
item.deprecated,
|
||||
json::serialize(item.range),
|
||||
item.children.size())
|
||||
<< '\n';
|
||||
|
||||
dbg(item.children, ident + 2);
|
||||
}
|
||||
}
|
||||
|
||||
void total_size(const proto::DocumentSymbolResult& result, size_t& size) {
|
||||
for(auto& item: result) {
|
||||
++size;
|
||||
@@ -40,7 +19,7 @@ size_t total_size(const proto::DocumentSymbolResult& result) {
|
||||
return size;
|
||||
}
|
||||
|
||||
const SourceConverter Converter{proto::PositionEncodingKind::UTF8};
|
||||
const SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
|
||||
TEST(DocumentSymbol, Namespace) {
|
||||
const char* main = R"cpp(
|
||||
@@ -67,7 +46,7 @@ namespace _1::_2{
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto res = feature::documentSymbol(txs.info, Converter);
|
||||
auto res = feature::documentSymbol(txs.info, converter);
|
||||
// dbg(res);
|
||||
ASSERT_EQ(total_size(res), 8);
|
||||
}
|
||||
@@ -96,7 +75,7 @@ int main(int argc, char* argv[]) {
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto res = feature::documentSymbol(txs.info, Converter);
|
||||
auto res = feature::documentSymbol(txs.info, converter);
|
||||
// dbg(res);
|
||||
ASSERT_EQ(total_size(res), 9);
|
||||
}
|
||||
@@ -121,7 +100,7 @@ struct x {
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto res = feature::documentSymbol(txs.info, Converter);
|
||||
auto res = feature::documentSymbol(txs.info, converter);
|
||||
// dbg(res);
|
||||
ASSERT_EQ(total_size(res), 7);
|
||||
}
|
||||
@@ -141,7 +120,7 @@ struct S {
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto res = feature::documentSymbol(txs.info, Converter);
|
||||
auto res = feature::documentSymbol(txs.info, converter);
|
||||
// dbg(res);
|
||||
ASSERT_EQ(total_size(res), 6);
|
||||
}
|
||||
@@ -163,7 +142,7 @@ struct _0 {
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto res = feature::documentSymbol(txs.info, Converter);
|
||||
auto res = feature::documentSymbol(txs.info, converter);
|
||||
// dbg(res);
|
||||
ASSERT_EQ(total_size(res), 7);
|
||||
}
|
||||
@@ -188,7 +167,7 @@ enum B {
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto res = feature::documentSymbol(txs.info, Converter);
|
||||
auto res = feature::documentSymbol(txs.info, converter);
|
||||
// dbg(res);
|
||||
ASSERT_EQ(total_size(res), 8);
|
||||
}
|
||||
@@ -202,7 +181,7 @@ int y = 2;
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto res = feature::documentSymbol(txs.info, Converter);
|
||||
auto res = feature::documentSymbol(txs.info, converter);
|
||||
// dbg(res);
|
||||
ASSERT_EQ(total_size(res), 2);
|
||||
}
|
||||
@@ -232,7 +211,7 @@ VAR(test)
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto res = feature::documentSymbol(txs.info, Converter);
|
||||
auto res = feature::documentSymbol(txs.info, converter);
|
||||
// dbg(res);
|
||||
|
||||
// clang-format off
|
||||
@@ -250,4 +229,4 @@ VAR(test)
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
@@ -1,36 +1,40 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <Feature/FoldingRange.h>
|
||||
#include "Test/CTest.h"
|
||||
#include "Feature/FoldingRange.h"
|
||||
|
||||
#include "Test/Test.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
void dbg(const proto::FoldingRangeResult& result) {
|
||||
for(auto& item: result) {
|
||||
llvm::outs()
|
||||
<< std::format("begin/end line: {}/{}, begin/end character: {}/{}, kind: {}, text: {}",
|
||||
item.startLine,
|
||||
item.endLine,
|
||||
item.startCharacter,
|
||||
item.endCharacter,
|
||||
json::serialize(item.kind),
|
||||
item.collapsedText)
|
||||
<< "\n";
|
||||
struct FoldingRange : public ::testing::Test {
|
||||
std::optional<Tester> tester;
|
||||
proto::FoldingRangeResult result;
|
||||
|
||||
void run(llvm::StringRef code) {
|
||||
tester.emplace("main.cpp", code);
|
||||
tester->run();
|
||||
auto& info = tester->info;
|
||||
|
||||
FoldingRangeParams param;
|
||||
SourceConverter converter;
|
||||
result = feature::foldingRange(param, info, converter);
|
||||
}
|
||||
}
|
||||
|
||||
// convert 0-0 based location in LSP to 1-1 based location in clang.
|
||||
auto fromLspLocation(const clang::SourceManager* src, proto::FoldingRange range)
|
||||
-> std::pair<clang::SourceLocation, clang::SourceLocation> {
|
||||
auto fileID = src->getMainFileID();
|
||||
return {src->translateLineCol(fileID, range.startLine + 1, range.startCharacter + 1),
|
||||
src->translateLineCol(fileID, range.endLine + 1, range.endCharacter + 1)};
|
||||
}
|
||||
void EXPECT_RANGE(std::size_t index,
|
||||
llvm::StringRef begin,
|
||||
llvm::StringRef end,
|
||||
std::source_location current = std::source_location::current()) {
|
||||
auto& folding = result[index];
|
||||
EXPECT_EQ(tester->pos(begin),
|
||||
proto::Position{folding.startLine, folding.startCharacter},
|
||||
current);
|
||||
EXPECT_EQ(tester->pos(end),
|
||||
proto::Position{folding.endLine, folding.endCharacter},
|
||||
current);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(FoldingRange, Namespace) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(FoldingRange, Namespace) {
|
||||
run(R"cpp(
|
||||
namespace single_line {$(1)
|
||||
//$(2)
|
||||
}
|
||||
@@ -49,39 +53,15 @@ namespace ugly
|
||||
//$(6)
|
||||
}
|
||||
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
txs.equal(res.size(), 3)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
.expect("5", toLoc(res[2]).first)
|
||||
.expect("6", toLoc(res[2]).second)
|
||||
//
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
EXPECT_RANGE(2, "5", "6");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, Enum) {
|
||||
auto main = R"cpp(
|
||||
TEST_F(FoldingRange, Enum) {
|
||||
run(R"cpp(
|
||||
enum _0 {$(1)
|
||||
A,
|
||||
B,
|
||||
@@ -96,35 +76,14 @@ enum class _2 {$(3)
|
||||
C$(4)
|
||||
};
|
||||
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 2)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, RecordDecl) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(FoldingRange, RecordDecl) {
|
||||
run(R"cpp(
|
||||
struct _2 {$(1)
|
||||
int x;
|
||||
float y;$(2)
|
||||
@@ -153,47 +112,18 @@ void f() {$(9)
|
||||
};$(10)
|
||||
}
|
||||
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 6)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
.expect("5", toLoc(res[2]).first)
|
||||
.expect("6", toLoc(res[2]).second)
|
||||
//
|
||||
.expect("7", toLoc(res[3]).first)
|
||||
.expect("8", toLoc(res[3]).second)
|
||||
//
|
||||
.expect("9", toLoc(res[4]).first)
|
||||
.expect("10", toLoc(res[4]).second)
|
||||
//
|
||||
.expect("11", toLoc(res[5]).first)
|
||||
.expect("12", toLoc(res[5]).second)
|
||||
//
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
EXPECT_RANGE(2, "5", "6");
|
||||
EXPECT_RANGE(3, "7", "8");
|
||||
EXPECT_RANGE(4, "9", "10");
|
||||
EXPECT_RANGE(5, "11", "12");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, CXXRecordDeclAndMemberMethod) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(FoldingRange, CXXRecordDeclAndMemberMethod) {
|
||||
run(R"cpp(
|
||||
struct _2 {$(1)
|
||||
int x;
|
||||
float y;
|
||||
@@ -215,44 +145,16 @@ $(4)
|
||||
};
|
||||
|
||||
struct _4;
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 4)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
.expect("5", toLoc(res[2]).first)
|
||||
.expect("6", toLoc(res[2]).second)
|
||||
//
|
||||
.expect("7", toLoc(res[3]).first)
|
||||
.expect("8", toLoc(res[3]).second)
|
||||
//
|
||||
// .expect("9", toLoc(res[4].startLine, res[4].startCharacter))
|
||||
// .expect("10", toLoc(res[4].endLine, res[4].endCharacter))
|
||||
//
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
EXPECT_RANGE(2, "5", "6");
|
||||
EXPECT_RANGE(3, "7", "8");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, LambdaCapture) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(FoldingRange, LambdaCapture) {
|
||||
run(R"cpp(
|
||||
auto z = [$(1)
|
||||
x = 0, y = 1$(2)
|
||||
]() {$(3)
|
||||
@@ -264,45 +166,17 @@ auto s = [$(5)
|
||||
y = 1$(6)
|
||||
](){ return; };
|
||||
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
EXPECT_EQ(result.size(), 3);
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 3)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
.expect("5", toLoc(res[2]).first)
|
||||
.expect("6", toLoc(res[2]).second)
|
||||
// //
|
||||
// .expect("7", toLoc(res[3]).first)
|
||||
// .expect("8", toLoc(res[3]).second)
|
||||
// //
|
||||
// .expect("9", toLoc(res[4].startLine, res[4].startCharacter))
|
||||
// .expect("10", toLoc(res[4].endLine, res[4].endCharacter))
|
||||
// //
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
EXPECT_RANGE(2, "5", "6");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, LambdaExpression) {
|
||||
const char* main = R"cpp(
|
||||
|
||||
TEST_F(FoldingRange, LambdaExpression) {
|
||||
run(R"cpp(
|
||||
auto _0 = [](int _) {};
|
||||
|
||||
auto _1 = [](int _) {$(1)
|
||||
@@ -319,44 +193,15 @@ auto _3 = []($(5)
|
||||
int _2$(6)
|
||||
) {};
|
||||
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 3)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
.expect("5", toLoc(res[2]).first)
|
||||
.expect("6", toLoc(res[2]).second)
|
||||
//
|
||||
// .expect("7", toLoc(res[3]).first)
|
||||
// .expect("8", toLoc(res[3]).second)
|
||||
// //
|
||||
// .expect("9", toLoc(res[4].startLine, res[4].startCharacter))
|
||||
// .expect("10", toLoc(res[4].endLine, res[4].endCharacter))
|
||||
// //
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
EXPECT_RANGE(2, "5", "6");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, FnParas) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(FoldingRange, FunctionParams) {
|
||||
run(R"cpp(
|
||||
void e() {}
|
||||
|
||||
void f($(1)
|
||||
@@ -375,39 +220,15 @@ void d($(5)
|
||||
int _2,
|
||||
...$(6)
|
||||
);
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 3)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
.expect("5", toLoc(res[2]).first)
|
||||
.expect("6", toLoc(res[2]).second)
|
||||
//
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
EXPECT_RANGE(2, "5", "6");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, FnBody) {
|
||||
const char* main = R"cpp(
|
||||
|
||||
TEST_F(FoldingRange, FunctionBody) {
|
||||
run(R"cpp(
|
||||
void f() {$(1)
|
||||
//
|
||||
//$(2)
|
||||
@@ -425,42 +246,16 @@ void n() {$(5)
|
||||
}
|
||||
//$(6)
|
||||
}
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 4)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
.expect("5", toLoc(res[2]).first)
|
||||
.expect("6", toLoc(res[2]).second)
|
||||
//
|
||||
.expect("7", toLoc(res[3]).first)
|
||||
.expect("8", toLoc(res[3]).second)
|
||||
//
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
EXPECT_RANGE(2, "5", "6");
|
||||
EXPECT_RANGE(3, "7", "8");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, FnCall) {
|
||||
const char* main = R"cpp(
|
||||
|
||||
TEST_F(FoldingRange, FunctionCall) {
|
||||
run(R"cpp(
|
||||
int f(int _1, int _2, int _3, int _4, int _5, int _6) { return _1 + _2; }
|
||||
|
||||
int main() {$(1)
|
||||
@@ -472,42 +267,14 @@ int main() {$(1)
|
||||
4, 5, 6$(4)
|
||||
);$(2)
|
||||
}
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 2)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
// .expect("5", toLoc(res[2]).first)
|
||||
// .expect("6", toLoc(res[2]).second)
|
||||
// //
|
||||
// .expect("7", toLoc(res[3]).first)
|
||||
// .expect("8", toLoc(res[3]).second)
|
||||
// //
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, CompoundStmt) {
|
||||
const char* main = R"cpp(
|
||||
|
||||
TEST_F(FoldingRange, CompoundStmt) {
|
||||
run(R"cpp(
|
||||
int main () {$(1)
|
||||
|
||||
{$(3)
|
||||
@@ -525,45 +292,15 @@ int main () {$(1)
|
||||
return 0;$(2)
|
||||
}
|
||||
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 4)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
.expect("5", toLoc(res[2]).first)
|
||||
.expect("6", toLoc(res[2]).second)
|
||||
// //
|
||||
// .expect("7", toLoc(res[3]).first)
|
||||
// .expect("8", toLoc(res[3]).second)
|
||||
// //
|
||||
// .expect("9", toLoc(res[4].startLine, res[4].startCharacter))
|
||||
// .expect("10", toLoc(res[4].endLine, res[4].endCharacter))
|
||||
// //
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
EXPECT_RANGE(2, "5", "6");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, InitializeList) {
|
||||
const char* main = R"cpp(
|
||||
|
||||
TEST_F(FoldingRange, InitializeList) {
|
||||
run(R"cpp(
|
||||
struct L { int xs[4]; };
|
||||
|
||||
L l1 = {$(1)
|
||||
@@ -575,38 +312,14 @@ L l2 = {$(3)
|
||||
//$(4)
|
||||
};
|
||||
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 2)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
// .expect("5", toLoc(res[2]).first)
|
||||
// .expect("6", toLoc(res[2]).second)
|
||||
//
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, AccessControlBlock) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(FoldingRange, AccessControlBlock) {
|
||||
run(R"cpp(
|
||||
struct empty { int x; };
|
||||
|
||||
class _0 {$(1)
|
||||
@@ -629,46 +342,18 @@ public:
|
||||
private:
|
||||
public:$(12)
|
||||
};
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 6)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[1]).first)
|
||||
.expect("4", toLoc(res[1]).second)
|
||||
//
|
||||
.expect("5", toLoc(res[2]).first)
|
||||
.expect("6", toLoc(res[2]).second)
|
||||
//
|
||||
.expect("7", toLoc(res[3]).first)
|
||||
.expect("8", toLoc(res[3]).second)
|
||||
//
|
||||
.expect("9", toLoc(res[4]).first)
|
||||
.expect("10", toLoc(res[4]).second)
|
||||
//
|
||||
.expect("11", toLoc(res[5]).first)
|
||||
.expect("12", toLoc(res[5]).second);
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "3", "4");
|
||||
EXPECT_RANGE(2, "5", "6");
|
||||
EXPECT_RANGE(3, "7", "8");
|
||||
EXPECT_RANGE(4, "9", "10");
|
||||
EXPECT_RANGE(5, "11", "12");
|
||||
}
|
||||
|
||||
TEST(FoldingRange, Macro) {
|
||||
const char* main = R"cpp(
|
||||
|
||||
TEST_F(FoldingRange, Macro) {
|
||||
run(R"cpp(
|
||||
#$(1)ifdef M1
|
||||
$(2)
|
||||
#$(3)else
|
||||
@@ -680,46 +365,13 @@ $(2)
|
||||
|
||||
//$(4)
|
||||
#endif
|
||||
)cpp");
|
||||
|
||||
)cpp";
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto toLoc = [src = &info.srcMgr()](const proto::FoldingRange& fr) {
|
||||
return fromLspLocation(src, fr);
|
||||
};
|
||||
|
||||
SourceConverter converter{proto::PositionEncodingKind::UTF8};
|
||||
FoldingRangeParams param;
|
||||
auto res = feature::foldingRange(param, info, converter);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 3)
|
||||
//
|
||||
.expect("1", toLoc(res[0]).first)
|
||||
.expect("2", toLoc(res[0]).second)
|
||||
//
|
||||
.expect("5", toLoc(res[1]).first)
|
||||
.expect("6", toLoc(res[1]).second)
|
||||
//
|
||||
.expect("3", toLoc(res[2]).first)
|
||||
.expect("4", toLoc(res[2]).second)
|
||||
// //
|
||||
// .expect("7", toLoc(res[3]).first)
|
||||
// .expect("8", toLoc(res[3]).second)
|
||||
// //
|
||||
// .expect("9", toLoc(res[4]).first)
|
||||
// .expect("10", toLoc(res[4]).second)
|
||||
// //
|
||||
// .expect("11", toLoc(res[5]).first)
|
||||
// .expect("12", toLoc(res[5]).second)
|
||||
|
||||
;
|
||||
EXPECT_RANGE(0, "1", "2");
|
||||
EXPECT_RANGE(1, "5", "6");
|
||||
EXPECT_RANGE(2, "3", "4");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
@@ -1,30 +1,11 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <Feature/InlayHint.h>
|
||||
#include <Basic/SourceConverter.h>
|
||||
#include "Test/CTest.h"
|
||||
#include "Feature/InlayHint.h"
|
||||
#include "Basic/SourceConverter.h"
|
||||
|
||||
#include "Test/Test.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
void dbg(const std::vector<proto::InlayHint>& hints) {
|
||||
for(auto& hint: hints) {
|
||||
llvm::outs() << std::format("kind:{}, position:{}, value_size:{},",
|
||||
hint.kind.name(),
|
||||
json::serialize(hint.position),
|
||||
hint.lable.size());
|
||||
for(auto& lable: hint.lable) {
|
||||
llvm::outs() << std::format(" value:{}, link position:{}",
|
||||
lable.value,
|
||||
json::serialize(lable.Location))
|
||||
<< '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SourceConverter Converter{proto::PositionEncodingKind::UTF8};
|
||||
|
||||
const config::InlayHintOption LikeClangd{
|
||||
.maxLength = 20,
|
||||
.maxArrayElements = 10,
|
||||
@@ -34,34 +15,45 @@ const config::InlayHintOption LikeClangd{
|
||||
.chainCall = false,
|
||||
};
|
||||
|
||||
TEST(InlayHint, RequestRange) {
|
||||
const char* main = R"cpp(
|
||||
struct InlayHint : public ::testing::Test {
|
||||
protected:
|
||||
void run(llvm::StringRef code,
|
||||
proto::Range range = {},
|
||||
const config::InlayHintOption& option = LikeClangd) {
|
||||
tester.emplace("main.cpp", code);
|
||||
tester->run();
|
||||
auto& info = tester->info;
|
||||
SourceConverter converter;
|
||||
result = feature::inlayHints({.range = range}, info, converter, option);
|
||||
}
|
||||
|
||||
std::optional<Tester> tester;
|
||||
proto::InlayHintsResult result;
|
||||
};
|
||||
|
||||
TEST_F(InlayHint, RequestRange) {
|
||||
run(R"cpp(
|
||||
auto x1 = 1;$(request_range_start)
|
||||
auto x2 = 1;
|
||||
auto x3 = 1;
|
||||
auto x4 = 1;$(request_range_end)
|
||||
)cpp";
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
auto& info = txs.info;
|
||||
|
||||
proto::Range request{
|
||||
.start = {1, 12}, // $(request_range_start)
|
||||
.end = {4, 12}, // $(request_range_end)
|
||||
};
|
||||
auto res = feature::inlayHints({.range = request}, info, Converter, {.implicitCast = true});
|
||||
|
||||
// dbg(res);
|
||||
)cpp",
|
||||
{
|
||||
// $(request_range_start)
|
||||
.start = {1, 12},
|
||||
// $(request_range_end)
|
||||
.end = {4, 12},
|
||||
},
|
||||
{
|
||||
.implicitCast = true,
|
||||
});
|
||||
|
||||
// 3: x2, x3, x4 is included in the request range.
|
||||
txs.equal(res.size(), 3)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 3);
|
||||
}
|
||||
|
||||
TEST(InlayHint, AutoDecl) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, AutoDecl) {
|
||||
run(R"cpp(
|
||||
auto$(1) x = 1;
|
||||
|
||||
void f() {
|
||||
@@ -76,23 +68,13 @@ template<typename T>
|
||||
void t() {
|
||||
auto z = T{};
|
||||
}
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, LikeClangd);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 4)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 4);
|
||||
}
|
||||
|
||||
TEST(InlayHint, FreeFnArguments) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, FreeFunctionArguments) {
|
||||
run(R"cpp(
|
||||
void f(int a, int b) {}
|
||||
void g(int a = 1) {}
|
||||
void h() {
|
||||
@@ -100,45 +82,25 @@ f($(1)1, $(2)2);
|
||||
g();
|
||||
}
|
||||
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, LikeClangd);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 2)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 2);
|
||||
}
|
||||
|
||||
TEST(InlayHint, FnArgPassedAsLValueRef) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, FnArgPassedAsLValueRef) {
|
||||
run(R"cpp(
|
||||
void f(int& a, int& b) { }
|
||||
void g() {
|
||||
int x = 1;
|
||||
f($(1)x, $(2)x);
|
||||
}
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, LikeClangd);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 2)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 2);
|
||||
}
|
||||
|
||||
TEST(InlayHint, MethodArguments) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, MethodArguments) {
|
||||
run(R"cpp(
|
||||
struct A {
|
||||
void f(int a, int b, int d) {}
|
||||
};
|
||||
@@ -147,23 +109,13 @@ void f() {
|
||||
A a;
|
||||
a.f($(1)1, $(2)2, $(3)3);
|
||||
}
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, LikeClangd);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 3)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 3);
|
||||
}
|
||||
|
||||
TEST(InlayHint, OperatorCall) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, OperatorCall) {
|
||||
run(R"cpp(
|
||||
struct A {
|
||||
int operator()(int a, int b) { return a + b; }
|
||||
};
|
||||
@@ -172,23 +124,13 @@ int f() {
|
||||
A a;
|
||||
return a(1, 2);
|
||||
}
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, LikeClangd);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 2)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 2);
|
||||
}
|
||||
|
||||
TEST(InlayHint, ReturnTypeHint) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, ReturnTypeHint) {
|
||||
run(R"cpp(
|
||||
auto f()$(1) {
|
||||
return 1;
|
||||
}
|
||||
@@ -203,45 +145,25 @@ void g() {
|
||||
}();
|
||||
}
|
||||
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, LikeClangd);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 3)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 3);
|
||||
}
|
||||
|
||||
TEST(InlayHint, StructureBinding) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, StructureBinding) {
|
||||
run(R"cpp(
|
||||
int f() {
|
||||
int a[2];
|
||||
auto [x$(1), y$(2)] = a;
|
||||
return x + y; // use x and y to avoid warning.
|
||||
}
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, LikeClangd);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 2)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 2);
|
||||
}
|
||||
|
||||
TEST(InlayHint, Constructor) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, Constructor) {
|
||||
run(R"cpp(
|
||||
struct A {
|
||||
int x;
|
||||
int y;
|
||||
@@ -255,61 +177,31 @@ void f() {
|
||||
A c$(3) = {1, 2};
|
||||
}
|
||||
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, LikeClangd);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 6)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 6);
|
||||
}
|
||||
|
||||
TEST(InlayHint, InitializeList) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, InitializeList) {
|
||||
run(R"cpp(
|
||||
int a[3] = {1, 2, 3};
|
||||
int b[2][3] = {{1, 2, 3}, {4, 5, 6}};
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, LikeClangd);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 3 + (3 * 2 + 2))
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 3 + (3 * 2 + 2));
|
||||
}
|
||||
|
||||
TEST(InlayHint, Designators) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, Designators) {
|
||||
run(R"cpp(
|
||||
struct A{ int x; int y;};
|
||||
A a = {.x = 1, .y = 2};
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, LikeClangd);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 0)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 0);
|
||||
}
|
||||
|
||||
TEST(InlayHint, IgnoreSimpleSetter) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, IgnoreSimpleSetter) {
|
||||
run(R"cpp(
|
||||
struct A {
|
||||
void setPara(int Para);
|
||||
void set_para(int para);
|
||||
@@ -323,23 +215,13 @@ void f() {
|
||||
a.set_para_meter(1);
|
||||
}
|
||||
|
||||
)cpp";
|
||||
)cpp");
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, LikeClangd);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 0)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 0);
|
||||
}
|
||||
|
||||
TEST(InlayHint, BlockEnd) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, BlockEnd) {
|
||||
run(R"cpp(
|
||||
struct A {
|
||||
int x;
|
||||
}$(1);
|
||||
@@ -362,48 +244,30 @@ struct Out {
|
||||
struct In {
|
||||
|
||||
};$(5)}$(6);
|
||||
)cpp";
|
||||
)cpp",
|
||||
{},
|
||||
{
|
||||
.blockEnd = true,
|
||||
.structSizeAndAlign = false,
|
||||
});
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
config::InlayHintOption option{
|
||||
.blockEnd = true,
|
||||
.structSizeAndAlign = false,
|
||||
};
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, option);
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 6)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 6);
|
||||
}
|
||||
|
||||
TEST(InlayHint, Lambda) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, Lambda) {
|
||||
run(R"cpp(
|
||||
auto l = []$(1) {
|
||||
return 1;
|
||||
}$(2);
|
||||
)cpp";
|
||||
)cpp",
|
||||
{},
|
||||
{.returnType = true, .blockEnd = true});
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, {.returnType = true, .blockEnd = true});
|
||||
|
||||
// dbg(res);
|
||||
|
||||
txs.equal(res.size(), 3)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 3);
|
||||
}
|
||||
|
||||
TEST(InlayHint, StructAndMemberHint) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, StructAndMemberHint) {
|
||||
run(R"cpp(
|
||||
struct A {
|
||||
int x;
|
||||
int y;
|
||||
@@ -412,47 +276,29 @@ struct A {
|
||||
int z;
|
||||
};
|
||||
};
|
||||
)cpp";
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
|
||||
config::InlayHintOption option{
|
||||
.blockEnd = false,
|
||||
.structSizeAndAlign = true,
|
||||
.memberSizeAndOffset = true,
|
||||
};
|
||||
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, option);
|
||||
|
||||
// dbg(res);
|
||||
)cpp",
|
||||
{},
|
||||
{
|
||||
.blockEnd = false,
|
||||
.structSizeAndAlign = true,
|
||||
.memberSizeAndOffset = true,
|
||||
});
|
||||
|
||||
/// TODO:
|
||||
/// if InlayHintOption::memberSizeAndOffset was implemented, the total hint count is 2 + 3.
|
||||
txs.equal(res.size(), 2 /*+ 3*/)
|
||||
//
|
||||
;
|
||||
EXPECT_EQ(result.size(), 2 /*+ 3*/);
|
||||
}
|
||||
|
||||
TEST(InlayHint, ImplicitCast) {
|
||||
const char* main = R"cpp(
|
||||
TEST_F(InlayHint, ImplicitCast) {
|
||||
run(R"cpp(
|
||||
int x = 1.0;
|
||||
)cpp";
|
||||
)cpp",
|
||||
{},
|
||||
{.implicitCast = true});
|
||||
|
||||
Tester txs("main.cpp", main);
|
||||
txs.run();
|
||||
auto& info = txs.info;
|
||||
auto res = feature::inlayHints({}, info, Converter, {.implicitCast = true});
|
||||
|
||||
// dbg(res);
|
||||
|
||||
/// FIXME:
|
||||
/// Hint count should be 1.
|
||||
txs.equal(res.size(), 0)
|
||||
//
|
||||
;
|
||||
/// FIXME: Hint count should be 1.
|
||||
EXPECT_EQ(result.size(), 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
// #include <Compiler/ParsedAST.h>
|
||||
#include <Feature/SemanticTokens.h>
|
||||
#include "Test/CTest.h"
|
||||
#include "Feature/SemanticTokens.h"
|
||||
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -8,3 +9,5 @@ TEST(Feature, SemanticTokens) {}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice::testing
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "IndexTester.h"
|
||||
#include "Test/IndexTester.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "Test/Test.h"
|
||||
#include "Server/Async.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {} // namespace
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "Test/Test.h"
|
||||
#include "Support/Compare.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "Test/Test.h"
|
||||
#include "Support/Enum.h"
|
||||
#include "Support/JSON.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(Reflection, EnumName) {
|
||||
enum class E {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
};
|
||||
|
||||
static_assert(refl::enum_name(E::A) == "A");
|
||||
static_assert(refl::enum_name(E::B) == "B");
|
||||
static_assert(refl::enum_name(E::C) == "C");
|
||||
|
||||
enum F {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
};
|
||||
|
||||
static_assert(refl::enum_name(F::A) == "A");
|
||||
static_assert(refl::enum_name(F::B) == "B");
|
||||
static_assert(refl::enum_name(F::C) == "C");
|
||||
}
|
||||
|
||||
struct Color : refl::Enum<Color> {
|
||||
enum Kind : uint8_t {
|
||||
Red,
|
||||
@@ -20,7 +41,7 @@ struct Color : refl::Enum<Color> {
|
||||
constexpr inline static auto InvalidEnum = Invalid;
|
||||
};
|
||||
|
||||
TEST(Support, NormalEnum) {
|
||||
TEST(Reflection, NormalEnum) {
|
||||
constexpr Color invalid = {};
|
||||
static_assert(!invalid);
|
||||
static_assert(invalid.value() == Color::Invalid);
|
||||
@@ -30,25 +51,19 @@ TEST(Support, NormalEnum) {
|
||||
constexpr Color blue = Color::Blue;
|
||||
constexpr Color yellow = Color::Yellow;
|
||||
|
||||
static_assert(red.name() == "Red" && red.value() == 0);
|
||||
static_assert(green.name() == "Green" && green.value() == 1);
|
||||
static_assert(blue.name() == "Blue" && blue.value() == 2);
|
||||
static_assert(yellow.name() == "Yellow" && yellow.value() == 3);
|
||||
static_assert(red.name() == "Red" && red.kind() == Color::Red && red.value() == 0);
|
||||
static_assert(green.name() == "Green" && green.kind() == Color::Green && green.value() == 1);
|
||||
static_assert(blue.name() == "Blue" && blue.kind() == Color::Blue && blue.value() == 2);
|
||||
static_assert(yellow.name() == "Yellow" && yellow.kind() == Color::Yellow &&
|
||||
yellow.value() == 3);
|
||||
|
||||
static_assert(red != green && red != blue && red != yellow);
|
||||
|
||||
constexpr Color red2 = Color(0);
|
||||
static_assert(red == red2);
|
||||
|
||||
EXPECT_EQ(json::serialize(red), json::Value(0));
|
||||
EXPECT_EQ(json::serialize(green), json::Value(1));
|
||||
EXPECT_EQ(json::serialize(blue), json::Value(2));
|
||||
EXPECT_EQ(json::serialize(yellow), json::Value(3));
|
||||
|
||||
EXPECT_EQ(json::deserialize<Color>(json::Value(0)), red);
|
||||
EXPECT_EQ(json::deserialize<Color>(json::Value(1)), green);
|
||||
EXPECT_EQ(json::deserialize<Color>(json::Value(2)), blue);
|
||||
EXPECT_EQ(json::deserialize<Color>(json::Value(3)), yellow);
|
||||
static_assert(red.is_one_of(Color::Red));
|
||||
static_assert(!red.is_one_of(Color::Blue));
|
||||
}
|
||||
|
||||
struct Mask : refl::Enum<Mask, true, uint32_t> {
|
||||
@@ -62,7 +77,7 @@ struct Mask : refl::Enum<Mask, true, uint32_t> {
|
||||
using Enum::Enum;
|
||||
};
|
||||
|
||||
TEST(Support, MaskEnum) {
|
||||
TEST(Reflection, MaskEnum) {
|
||||
constexpr Mask invalid = {};
|
||||
static_assert(!invalid);
|
||||
static_assert(invalid.value() == 0);
|
||||
@@ -93,16 +108,6 @@ TEST(Support, MaskEnum) {
|
||||
EXPECT_TRUE(bool(mask6 & Mask::A));
|
||||
EXPECT_TRUE(bool(mask6 & Mask::B));
|
||||
EXPECT_TRUE(bool(mask6 & Mask::C));
|
||||
|
||||
EXPECT_EQ(json::serialize(mask), json::Value(1));
|
||||
EXPECT_EQ(json::serialize(mask2), json::Value(2));
|
||||
EXPECT_EQ(json::serialize(mask3), json::Value(4));
|
||||
EXPECT_EQ(json::serialize(mask4), json::Value(8));
|
||||
|
||||
EXPECT_EQ(json::deserialize<Mask>(json::Value(1)), mask);
|
||||
EXPECT_EQ(json::deserialize<Mask>(json::Value(2)), mask2);
|
||||
EXPECT_EQ(json::deserialize<Mask>(json::Value(4)), mask3);
|
||||
EXPECT_EQ(json::deserialize<Mask>(json::Value(8)), mask4);
|
||||
}
|
||||
|
||||
struct StringEnum : refl::Enum<StringEnum, false, std::string_view> {
|
||||
@@ -116,7 +121,7 @@ struct StringEnum : refl::Enum<StringEnum, false, std::string_view> {
|
||||
constexpr inline static std::array All = {A, B, C, D};
|
||||
};
|
||||
|
||||
TEST(Support, StringEnum) {
|
||||
TEST(Reflection, StringEnum) {
|
||||
constexpr StringEnum a = StringEnum::A;
|
||||
constexpr StringEnum b = StringEnum::B;
|
||||
constexpr StringEnum c = StringEnum::C;
|
||||
@@ -128,19 +133,9 @@ TEST(Support, StringEnum) {
|
||||
static_assert(d.value() == "D");
|
||||
|
||||
static_assert(a != b && a != c && a != d);
|
||||
|
||||
EXPECT_EQ(json::serialize(a), json::Value("A"));
|
||||
EXPECT_EQ(json::serialize(b), json::Value("B"));
|
||||
EXPECT_EQ(json::serialize(c), json::Value("C"));
|
||||
EXPECT_EQ(json::serialize(d), json::Value("D"));
|
||||
|
||||
EXPECT_EQ(json::deserialize<StringEnum>(json::Value("A")), a);
|
||||
EXPECT_EQ(json::deserialize<StringEnum>(json::Value("B")), b);
|
||||
EXPECT_EQ(json::deserialize<StringEnum>(json::Value("C")), c);
|
||||
EXPECT_EQ(json::deserialize<StringEnum>(json::Value("D")), d);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "Test/Test.h"
|
||||
#include "Support/JSON.h"
|
||||
|
||||
namespace clice {
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct ValueRef {
|
||||
std::size_t index;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
template <>
|
||||
struct json::Serde<ValueRef> {
|
||||
struct clice::json::Serde<ValueRef> {
|
||||
constexpr inline static bool stateful = true;
|
||||
|
||||
std::vector<int>& encoder;
|
||||
@@ -26,28 +41,153 @@ struct json::Serde<ValueRef> {
|
||||
}
|
||||
};
|
||||
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(Support, JSON) {
|
||||
struct Point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
TEST(JSON, String) {
|
||||
json::Value expect = "hello";
|
||||
|
||||
json::Value object = json::Object{
|
||||
{"x", 1},
|
||||
{"y", 2},
|
||||
};
|
||||
std::string input = "hello";
|
||||
EXPECT_EQ(json::serialize(input), expect);
|
||||
EXPECT_EQ(json::deserialize<std::string>(expect), input);
|
||||
|
||||
auto point = json::deserialize<Point>(std::move(object));
|
||||
ASSERT_EQ(point.x, 1);
|
||||
ASSERT_EQ(point.y, 2);
|
||||
std::string_view input2 = "hello";
|
||||
EXPECT_EQ(json::serialize(input2), expect);
|
||||
EXPECT_EQ(json::deserialize<std::string_view>(expect), input2);
|
||||
|
||||
auto result = json::serialize(point);
|
||||
ASSERT_EQ(result, object);
|
||||
llvm::StringRef input3 = "hello";
|
||||
EXPECT_EQ(json::serialize(input3), expect);
|
||||
EXPECT_EQ(json::deserialize<llvm::StringRef>(expect), input3);
|
||||
|
||||
llvm::SmallString<5> input4 = {"hello"};
|
||||
EXPECT_EQ(json::serialize(input4), expect);
|
||||
EXPECT_EQ(json::deserialize<llvm::SmallString<5>>(expect), input4);
|
||||
}
|
||||
|
||||
TEST(Support, StatefulSerde) {
|
||||
TEST(JSON, MapRange) {
|
||||
json::Value expect = json::Object{
|
||||
{"1", 2},
|
||||
{"3", 4},
|
||||
{"5", 6}
|
||||
};
|
||||
|
||||
std::map<int, int> input = {
|
||||
{1, 2},
|
||||
{3, 4},
|
||||
{5, 6}
|
||||
};
|
||||
|
||||
EXPECT_EQ(json::serialize(input), expect);
|
||||
EXPECT_EQ(input, json::deserialize<decltype(input)>(expect));
|
||||
|
||||
std::unordered_map<int, int> input2 = {
|
||||
{1, 2},
|
||||
{3, 4},
|
||||
{5, 6}
|
||||
};
|
||||
EXPECT_EQ(json::serialize(input2), expect);
|
||||
EXPECT_EQ(input2, json::deserialize<decltype(input2)>(expect));
|
||||
|
||||
llvm::DenseMap<int, int> input4 = {
|
||||
{1, 2},
|
||||
{3, 4},
|
||||
{5, 6}
|
||||
};
|
||||
EXPECT_EQ(json::serialize(input4), expect);
|
||||
EXPECT_EQ(input4, json::deserialize<decltype(input4)>(expect));
|
||||
}
|
||||
|
||||
TEST(JSON, SetRange) {
|
||||
json::Value expect = {1, 2, 3, 4, 5};
|
||||
|
||||
std::set<int> input = {1, 2, 3, 4, 5};
|
||||
EXPECT_EQ(input, json::deserialize<decltype(input)>(expect));
|
||||
|
||||
std::unordered_set<int> input2 = {1, 2, 3, 4, 5};
|
||||
EXPECT_EQ(input2, json::deserialize<decltype(input2)>(expect));
|
||||
}
|
||||
|
||||
TEST(JSON, SequenceRange) {
|
||||
json::Value expect = {1, 2, 3, 4, 5};
|
||||
|
||||
std::vector<int> input = {1, 2, 3, 4, 5};
|
||||
EXPECT_EQ(json::serialize(input), expect);
|
||||
EXPECT_EQ(json::deserialize<std::vector<int>>(expect), input);
|
||||
|
||||
llvm::ArrayRef<int> input2 = input;
|
||||
EXPECT_EQ(json::serialize(input2), expect);
|
||||
|
||||
llvm::SmallVector<int, 5> input3 = {1, 2, 3, 4, 5};
|
||||
EXPECT_EQ(json::serialize(input3), expect);
|
||||
EXPECT_EQ((json::deserialize<llvm::SmallVector<int, 5>>(expect)), input3);
|
||||
}
|
||||
|
||||
TEST(JSON, Enum) {
|
||||
enum class E { A, B, C };
|
||||
|
||||
json::Value expect = json::Value(1);
|
||||
|
||||
E input = E::B;
|
||||
EXPECT_EQ(json::serialize(input), expect);
|
||||
EXPECT_EQ(json::deserialize<E>(expect), input);
|
||||
|
||||
struct Color : refl::Enum<Color, false, int> {
|
||||
enum Kind {
|
||||
Red = 0,
|
||||
Green,
|
||||
Blue,
|
||||
Yellow,
|
||||
};
|
||||
|
||||
using Enum::Enum;
|
||||
};
|
||||
|
||||
json::Value expect2 = json::Value(2);
|
||||
|
||||
Color input2 = Color::Blue;
|
||||
EXPECT_EQ(json::serialize(input2), expect2);
|
||||
EXPECT_EQ(json::deserialize<Color>(expect2), input2);
|
||||
}
|
||||
|
||||
TEST(JSON, Struct) {
|
||||
struct A {
|
||||
int x;
|
||||
int y;
|
||||
|
||||
bool operator== (const A& other) const = default;
|
||||
};
|
||||
|
||||
json::Value expect = json::Object{
|
||||
{"x", 1},
|
||||
{"y", 2}
|
||||
};
|
||||
|
||||
A input = {1, 2};
|
||||
EXPECT_EQ(json::serialize(input), expect);
|
||||
EXPECT_EQ(json::deserialize<A>(expect), input);
|
||||
|
||||
struct B {
|
||||
A a;
|
||||
std::string s;
|
||||
|
||||
bool operator== (const B& other) const = default;
|
||||
};
|
||||
|
||||
json::Value expect2 = json::Object{
|
||||
{"a", json::Object{{"x", 1}, {"y", 2}}},
|
||||
{"s", "hello" }
|
||||
};
|
||||
|
||||
B input2 = {
|
||||
{1, 2},
|
||||
"hello",
|
||||
};
|
||||
EXPECT_EQ(json::serialize(input2), expect2);
|
||||
EXPECT_EQ(json::deserialize<B>(expect2), input2);
|
||||
}
|
||||
|
||||
TEST(JSON, StatefulSerde) {
|
||||
struct Refs {
|
||||
std::vector<ValueRef> data;
|
||||
};
|
||||
@@ -80,4 +220,4 @@ TEST(Support, StatefulSerde) {
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "Test/Test.h"
|
||||
#include "Support/Struct.h"
|
||||
#include "Support/JSON.h"
|
||||
|
||||
namespace clice {
|
||||
namespace clice::testing {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -15,7 +14,53 @@ struct X {
|
||||
|
||||
static_assert(std::is_same_v<refl::member_types<X>, type_list<int, int>>);
|
||||
|
||||
TEST(Support, Struct) {
|
||||
TEST(Reflection, FieldName) {
|
||||
static struct X {
|
||||
int a;
|
||||
int b;
|
||||
} x;
|
||||
|
||||
static_assert(refl::impl::member_name<&x.a>() == "a", "Member name mismatch");
|
||||
static_assert(refl::impl::member_name<&x.b>() == "b", "Member name mismatch");
|
||||
static_assert(refl::member_names<X>() == std::array<std::string_view, 2>{"a", "b"});
|
||||
|
||||
static struct Y {
|
||||
X x;
|
||||
} y;
|
||||
|
||||
static_assert(refl::impl::member_name<&y.x>() == "x", "Member name mismatch");
|
||||
static_assert(refl::impl::member_name<&y.x.a>() == "a", "Member name mismatch");
|
||||
static_assert(refl::impl::member_name<&y.x.b>() == "b", "Member name mismatch");
|
||||
static_assert(refl::member_names<Y>() == std::array<std::string_view, 1>{"x"});
|
||||
|
||||
struct H {
|
||||
X x;
|
||||
Y y;
|
||||
H() = delete;
|
||||
};
|
||||
|
||||
static union Z {
|
||||
char dummy;
|
||||
H h;
|
||||
|
||||
Z() {};
|
||||
~Z() {};
|
||||
} z;
|
||||
|
||||
static_assert(refl::impl::member_name<&z.h.x>() == "x", "Member name mismatch");
|
||||
static_assert(refl::impl::member_name<&z.h.y>() == "y", "Member name mismatch");
|
||||
static_assert(refl::impl::member_name<&z.h.y.x>() == "x", "Member name mismatch");
|
||||
|
||||
struct M {
|
||||
X x;
|
||||
Y y;
|
||||
H h;
|
||||
};
|
||||
|
||||
static_assert(refl::member_names<M>() == std::array<std::string_view, 3>{"x", "y", "h"});
|
||||
}
|
||||
|
||||
TEST(Reflection, Foreach) {
|
||||
bool x = false, y = false;
|
||||
refl::foreach(X{1, 2}, [&](auto name, auto value) {
|
||||
if(name == "x") {
|
||||
@@ -35,27 +80,15 @@ TEST(Support, Struct) {
|
||||
EXPECT_TRUE(refl::foreach(x1, x2, [](auto& lhs, auto& rhs) { return lhs = rhs; }));
|
||||
EXPECT_EQ(x1.x, 3);
|
||||
EXPECT_EQ(x1.y, 4);
|
||||
|
||||
auto j1 = json::Value(json::Object{
|
||||
{"x", 3},
|
||||
{"y", 4},
|
||||
});
|
||||
EXPECT_EQ(json::serialize(x1), j1);
|
||||
|
||||
auto j2 = json::Value(json::Object{
|
||||
{"x", 3},
|
||||
{"y", 4},
|
||||
});
|
||||
EXPECT_EQ(x2, json::deserialize<X>(j2));
|
||||
}
|
||||
|
||||
inherited_struct(Y, X) {
|
||||
int z;
|
||||
};
|
||||
TEST(Reflection, Inheritance) {
|
||||
inherited_struct(Y, X) {
|
||||
int z;
|
||||
};
|
||||
|
||||
static_assert(std::is_same_v<refl::member_types<Y>, type_list<int, int, int>>);
|
||||
static_assert(std::is_same_v<refl::member_types<Y>, type_list<int, int, int>>);
|
||||
|
||||
TEST(Support, Inheritance) {
|
||||
bool x = false, y = false, z = false;
|
||||
refl::foreach(Y{1, 2, 3}, [&](auto name, auto value) {
|
||||
if(name == "x") {
|
||||
@@ -79,26 +112,41 @@ TEST(Support, Inheritance) {
|
||||
EXPECT_EQ(y1.x, 4);
|
||||
EXPECT_EQ(y1.y, 5);
|
||||
EXPECT_EQ(y1.z, 6);
|
||||
}
|
||||
|
||||
auto j1 = json::Value(json::Object{
|
||||
{"x", 4},
|
||||
{"y", 5},
|
||||
{"z", 6},
|
||||
});
|
||||
EXPECT_EQ(json::serialize(y1), j1);
|
||||
TEST(Reflection, TupleLike) {
|
||||
std::pair<int, int> p = {1, 2};
|
||||
|
||||
auto j2 = json::Value(json::Object{
|
||||
{"x", 4},
|
||||
{"y", 5},
|
||||
{"z", 6},
|
||||
static_assert(refl::member_names<decltype(p)>() == std::array<std::string_view, 2>{"0", "1"});
|
||||
|
||||
std::tuple<int, int> t = {1, 2};
|
||||
|
||||
static_assert(refl::member_names<decltype(t)>() == std::array<std::string_view, 2>{"0", "1"});
|
||||
|
||||
bool x = false, y = false;
|
||||
refl::foreach(t, [&](auto name, auto value) {
|
||||
if(name == "0") {
|
||||
x = true;
|
||||
EXPECT_EQ(value, 1);
|
||||
} else if(name == "1") {
|
||||
y = true;
|
||||
EXPECT_EQ(value, 2);
|
||||
} else {
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
});
|
||||
auto y3 = json::deserialize<Y>(j2);
|
||||
EXPECT_EQ(y2.x, y3.x);
|
||||
EXPECT_EQ(y2.y, y3.y);
|
||||
EXPECT_EQ(y2.z, y3.z);
|
||||
EXPECT_TRUE(x && y);
|
||||
|
||||
std::tuple<int, int> t1 = {1, 2};
|
||||
std::tuple<int, int> t2 = {3, 4};
|
||||
|
||||
EXPECT_TRUE(refl::foreach(t1, t2, [](auto& lhs, auto& rhs) { return lhs = rhs; }));
|
||||
EXPECT_EQ(std::get<0>(t1), 3);
|
||||
EXPECT_EQ(std::get<1>(t1), 4);
|
||||
EXPECT_EQ(std::get<0>(t2), 3);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace clice
|
||||
} // namespace clice::testing
|
||||
|
||||
|
||||
Reference in New Issue
Block a user