Clear Project (#40)

This commit is contained in:
ykiko
2025-01-16 22:48:35 +08:00
committed by GitHub
parent 2476020c71
commit 2f44874cbf
56 changed files with 1500 additions and 1696 deletions

View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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",
}
]
}

View File

@@ -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

View File

@@ -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"
}
]
}
}

View File

@@ -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.

View File

@@ -14,7 +14,6 @@
#include "Feature/SignatureHelp.h"
#include "Feature/CodeAction.h"
#include "Feature/Formatting.h"
#include "Support/Support.h"
namespace clice::proto {

View File

@@ -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:

View File

@@ -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 {

View File

@@ -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"

View File

@@ -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;
};

View File

@@ -1,6 +1,6 @@
#pragma once
#include <Compiler/Clang.h>
#include "Clang.h"
namespace clice {

View File

@@ -1,6 +1,6 @@
#pragma once
#include <Compiler/Clang.h>
#include "Clang.h"
namespace clice {

View File

@@ -1,6 +1,6 @@
#pragma once
#include <Compiler/Clang.h>
#include "Clang.h"
namespace clice {

View File

@@ -1,4 +1,4 @@
#include <Compiler/Clang.h>
#include "Clang.h"
namespace clice {

View File

@@ -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;

View File

@@ -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);

View File

@@ -1,6 +1,6 @@
#pragma once
#include <Basic/Location.h>
#include "Basic/Location.h"
namespace clice::proto {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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>;
})

View File

@@ -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

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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

View File

@@ -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());
}
};

View File

@@ -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
View 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

View File

@@ -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
View 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
View 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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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()) {

View File

@@ -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();

View File

@@ -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

View File

@@ -1,8 +1,7 @@
#include "Test/Test.h"
#include "Compiler/Command.h"
namespace clice {
namespace clice::testing {
namespace {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -1,8 +1,9 @@
#include "gtest/gtest.h"
#include "Test/Test.h"
#include "Compiler/Preamble.h"
namespace {
namespace clice::testing {
namespace {}
} // namespace clice::testing
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,6 +1,6 @@
#include "IndexTester.h"
#include "Test/IndexTester.h"
namespace clice {
namespace clice::testing {
namespace {

View File

@@ -1,7 +1,7 @@
#include "Test/Test.h"
#include "Server/Async.h"
namespace clice {
namespace clice::testing {
namespace {} // namespace

View File

@@ -1,7 +1,7 @@
#include "gtest/gtest.h"
#include "Test/Test.h"
#include "Support/Compare.h"
namespace clice {
namespace clice::testing {
namespace {

View File

@@ -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

View File

@@ -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

View File

@@ -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