diff --git a/.clang-format b/.clang-format index 33d2437a2..f52aacde4 100644 --- a/.clang-format +++ b/.clang-format @@ -38,7 +38,7 @@ BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: true BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon -BreakAdjacentStringLiterals: false +BreakAdjacentStringLiterals: true BreakStringLiterals: false CompactNamespaces: false Cpp11BracedListStyle: true @@ -141,6 +141,9 @@ KeepEmptyLines: AtStartOfFile: false StatementMacros: + # Field-position annotation: always break between the macro and the + # annotated declaration, so ` name;` gets its own line. + - KOTATSU_ANNOTATE - DECO_CFG_START - DECO_CFG - DECO_CFG_END diff --git a/benchmarks/scan_benchmark.cpp b/benchmarks/scan_benchmark.cpp index 66e025d82..f382d192c 100644 --- a/benchmarks/scan_benchmark.cpp +++ b/benchmarks/scan_benchmark.cpp @@ -96,7 +96,7 @@ void export_graph_json(const PathPool& path_pool, export_data.files.push_back(std::move(node)); } - auto json = kota::codec::json::to_json(export_data); + auto json = kota::codec::json::to_string(export_data); if(!json) { std::println(stderr, "Failed to serialize dependency graph"); return; diff --git a/cmake/package.cmake b/cmake/package.cmake index 389fd78e5..1f649512a 100644 --- a/cmake/package.cmake +++ b/cmake/package.cmake @@ -30,7 +30,7 @@ set(ENABLE_ROARING_MICROBENCHMARKS OFF CACHE INTERNAL "" FORCE) FetchContent_Declare( kotatsu GIT_REPOSITORY https://github.com/clice-io/kotatsu - GIT_TAG c516e3ae0ca3c7d7fb35fdcfdc7c6a111adef764 + GIT_TAG af2b6d1c2bf19d5b9cd643e7dfa43739bfffb361 ) set(KOTA_ENABLE_ZEST ON) diff --git a/src/command/command.cpp b/src/command/command.cpp index cb0b0fd47..ff6a1e576 100644 --- a/src/command/command.cpp +++ b/src/command/command.cpp @@ -245,7 +245,8 @@ std::optional CompilationDatabase::load(llvm::StringRef path) { simdjson::ondemand::object obj; if(element.get_object().get(obj)) { LOG_ERROR( - "Invalid compilation database in {}. Skipping item at index {}: " "item is not an object.", + "Invalid compilation database in {}. Skipping item at index {}: " + "item is not an object.", path, index); ++index; @@ -255,7 +256,8 @@ std::optional CompilationDatabase::load(llvm::StringRef path) { std::string_view dir_sv, file_sv; if(obj["directory"].get_string().get(dir_sv)) { LOG_ERROR( - "Invalid compilation database in {}. Skipping item at index {}: " "'directory' key is missing.", + "Invalid compilation database in {}. Skipping item at index {}: " + "'directory' key is missing.", path, index); ++index; @@ -264,7 +266,8 @@ std::optional CompilationDatabase::load(llvm::StringRef path) { if(obj["file"].get_string().get(file_sv)) { LOG_ERROR( - "Invalid compilation database in {}. Skipping item at index {}: " "'file' key is missing.", + "Invalid compilation database in {}. Skipping item at index {}: " + "'file' key is missing.", path, index); ++index; @@ -316,7 +319,8 @@ std::optional CompilationDatabase::load(llvm::StringRef path) { std::string_view cmd_sv; if(obj["command"].get_string().get(cmd_sv)) { LOG_ERROR( - "Invalid compilation database in {}. Skipping item at index {}: " "neither 'arguments' nor 'command' key is present.", + "Invalid compilation database in {}. Skipping item at index {}: " + "neither 'arguments' nor 'command' key is present.", path, index); ++index; diff --git a/src/driver/inspect.cc b/src/driver/inspect.cc index 48359930f..f09c0ea8f 100644 --- a/src/driver/inspect.cc +++ b/src/driver/inspect.cc @@ -43,34 +43,35 @@ struct InspectOptions { DecoFlag(names = {"-h", "--help"}, help = "Show help", required = false) help; - DecoInput( - meta_var = " ", - help = - "Feature to run (code_completion, document_links, document_symbol, " "folding_range, hover, inlay_hint, semantic_tokens, signature_help, " "tu_index) and a source file or directory", - required = false) + DecoInput(meta_var = " ", + help = + "Feature to run (code_completion, document_links, document_symbol, " + "folding_range, hover, inlay_hint, semantic_tokens, signature_help, " + "tu_index) and a source file or directory", + required = false) > inputs; - DecoFlag( - names = {"--annotations"}, - help = - "Treat inputs as annotated fixture sources: strip inline " "§-markers before compiling (the snap-test grammar)", - required = false) + DecoFlag(names = {"--annotations"}, + help = + "Treat inputs as annotated fixture sources: strip inline " + "§-markers before compiling (the snap-test grammar)", + required = false) annotations; - DecoKVStyled( - kota::deco::decl::KVStyle::JoinedOrSeparate, - names = {"--flags", "--flags="}, - help = - "Compile flags for the inputs as a JSON string array; " "replaces the compile_commands.json lookup", - required = false) + DecoKVStyled(kota::deco::decl::KVStyle::JoinedOrSeparate, + names = {"--flags", "--flags="}, + help = + "Compile flags for the inputs as a JSON string array; " + "replaces the compile_commands.json lookup", + required = false) flags; - DecoKVStyled( - kota::deco::decl::KVStyle::JoinedOrSeparate, - names = {"--config", "--config="}, - help = - "Feature options overlay as a JSON object " "(only features that take options accept it)", - required = false) + DecoKVStyled(kota::deco::decl::KVStyle::JoinedOrSeparate, + names = {"--config", "--config="}, + help = + "Feature options overlay as a JSON object " + "(only features that take options accept it)", + required = false) config; DecoKVStyled(kota::deco::decl::KVStyle::JoinedOrSeparate, @@ -142,9 +143,9 @@ struct StrictJson { /// The fixture's --config JSON overlaid on the feature's default options. /// The options struct doubles as its config section (all fields -/// `defaulted`), so decoding onto a fresh value IS the overlay: missing -/// keys keep the field initializers, exactly like the server's config -/// sections. Runners re-parse on each call; --config was validated up +/// `defaulted = true`), so decoding onto a fresh value IS the overlay: +/// missing keys keep the field initializers, exactly like the server's +/// config sections. Runners re-parse on each call; --config was validated up /// front in run_inspect, so their parse cannot fail. template std::optional parse_feature_config(llvm::StringRef config) { @@ -152,7 +153,7 @@ std::optional parse_feature_config(llvm::StringRef config) { if(config.empty()) { return options; } - if(auto result = kota::codec::json::from_json(config, options); !result) { + if(auto result = kota::codec::json::from_string(config, options); !result) { LOG_ERROR("invalid --config: {}", result.error().message); return std::nullopt; } @@ -789,7 +790,7 @@ int run_inspect(const InspectOptions& opts) { std::vector flags; if(opts.flags.has_value()) { - if(auto result = kota::codec::json::from_json(*opts.flags, flags); !result) { + if(auto result = kota::codec::json::from_string(*opts.flags, flags); !result) { LOG_ERROR("--flags is not a JSON string array: {}", result.error().message); return 1; } diff --git a/src/feature/feature.h b/src/feature/feature.h index b9cb021d8..52ba57eae 100644 --- a/src/feature/feature.h +++ b/src/feature/feature.h @@ -13,6 +13,7 @@ #include "support/filesystem.h" #include "support/markup.h" +#include "kota/codec/macro.h" #include "kota/ipc/lsp/position.h" #include "kota/ipc/lsp/protocol.h" #include "kota/ipc/lsp/uri.h" @@ -24,11 +25,10 @@ namespace clice::feature { namespace lsp = kota::ipc::lsp; namespace protocol = kota::ipc::protocol; -/// Feature options double as their clice.toml/initializationOptions config -/// sections: `defaulted` lets a decode leave unmentioned fields at the -/// values below, so the field initializers are the single source of every -/// default and a config source only ever overlays what it names. -using kota::meta::defaulted; +// Feature options double as their clice.toml/initializationOptions config +// sections: `defaulted = true` lets a decode leave unmentioned fields at +// their initializers, so those are the single source of every default and a +// config source only ever overlays what it names. using kota::ipc::lsp::LineMap; using kota::ipc::lsp::PositionEncoding; @@ -86,22 +86,46 @@ inline auto to_range(const LineMap& map, LocalSourceRange range) -> std::optiona /// Corresponds to the `[code_completion]` section in clice.toml. struct CodeCompletionOptions { - defaulted enable_keyword_snippet = false; - defaulted enable_function_arguments_snippet = false; - defaulted enable_template_arguments_snippet = false; - defaulted insert_paren_in_function_call = false; - defaulted bundle_overloads = true; - defaulted limit = 0; + KOTATSU_ANNOTATE(defaulted = true, + description = "Complete keywords as snippets (not yet implemented).") + enable_keyword_snippet = false; + + KOTATSU_ANNOTATE(defaulted = true, + description = "Insert function arguments as a snippet on completion.") + enable_function_arguments_snippet = false; + + KOTATSU_ANNOTATE(defaulted = true, + description = + "Insert template arguments as a snippet on completion " + "(not yet implemented).") + enable_template_arguments_snippet = false; + + KOTATSU_ANNOTATE(defaulted = true, + description = + "Insert parentheses when completing a function call " + "(not yet implemented).") + insert_paren_in_function_call = false; + + KOTATSU_ANNOTATE(defaulted = true, + description = "Collapse an overload set into a single completion item.") + bundle_overloads = true; + + KOTATSU_ANNOTATE(defaulted = true, + description = "Maximum number of completion items (not yet implemented).") + limit = 0; }; /// Corresponds to the `[hover]` section in clice.toml. struct HoverOptions { - /// Render the hover card as markdown rather than plain text. - defaulted parse_comment_as_markdown = true; - - /// Show the desugared form of a type, e.g. `vector::size_type (aka - /// unsigned long)`. - defaulted show_aka = true; + KOTATSU_ANNOTATE(defaulted = true, + description = "Render the hover card as markdown rather than plain text.") + parse_comment_as_markdown = true; + + KOTATSU_ANNOTATE(defaulted = true, + description = + "Show the desugared form of a type, e.g. " + "`vector::size_type (aka unsigned long)`.") + show_aka = true; }; /// Contains detailed information about a symbol. Especially useful when @@ -206,13 +230,32 @@ void parse_documentation(llvm::StringRef input, markup::Document& output); /// Corresponds to the `[inlay_hints]` section in clice.toml. struct InlayHintsOptions { - defaulted enabled = true; - defaulted parameters = true; - defaulted deduced_types = true; - defaulted designators = true; - defaulted block_end = false; - defaulted default_arguments = false; - defaulted type_name_limit = 32; + KOTATSU_ANNOTATE(defaulted = true, description = "Master switch for inlay hints.") + enabled = true; + + KOTATSU_ANNOTATE(defaulted = true, description = "Show parameter name hints at call sites.") + parameters = true; + + KOTATSU_ANNOTATE(defaulted = true, + description = "Show deduced types for `auto` and templated declarations.") + deduced_types = true; + + KOTATSU_ANNOTATE(defaulted = true, + description = "Show designators in aggregate initialization.") + designators = true; + + KOTATSU_ANNOTATE(defaulted = true, + description = "Show a hint naming the construct after a closing brace.") + block_end = false; + + KOTATSU_ANNOTATE(defaulted = true, description = "Show omitted default arguments.") + default_arguments = false; + + KOTATSU_ANNOTATE(defaulted = true, + description = + "Character budget for a rendered type name; longer names " + "are truncated; 0 means no limit.") + type_name_limit = 32; }; struct SignatureHelpOptions {}; diff --git a/src/semantic/resolver.cpp b/src/semantic/resolver.cpp index faa7a9af4..d7d597275 100644 --- a/src/semantic/resolver.cpp +++ b/src/semantic/resolver.cpp @@ -331,7 +331,8 @@ class PseudoInstantiator { } LOG_DEBUG( - "{}" "default arg: '{}' = '{}'", + "{}" + "default arg: '{}' = '{}'", pad(), TTPD->getNameAsString(), result.getAsString()); @@ -602,7 +603,8 @@ class PseudoInstantiator { if(!resolved_type.isNull()) { if(auto members = lookup(resolved_type, name); !members.empty()) { LOG_DEBUG( - "{}" "found '{}' via base '{}'", + "{}" + "found '{}' via base '{}'", pad(), name.getAsString(), resolved_type.getAsString()); @@ -660,7 +662,8 @@ class PseudoInstantiator { CTD->getPartialSpecializations(partials); LOG_DEBUG( - "{}" "lookup '{}' in '{}' (partials={})", + "{}" + "lookup '{}' in '{}' (partials={})", pad(), name.getAsString(), CTD->getNameAsString(), @@ -678,7 +681,8 @@ class PseudoInstantiator { stack.pop(); if(!viable) { LOG_DEBUG( - "{}" "pruned partial '{}' (member absent)", + "{}" + "pruned partial '{}' (member absent)", pad(), partial->getNameAsString()); continue; @@ -696,7 +700,10 @@ class PseudoInstantiator { if(best && matched.size() > 1) { for(auto partial: matched) { if(partial != best && !more_specialized(context, best, partial)) { - LOG_DEBUG("{}" "ambiguous partials; degrading", pad()); + LOG_DEBUG( + "{}" + "ambiguous partials; degrading", + pad()); indent -= 1; return lookup_result(); } @@ -707,21 +714,34 @@ class PseudoInstantiator { /// need subsumption machinery); a structurally matching constrained /// partial is therefore unverifiable — degrade rather than trust it. if(best && best->getTemplateParameters()->hasAssociatedConstraints()) { - LOG_DEBUG("{}" "constrained partial; degrading", pad()); + LOG_DEBUG( + "{}" + "constrained partial; degrading", + pad()); indent -= 1; return lookup_result(); } if(best && deduce_template_arguments(best, arguments)) { - LOG_DEBUG("{}" "matched partial '{}'", pad(), best->getNameAsString()); + LOG_DEBUG( + "{}" + "matched partial '{}'", + pad(), + best->getNameAsString()); if(auto members = best->lookup(name); !members.empty()) { - LOG_DEBUG("{}" "found in 'partial'", pad()); + LOG_DEBUG( + "{}" + "found in 'partial'", + pad()); indent -= 1; return members; } if(auto members = lookup_in_bases(best, name); !members.empty()) { - LOG_DEBUG("{}" "found in 'base'", pad()); + LOG_DEBUG( + "{}" + "found in 'base'", + pad()); indent -= 1; return members; } @@ -733,13 +753,19 @@ class PseudoInstantiator { LOG_DEBUG("{}using primary template", pad()); auto CRD = CTD->getTemplatedDecl(); if(auto members = CRD->lookup(name); !members.empty()) { - LOG_DEBUG("{}" "found in 'primary'", pad()); + LOG_DEBUG( + "{}" + "found in 'primary'", + pad()); indent -= 1; return members; } if(auto members = lookup_in_bases(CRD, name); !members.empty()) { - LOG_DEBUG("{}" "found in 'base'", pad()); + LOG_DEBUG( + "{}" + "found in 'base'", + pad()); indent -= 1; return members; } @@ -1905,13 +1931,21 @@ class PseudoInstantiator { } clang::QualType resolve_dependent_name(const clang::DependentNameType* DNT) { - LOG_DEBUG("{}" "resolve '{}'", pad(), clang::QualType(DNT, 0).getAsString()); + LOG_DEBUG( + "{}" + "resolve '{}'", + pad(), + clang::QualType(DNT, 0).getAsString()); indent += 1; // Check cache. if(pack_narrowing == 0) { if(auto iter = resolved.find(DNT); iter != resolved.end()) { - LOG_DEBUG("{}" "→ '{}' (cached)", pad(), iter->second.getAsString()); + LOG_DEBUG( + "{}" + "→ '{}' (cached)", + pad(), + iter->second.getAsString()); indent -= 1; return iter->second; } @@ -1939,11 +1973,21 @@ class PseudoInstantiator { auto decl_name = llvm::dyn_cast(decl) ? llvm::dyn_cast(decl)->getNameAsString() : "?"; - LOG_DEBUG("{}" "found {} '{}' = '{}'", pad(), decl_kind, decl_name, type.getAsString()); + LOG_DEBUG( + "{}" + "found {} '{}' = '{}'", + pad(), + decl_kind, + decl_name, + type.getAsString()); // Step 1: substitute params (expand typedefs, no lookup). result = substitute(type); - LOG_DEBUG("{}" "substitute → '{}'", pad(), result.getAsString()); + LOG_DEBUG( + "{}" + "substitute → '{}'", + pad(), + result.getAsString()); // Pop lookup frames BEFORE further resolution. The substitute step already // used the full stack for parameter substitution. Resolution should only @@ -1966,7 +2010,11 @@ class PseudoInstantiator { active_resolutions.erase(DNT); if(!result.isNull()) { - LOG_DEBUG("{}" "→ '{}'", pad(), result.getAsString()); + LOG_DEBUG( + "{}" + "→ '{}'", + pad(), + result.getAsString()); indent -= 1; if(pack_narrowing == 0 && !truncated && !ctd_guard_tripped) { resolved.try_emplace(DNT, result); @@ -1983,7 +2031,11 @@ class PseudoInstantiator { /// (`T::template rebind`) to a concrete TST when lookup finds the /// template. clang::QualType resolve_dependent_template(const clang::TemplateSpecializationType* TST) { - LOG_DEBUG("{}" "resolve TST '{}'", pad(), clang::QualType(TST, 0).getAsString()); + LOG_DEBUG( + "{}" + "resolve TST '{}'", + pad(), + clang::QualType(TST, 0).getAsString()); indent += 1; auto& template_name = *TST->getTemplateName().getAsDependentTemplateName(); @@ -2023,7 +2075,11 @@ class PseudoInstantiator { type = rewrite(type, Policy::Resolve); } if(!type.isNull()) { - LOG_DEBUG("{}" "→ '{}' (alias)", pad(), type.getAsString()); + LOG_DEBUG( + "{}" + "→ '{}' (alias)", + pad(), + type.getAsString()); indent -= 1; if(cacheable && !truncated && !ctd_guard_tripped) { resolved.try_emplace(TST, type); @@ -2038,7 +2094,11 @@ class PseudoInstantiator { // Keep lookup frames on stack — the caller (e.g. rewrite_specifier // processing A::B::C) needs them for parameter substitution. auto result = make_specialization(clang::TemplateName(CTD), arguments); - LOG_DEBUG("{}" "→ TST '{}' (class)", pad(), result.getAsString()); + LOG_DEBUG( + "{}" + "→ TST '{}' (class)", + pad(), + result.getAsString()); indent -= 1; if(cacheable && !truncated && !ctd_guard_tripped) { resolved.try_emplace(TST, result); diff --git a/src/server/compiler/compiler.cpp b/src/server/compiler/compiler.cpp index eac666c63..af8ea3a47 100644 --- a/src/server/compiler/compiler.cpp +++ b/src/server/compiler/compiler.cpp @@ -101,9 +101,10 @@ static kota::codec::RawValue quarantine_diagnostics(unsigned crashes) { diagnostic.severity = protocol::DiagnosticSeverity::Error; diagnostic.source = "clice"; diagnostic.message = std::format( - "compiling this file crashed the language server worker {} times; " "the file is quarantined until it is edited", + "compiling this file crashed the language server worker {} times; " + "the file is quarantined until it is edited", crashes); - auto json = kota::codec::json::to_json(diagnostics); + auto json = kota::codec::json::to_string(diagnostics); return kota::codec::RawValue{json ? std::move(*json) : "[]"}; } @@ -1164,7 +1165,7 @@ kota::task<> Compiler::run_compile(std::shared_ptr session) { std::vector diagnostics; if(!result.value().diagnostics.empty()) { [[maybe_unused]] auto status = - kota::codec::json::from_json(result.value().diagnostics.data, diagnostics); + kota::codec::json::from_string(result.value().diagnostics.data, diagnostics); } session->trial_done = true; contexts.record_header_mode(pid, HeaderMode::SelfContained); diff --git a/src/server/compiler/indexer.cpp b/src/server/compiler/indexer.cpp index f7b48a2df..4d90d8226 100644 --- a/src/server/compiler/indexer.cpp +++ b/src/server/compiler/indexer.cpp @@ -596,7 +596,8 @@ kota::task<> Indexer::index_one(std::uint32_t server_path_id, // so there is no diagnostic surface. Cross-file references // into this file stay stale until its content changes. LOG_WARN( - "[{}/{}] Index giving up on {} after {} crash requeues; " "its cross-file data stays stale until it is edited: {}", + "[{}/{}] Index giving up on {} after {} crash requeues; " + "its cross-file data stays stale until it is edited: {}", index, total, file_path, diff --git a/src/server/protocol/serialize.h b/src/server/protocol/serialize.h index a828ec4ab..31ef198ef 100644 --- a/src/server/protocol/serialize.h +++ b/src/server/protocol/serialize.h @@ -10,7 +10,7 @@ namespace clice { /// Serialize a value to JSON RawValue using LSP config. template kota::codec::RawValue to_raw(const T& value) { - auto json = kota::codec::json::to_json(value); + auto json = kota::codec::json::to_string(value); return kota::codec::RawValue{json ? std::move(*json) : "null"}; } diff --git a/src/server/service/feature_router.cpp b/src/server/service/feature_router.cpp index 0a7f8dbfa..b658a842c 100644 --- a/src/server/service/feature_router.cpp +++ b/src/server/service/feature_router.cpp @@ -289,7 +289,7 @@ FeatureRouter::RawResult FeatureRouter::completion(std::shared_ptr sess item.kind = protocol::CompletionItemKind::File; items.push_back(std::move(item)); } - auto json = kota::codec::json::to_json(items); + auto json = kota::codec::json::to_string(items); co_return serde_raw{json ? std::move(*json) : "[]"}; } if(pctx.kind == CompletionContext::Import) { @@ -304,7 +304,7 @@ FeatureRouter::RawResult FeatureRouter::completion(std::shared_ptr sess item.insert_text = name + ";"; items.push_back(std::move(item)); } - auto json = kota::codec::json::to_json(items); + auto json = kota::codec::json::to_string(items); co_return serde_raw{json ? std::move(*json) : "[]"}; } } diff --git a/src/server/service/format.cpp b/src/server/service/format.cpp index 4fc057775..bf00a7248 100644 --- a/src/server/service/format.cpp +++ b/src/server/service/format.cpp @@ -46,7 +46,8 @@ static protocol::Diagnostic make_inferred_command_diagnostic(CommandSource sourc } diagnostic.source = "clice"; diagnostic.message = std::format( - "No compilation database entry for this file (compile command was {}), so some includes " "may not be found. Configure compile_commands.json for accurate diagnostics.", + "No compilation database entry for this file (compile command was {}), so some includes " + "may not be found. Configure compile_commands.json for accurate diagnostics.", source == CommandSource::Fallback ? "synthesized from defaults" : "inferred from an including file"); return diagnostic; @@ -55,7 +56,7 @@ static protocol::Diagnostic make_inferred_command_diagnostic(CommandSource sourc std::vector format_diagnostics(const CompileOutput& output) { std::vector diagnostics; if(!output.diagnostics.empty()) { - auto status = kota::codec::json::from_json(output.diagnostics.data, diagnostics); + auto status = kota::codec::json::from_string(output.diagnostics.data, diagnostics); if(!status) { LOG_WARN("Failed to deserialize diagnostics JSON"); } diff --git a/src/server/state/config.cpp b/src/server/state/config.cpp index b6abc86e7..27d557472 100644 --- a/src/server/state/config.cpp +++ b/src/server/state/config.cpp @@ -196,7 +196,7 @@ std::optional Config::load(llvm::StringRef path, if(!content) return std::nullopt; - auto result = kota::codec::toml::parse(*content); + auto result = kota::codec::toml::from_string(*content); if(!result) { LOG_ERROR("Invalid clice.toml {}: {}", path, result.error().to_string()); if(issues) @@ -209,7 +209,8 @@ std::optional Config::load(llvm::StringRef path, // misspelled option silently doing nothing) as Warning issues. if(issues) { Config probe{}; - if(auto strict = kota::codec::toml::from_toml(*content, probe); !strict) { + if(auto strict = kota::codec::toml::from_string(*content, probe); + !strict) { LOG_WARN("clice.toml {}: {}", path, strict.error().to_string()); issues->push_back(make_issue(ConfigIssue::Severity::Warning, path, strict.error())); } @@ -224,7 +225,7 @@ std::optional Config::load(llvm::StringRef path, std::optional Config::load_from_json(llvm::StringRef json, llvm::StringRef workspace_root) { Config config{}; - auto result = kota::codec::json::from_json(json, config); + auto result = kota::codec::json::from_string(json, config); if(!result) { LOG_WARN("Failed to parse initializationOptions JSON: {}", result.error().message); return std::nullopt; diff --git a/src/server/state/config.h b/src/server/state/config.h index c11664770..275e53982 100644 --- a/src/server/state/config.h +++ b/src/server/state/config.h @@ -8,13 +8,12 @@ #include "feature/feature.h" #include "support/glob_pattern.h" +#include "kota/codec/macro.h" #include "kota/meta/annotation.h" #include "llvm/ADT/StringRef.h" namespace clice { -using kota::meta::defaulted; - /// Defaults that are computed rather than written: a fresh Config queries /// them in its field initializers, so a default-constructed Config is /// already fully valid ("born valid") and no later pass fills options in. @@ -24,9 +23,17 @@ std::uint32_t default_max_stateless_worker_count(); /// A file-pattern rule that appends/removes compilation flags. /// Corresponds to `[[rules]]` in clice.toml. struct ConfigRule { - defaulted> patterns; - defaulted> append; - defaulted> remove; + KOTATSU_ANNOTATE(defaulted = true, + description = "Glob patterns selecting the files this rule applies to.") + > patterns; + + KOTATSU_ANNOTATE(defaulted = true, + description = "Compilation flags appended for matching files.") + > append; + + KOTATSU_ANNOTATE(defaulted = true, + description = "Compilation flags removed for matching files.") + > remove; }; /// Corresponds to the `[project]` section in clice.toml. Field @@ -34,36 +41,75 @@ struct ConfigRule { /// here because their defaults derive from the workspace root in /// finalize(). struct ProjectConfig { - defaulted clang_tidy = false; - - defaulted cache_dir; - defaulted logging_dir; - - defaulted> compile_commands_paths; - - defaulted enable_indexing = true; - defaulted idle_timeout_ms = 3000; - - /// Enables the clice/internal test hooks that can generate load on - /// demand (log floods). Off unless the harness asks for them. - defaulted test_hooks = false; - - defaulted stateful_worker_count = 2; - defaulted stateless_worker_count = default_stateless_worker_count(); - /// Dynamic scaling bounds for stateless workers; see WorkerPoolOptions. - defaulted min_stateless_worker_count = 1; - defaulted max_stateless_worker_count = default_max_stateless_worker_count(); - defaulted worker_memory_limit = 4ULL * 1024 * 1024 * 1024; + KOTATSU_ANNOTATE(defaulted = true, + description = "Run clang-tidy alongside compiler diagnostics.") + clang_tidy = false; + + KOTATSU_ANNOTATE(defaulted = true, + description = + "Directory for the index and PCH cache; empty derives it " + "from the workspace root.") + cache_dir; + + KOTATSU_ANNOTATE(defaulted = true, + description = + "Directory for log files; empty derives it from the " + "cache directory.") + logging_dir; + + KOTATSU_ANNOTATE(defaulted = true, description = "Paths searched for compile_commands.json.") + > compile_commands_paths; + + KOTATSU_ANNOTATE(defaulted = true, description = "Build the background index.") + enable_indexing = true; + + KOTATSU_ANNOTATE(defaulted = true, + description = + "Idle delay in milliseconds before background indexing " + "starts.") + idle_timeout_ms = 3000; + + /// The hooks can generate load on demand (log floods). + KOTATSU_ANNOTATE(defaulted = true, + description = + "Enable the clice/internal test hooks used by the test " + "harness.") + test_hooks = false; + + KOTATSU_ANNOTATE(defaulted = true, description = "Number of stateful workers.") + stateful_worker_count = 2; + + KOTATSU_ANNOTATE(defaulted = true, description = "Initial number of stateless workers.") + stateless_worker_count = default_stateless_worker_count(); + + /// See WorkerPoolOptions. + KOTATSU_ANNOTATE(defaulted = true, + description = "Lower bound for dynamic stateless-worker scaling.") + min_stateless_worker_count = 1; + + KOTATSU_ANNOTATE(defaulted = true, + description = "Upper bound for dynamic stateless-worker scaling.") + max_stateless_worker_count = default_max_stateless_worker_count(); + + KOTATSU_ANNOTATE(defaulted = true, description = "Per-stateful-worker memory limit in bytes.") + worker_memory_limit = 4ULL * 1024 * 1024 * 1024; }; /// Corresponds to the `[tracker]` section in clice.toml: the stat-polling -/// file tracker's intervals. 0 disables the loop (integration tests drive -/// ticks through the clice/internal/poll hook instead). +/// file tracker's intervals (integration tests drive ticks through the +/// clice/internal/poll hook instead). struct TrackerConfig { - /// Compilation database poll interval in seconds. - defaulted cdb_poll_seconds = 3; - /// Workspace file sweep interval in seconds. - defaulted workspace_poll_seconds = 30; + KOTATSU_ANNOTATE(defaulted = true, + description = + "Compilation database poll interval in seconds; 0 disables " + "polling.") + cdb_poll_seconds = 3; + + KOTATSU_ANNOTATE(defaulted = true, + description = + "Workspace file sweep interval in seconds; 0 disables " + "polling.") + workspace_poll_seconds = 30; }; struct CompiledRule { @@ -103,19 +149,34 @@ struct ConfigIssue { /// finalize() never fills option defaults; it only computes derived /// values from the merged result. struct Config { - defaulted project; - - defaulted tracker; - - defaulted hover; - - defaulted inlay_hints; - - defaulted code_completion; - - defaulted> rules; - - kota::meta::annotation, kota::meta::attrs::skip> compiled_rules; + KOTATSU_ANNOTATE(defaulted = true, + description = "The [project] section: project-wide server options.") + project; + + KOTATSU_ANNOTATE(defaulted = true, + description = "The [tracker] section: file tracker poll intervals.") + tracker; + + KOTATSU_ANNOTATE(defaulted = true, + description = "The [hover] section: hover rendering options.") + hover; + + KOTATSU_ANNOTATE(defaulted = true, + description = "The [inlay_hints] section: inlay hint options.") + inlay_hints; + + KOTATSU_ANNOTATE(defaulted = true, + description = "The [code_completion] section: code completion options.") + code_completion; + + KOTATSU_ANNOTATE(defaulted = true, + description = + "File-pattern rules that adjust compilation flags " + "([[rules]] in clice.toml).") + > rules; + + KOTATSU_ANNOTATE(skip = true) + > compiled_rules; /// Compute the values derived from the final merged config: default /// cache/logging directories, ${workspace} substitution, path diff --git a/src/server/state/session_store.cpp b/src/server/state/session_store.cpp index 8fccdd2f7..94c485133 100644 --- a/src/server/state/session_store.cpp +++ b/src/server/state/session_store.cpp @@ -81,7 +81,8 @@ void SessionStore::apply_change(Session& session, // the edit, which would silently desync every // subsequent position until a full sync or reopen. LOG_INFO( - "didChange range {}:{}-{}:{} does not fit the buffer " "(path_id={} version={}); clamped", + "didChange range {}:{}-{}:{} does not fit the buffer " + "(path_id={} version={}); clamped", range.start.line, range.start.character, range.end.line, diff --git a/src/server/state/workspace.cpp b/src/server/state/workspace.cpp index a83af0e8f..1705ba0cc 100644 --- a/src/server/state/workspace.cpp +++ b/src/server/state/workspace.cpp @@ -14,6 +14,7 @@ #include "syntax/scan.h" #include "kota/codec/json/json.h" +#include "kota/codec/macro.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" @@ -331,9 +332,14 @@ struct CacheDepEntry { // still load: the fields read back zeroed ("no fast path") and the first // staleness check re-earns them by hash. Their old entry-level build_at // is skipped as an unknown field. - kota::meta::defaulted size; - kota::meta::defaulted mtime_ns; - kota::meta::defaulted missing; + KOTATSU_ANNOTATE(defaulted = true) + size; + + KOTATSU_ANNOTATE(defaulted = true) + mtime_ns; + + KOTATSU_ANNOTATE(defaulted = true) + missing; }; struct CachePCHEntry { @@ -460,7 +466,7 @@ void Workspace::load_cache(ContextResolver& contexts) { } CacheData data; - auto status = kota::codec::json::from_json(*content, data); + auto status = kota::codec::json::from_string(*content, data); if(!status) { LOG_WARN("Failed to parse cache.json"); return; @@ -605,7 +611,7 @@ void Workspace::save_cache(const ContextResolver& contexts) { intern, intern_path); - auto json_str = kota::codec::json::to_json(data); + auto json_str = kota::codec::json::to_string(data); if(!json_str) { LOG_WARN("Failed to serialize cache.json"); return; diff --git a/src/server/transport/agentic.h b/src/server/transport/agentic.h index 806fc8bf3..20cc38e65 100644 --- a/src/server/transport/agentic.h +++ b/src/server/transport/agentic.h @@ -22,9 +22,10 @@ struct QueryOptions { port; DecoKV(style = deco::decl::KVStyle::JoinedOrSeparate, - help = "Query method (compileCommand, symbolSearch, definition, references, " - "documentSymbols, readSymbol, callGraph, typeHierarchy, projectFiles, " - "fileDeps, impactAnalysis, status, shutdown)", + help = + "Query method (compileCommand, symbolSearch, definition, references, " + "documentSymbols, readSymbol, callGraph, typeHierarchy, projectFiles, " + "fileDeps, impactAnalysis, status, shutdown)", required = false) method = "compileCommand"; diff --git a/src/server/transport/lsp_client.cpp b/src/server/transport/lsp_client.cpp index acd3b8791..5831f1c41 100644 --- a/src/server/transport/lsp_client.cpp +++ b/src/server/transport/lsp_client.cpp @@ -105,7 +105,7 @@ void LSPClient::register_lifecycle() { if(init.initialization_options.has_value()) { auto json = - kota::codec::json::to_json(*init.initialization_options); + kota::codec::json::to_string(*init.initialization_options); if(json) srv.init_options_json = std::move(*json); } diff --git a/src/server/transport/master_server.cpp b/src/server/transport/master_server.cpp index f9913f5e2..1bec108f5 100644 --- a/src/server/transport/master_server.cpp +++ b/src/server/transport/master_server.cpp @@ -85,7 +85,7 @@ void MasterServer::initialize() { std::string raw_init_options = init_options_json; if(!init_options_json.empty()) { - if(auto ov = kota::codec::json::parse(init_options_json, workspace.config); !ov) { + if(auto ov = kota::codec::json::from_string(init_options_json, workspace.config); !ov) { LOG_GUIDANCE("Failed to apply initializationOptions: {}", ov.error().to_string()); } else { LOG_INFO("Applied initializationOptions overlay"); @@ -506,7 +506,8 @@ void MasterServer::load_workspace() { auto cdb_path = discover_compile_commands(workspace.config, workspace_root); if(cdb_path.empty()) { LOG_GUIDANCE( - "No compile_commands.json found in workspace {}. Compile commands will be " "guessed; see https://clice.io/en/guide/quick-start for setup.", + "No compile_commands.json found in workspace {}. Compile commands will be " + "guessed; see https://clice.io/en/guide/quick-start for setup.", workspace_root); // Persisted index shards are CDB-independent; load them so a // database generated later (picked up by the CDB poll) starts from @@ -538,7 +539,8 @@ void MasterServer::load_workspace() { ? 100.0 * static_cast(report.includes_resolved) / report.includes_found : 100.0; LOG_INFO( - "Dependency scan: {}ms, {} files ({} source + {} header), " "{} edges, {}/{} resolved ({:.1f}%), {} waves", + "Dependency scan: {}ms, {} files ({} source + {} header), " + "{} edges, {}/{} resolved ({:.1f}%), {} waves", report.elapsed_ms, report.total_files, report.source_files, diff --git a/src/server/worker/stateful_worker.cpp b/src/server/worker/stateful_worker.cpp index 2c6e0188e..8f75367b7 100644 --- a/src/server/worker/stateful_worker.cpp +++ b/src/server/worker/stateful_worker.cpp @@ -276,7 +276,7 @@ void StatefulWorker::register_handlers() { if(doc->unit.completed() || doc->unit.fatal_error()) { auto diags = feature::diagnostics(doc->unit); - auto json = kota::codec::json::to_json(diags); + auto json = kota::codec::json::to_string(diags); result.diagnostics = kota::codec::RawValue{json ? std::move(*json) : "[]"}; LOG_INFO("Compile done: path={}, {}ms, {} diags, fatal={}", params.path, diff --git a/src/server/worker/worker_pool.cpp b/src/server/worker/worker_pool.cpp index 076142cef..ca83422e5 100644 --- a/src/server/worker/worker_pool.cpp +++ b/src/server/worker/worker_pool.cpp @@ -796,7 +796,8 @@ kota::task<> WorkerPool::monitor_loop() { void WorkerPool::tick_memory(double available_ratio) { LOG_DEBUG( - "Memory: {:.0f}% available, low_limit={}/{}, busy={} (low={}), " "queued=hi:{}/lo:{}, alive={}, sat={}/idle={}", + "Memory: {:.0f}% available, low_limit={}/{}, busy={} (low={}), " + "queued=hi:{}/lo:{}, alive={}, sat={}/idle={}", available_ratio * 100, low_limit, max_low_limit(), diff --git a/src/support/cache_store.cpp b/src/support/cache_store.cpp index 788a41569..4e9d778ec 100644 --- a/src/support/cache_store.cpp +++ b/src/support/cache_store.cpp @@ -275,7 +275,7 @@ std::expected CacheStore::open(llvm::StringRef root auto manifest_path = path::join(state->base, "manifest.json"); if(auto content = fs::read(manifest_path)) { ManifestData data; - if(kota::codec::json::from_json(*content, data)) { + if(kota::codec::json::from_string(*content, data)) { for(auto& entry: data.entries) { auto key = entry.ns + "/" + entry.key; state->manifest_atimes[key] = entry.atime; @@ -763,7 +763,7 @@ void CacheStore::State::checkpoint_locked() { } } - auto json = kota::codec::json::to_json(data); + auto json = kota::codec::json::to_string(data); if(!json) { LOG_WARN("CacheStore: failed to serialize manifest"); return; diff --git a/src/support/glob_pattern.cpp b/src/support/glob_pattern.cpp index 780943f25..79361092b 100644 --- a/src/support/glob_pattern.cpp +++ b/src/support/glob_pattern.cpp @@ -90,7 +90,8 @@ static std::expected, std::string> if(s[i] == '\\') { if(i == e) { return std::unexpected{ - "Invalid glob pattern, unmatched '[', with stray " "'\\' inside"}; + "Invalid glob pattern, unmatched '[', with stray " + "'\\' inside"}; } ++i; } diff --git a/src/syntax/annotation.cpp b/src/syntax/annotation.cpp index f9c7ae040..2a23dd286 100644 --- a/src/syntax/annotation.cpp +++ b/src/syntax/annotation.cpp @@ -67,7 +67,8 @@ AnnotatedSource AnnotatedSource::from(llvm::StringRef content) { return std::isalnum(static_cast(c)) || c == '_'; })) { LOG_FATAL( - "§({}) is not an identifier name; use §() for a nameless " "point before real parentheses.", + "§({}) is not an identifier name; use §() for a nameless " + "point before real parentheses.", key); } // `nameless_` is how unnamed markers key their results; diff --git a/src/syntax/dependency_graph.cpp b/src/syntax/dependency_graph.cpp index 20812ee9b..26d156951 100644 --- a/src/syntax/dependency_graph.cpp +++ b/src/syntax/dependency_graph.cpp @@ -784,7 +784,8 @@ kota::task<> scan_impl(CompilationDatabase& cdb, report.wave_stats.push_back(ws); LOG_INFO( - "Wave {}: {} files | read+scan={}ms resolve={}ms graph={}ms | next={} " "prefetch={}", + "Wave {}: {} files | read+scan={}ms resolve={}ms graph={}ms | next={} " + "prefetch={}", wave_num, current_wave.size(), p1, diff --git a/tests/unit/command/command_tests.cpp b/tests/unit/command/command_tests.cpp index 9077b305b..fee439824 100644 --- a/tests/unit/command/command_tests.cpp +++ b/tests/unit/command/command_tests.cpp @@ -299,7 +299,8 @@ TEST_CASE(CodegenFilter) { database.add_command( "fake", "main.cpp", - "clang++ -std=c++20 -fPIC -fno-omit-frame-pointer -fstack-protector-strong " "-fdata-sections -ffunction-sections -flto -fcolor-diagnostics -g main.cpp"sv); + "clang++ -std=c++20 -fPIC -fno-omit-frame-pointer -fstack-protector-strong " + "-fdata-sections -ffunction-sections -flto -fcolor-diagnostics -g main.cpp"sv); auto result = database.lookup("main.cpp", quiet_options()).front().to_argv(); auto argv = print_argv(result); diff --git a/tests/unit/server/compile_graph_integration_tests.cpp b/tests/unit/server/compile_graph_integration_tests.cpp index 4d1300fbd..60910a681 100644 --- a/tests/unit/server/compile_graph_integration_tests.cpp +++ b/tests/unit/server/compile_graph_integration_tests.cpp @@ -157,7 +157,9 @@ void execute(F&& fn) { /// ============================================================================ TEST_CASE(single_module) { - env.tmp.touch("mod_a.cppm", "export module A;\n" "export int foo() { return 42; }\n"); + env.tmp.touch("mod_a.cppm", + "export module A;\n" + "export int foo() { return 42; }\n"); auto json = build_cdb_json({ {env.tmp.root, env.tmp.path("mod_a.cppm"), {}} @@ -177,7 +179,9 @@ TEST_CASE(single_module) { } TEST_CASE(chained_modules) { - env.tmp.touch("mod_a.cppm", "export module A;\n" "export int foo() { return 42; }\n"); + env.tmp.touch("mod_a.cppm", + "export module A;\n" + "export int foo() { return 42; }\n"); env.tmp.touch("mod_b.cppm", "export module B;\n" "import A;\n" @@ -206,7 +210,8 @@ TEST_CASE(chained_modules) { TEST_CASE(diamond_modules) { env.tmp.touch("mod_base.cppm", - "export module Base;\n" "export int base_val() { return 10; }\n"); + "export module Base;\n" + "export int base_val() { return 10; }\n"); env.tmp.touch("mod_left.cppm", "export module Left;\n" "import Base;\n" @@ -246,7 +251,9 @@ TEST_CASE(diamond_modules) { /// ============================================================================ TEST_CASE(dotted_module_name) { - env.tmp.touch("io.cppm", "export module my.io;\n" "export void print() {}\n"); + env.tmp.touch("io.cppm", + "export module my.io;\n" + "export void print() {}\n"); env.tmp.touch("app.cppm", "export module my.app;\n" "import my.io;\n" @@ -275,7 +282,9 @@ TEST_CASE(dotted_module_name) { /// ============================================================================ TEST_CASE(re_export) { - env.tmp.touch("core.cppm", "export module Core;\n" "export int core_fn() { return 1; }\n"); + env.tmp.touch("core.cppm", + "export module Core;\n" + "export int core_fn() { return 1; }\n"); env.tmp.touch("wrapper.cppm", "export module Wrapper;\n" "export import Core;\n" @@ -350,7 +359,8 @@ TEST_CASE(global_module_fragment) { env.tmp.touch("legacy.h", "inline int legacy_fn() { return 99; }\n"); env.tmp.touch("gmf.cppm", "module;\n" - R"(#include "legacy.h")" "\n" + R"(#include "legacy.h")" + "\n" "export module GMF;\n" "export int wrapped() { return legacy_fn(); }\n"); @@ -406,7 +416,9 @@ TEST_CASE(private_module_fragment) { TEST_CASE(partition_interface) { // Partition interface unit. - env.tmp.touch("part.cppm", "export module M:Part;\n" "export int part_fn() { return 5; }\n"); + env.tmp.touch("part.cppm", + "export module M:Part;\n" + "export int part_fn() { return 5; }\n"); // Primary module interface re-exports the partition. env.tmp.touch("primary.cppm", "export module M;\n" @@ -439,8 +451,12 @@ TEST_CASE(partition_interface) { /// ============================================================================ TEST_CASE(multiple_partitions) { - env.tmp.touch("part_a.cppm", "export module Lib:A;\n" "export int a_fn() { return 1; }\n"); - env.tmp.touch("part_b.cppm", "export module Lib:B;\n" "export int b_fn() { return 2; }\n"); + env.tmp.touch("part_a.cppm", + "export module Lib:A;\n" + "export int a_fn() { return 1; }\n"); + env.tmp.touch("part_b.cppm", + "export module Lib:B;\n" + "export int b_fn() { return 2; }\n"); env.tmp.touch("lib.cppm", "export module Lib;\n" "export import :A;\n" @@ -473,7 +489,8 @@ TEST_CASE(multiple_partitions) { TEST_CASE(partition_chain) { env.tmp.touch("types.cppm", - "export module Sys:Types;\n" "export struct Config { int value = 0; };\n"); + "export module Sys:Types;\n" + "export struct Config { int value = 0; };\n"); env.tmp.touch("core.cppm", "export module Sys:Core;\n" "import :Types;\n" @@ -543,10 +560,13 @@ TEST_CASE(export_namespace) { TEST_CASE(gmf_with_import) { env.tmp.touch("util.h", "inline int util_helper() { return 7; }\n"); - env.tmp.touch("base.cppm", "export module Base;\n" "export int base() { return 100; }\n"); + env.tmp.touch("base.cppm", + "export module Base;\n" + "export int base() { return 100; }\n"); env.tmp.touch("combined.cppm", "module;\n" - R"(#include "util.h")" "\n" + R"(#include "util.h")" + "\n" "export module Combined;\n" "import Base;\n" "export int combined() { return base() + util_helper(); }\n"); @@ -574,7 +594,9 @@ TEST_CASE(gmf_with_import) { /// ============================================================================ TEST_CASE(deep_chain) { - env.tmp.touch("m1.cppm", "export module M1;\n" "export int f1() { return 1; }\n"); + env.tmp.touch("m1.cppm", + "export module M1;\n" + "export int f1() { return 1; }\n"); env.tmp.touch("m2.cppm", "export module M2;\n" "import M1;\n" @@ -618,8 +640,12 @@ TEST_CASE(deep_chain) { /// ============================================================================ TEST_CASE(independent_modules) { - env.tmp.touch("x.cppm", "export module X;\n" "export int x() { return 1; }\n"); - env.tmp.touch("y.cppm", "export module Y;\n" "export int y() { return 2; }\n"); + env.tmp.touch("x.cppm", + "export module X;\n" + "export int x() { return 1; }\n"); + env.tmp.touch("y.cppm", + "export module Y;\n" + "export int y() { return 2; }\n"); auto json = build_cdb_json({ {env.tmp.root, env.tmp.path("x.cppm"), {}}, @@ -721,7 +747,9 @@ TEST_CASE(class_export_inheritance) { /// ============================================================================ TEST_CASE(recompile_after_update) { - env.tmp.touch("leaf.cppm", "export module Leaf;\n" "export int leaf() { return 1; }\n"); + env.tmp.touch("leaf.cppm", + "export module Leaf;\n" + "export int leaf() { return 1; }\n"); env.tmp.touch("mid.cppm", "export module Mid;\n" "import Leaf;\n" @@ -768,10 +796,13 @@ TEST_CASE(partition_with_gmf) { env.tmp.touch("config.h", "#define MAX_SIZE 100\n"); env.tmp.touch("part_cfg.cppm", "module;\n" - R"(#include "config.h")" "\n" + R"(#include "config.h")" + "\n" "export module Cfg:Limits;\n" "export constexpr int max_size = MAX_SIZE;\n"); - env.tmp.touch("cfg.cppm", "export module Cfg;\n" "export import :Limits;\n"); + env.tmp.touch("cfg.cppm", + "export module Cfg;\n" + "export import :Limits;\n"); auto json = build_cdb_json({ {env.tmp.root, env.tmp.path("part_cfg.cppm"), {"-I", env.tmp.path(".")}}, @@ -797,14 +828,18 @@ TEST_CASE(partition_with_gmf) { TEST_CASE(partition_external_import) { // External module. - env.tmp.touch("ext.cppm", "export module Ext;\n" "export int ext_val() { return 99; }\n"); + env.tmp.touch("ext.cppm", + "export module Ext;\n" + "export int ext_val() { return 99; }\n"); // Partition that imports the external module. env.tmp.touch("part.cppm", "export module App:Core;\n" "import Ext;\n" "export int core_fn() { return ext_val() + 1; }\n"); // Primary module interface. - env.tmp.touch("app.cppm", "export module App;\n" "export import :Core;\n"); + env.tmp.touch("app.cppm", + "export module App;\n" + "export import :Core;\n"); auto json = build_cdb_json({ {env.tmp.root, env.tmp.path("ext.cppm"), {}}, @@ -832,7 +867,8 @@ TEST_CASE(partition_external_import) { TEST_CASE(diamond_update_cascade) { env.tmp.touch("mod_base.cppm", - "export module Base;\n" "export int base_val() { return 10; }\n"); + "export module Base;\n" + "export int base_val() { return 10; }\n"); env.tmp.touch("mod_left.cppm", "export module Left;\n" "import Base;\n" @@ -900,8 +936,12 @@ TEST_CASE(diamond_update_cascade) { TEST_CASE(re_resolve_after_update) { // Start with Mid importing Leaf. - env.tmp.touch("leaf.cppm", "export module Leaf;\n" "export int leaf() { return 1; }\n"); - env.tmp.touch("extra.cppm", "export module Extra;\n" "export int extra() { return 99; }\n"); + env.tmp.touch("leaf.cppm", + "export module Leaf;\n" + "export int leaf() { return 1; }\n"); + env.tmp.touch("extra.cppm", + "export module Extra;\n" + "export int extra() { return 99; }\n"); env.tmp.touch("mid.cppm", "export module Mid;\n" "import Leaf;\n" @@ -952,7 +992,9 @@ TEST_CASE(re_resolve_after_update) { TEST_CASE(compile_failure_propagation) { // Good module. - env.tmp.touch("good.cppm", "export module Good;\n" "export int good() { return 1; }\n"); + env.tmp.touch("good.cppm", + "export module Good;\n" + "export int good() { return 1; }\n"); // Bad module with syntax error. env.tmp.touch("bad.cppm", "export module Bad;\n" @@ -988,10 +1030,14 @@ TEST_CASE(compile_failure_propagation) { TEST_CASE(module_implementation_unit) { // Module interface unit — produces PCM. - env.tmp.touch("iface.cppm", "export module Greeter;\n" "export const char* greet();\n"); + env.tmp.touch("iface.cppm", + "export module Greeter;\n" + "export const char* greet();\n"); // Module implementation unit — consumes PCM, no export. env.tmp.touch("impl.cpp", - "module Greeter;\n" R"(const char* greet() { return "hello"; })" "\n"); + "module Greeter;\n" + R"(const char* greet() { return "hello"; })" + "\n"); auto json = build_cdb_json({ {env.tmp.root, env.tmp.path("iface.cppm"), {}}, @@ -1040,7 +1086,8 @@ TEST_CASE(module_implementation_unit) { TEST_CASE(shared_dep_import_switch) { env.tmp.touch("shared.cppm", - "export module Shared;\n" "export int shared_val() { return 1; }\n"); + "export module Shared;\n" + "export int shared_val() { return 1; }\n"); env.tmp.touch("a.cppm", "export module A;\n" "import Shared;\n" @@ -1139,9 +1186,9 @@ TEST_CASE(shared_dep_import_switch) { /// ============================================================================ TEST_CASE(shared_dep_fails_both) { - env.tmp.touch( - "shared.cppm", - "export module Shared;\n" "export int shared_val() { return UNDEFINED_SYMBOL; }\n"); + env.tmp.touch("shared.cppm", + "export module Shared;\n" + "export int shared_val() { return UNDEFINED_SYMBOL; }\n"); env.tmp.touch("a.cppm", "export module A;\n" "import Shared;\n" diff --git a/tests/unit/server/compiler_tests.cpp b/tests/unit/server/compiler_tests.cpp index 96e85836b..6f0aba524 100644 --- a/tests/unit/server/compiler_tests.cpp +++ b/tests/unit/server/compiler_tests.cpp @@ -304,7 +304,8 @@ TEST_CASE(StopUnblocksCompileWaiters) { auto session = std::make_shared(); session->path_id = workspace.path_pool.intern(src); session->text = - "#include \n#include \n#include \n" "#include \nint main() { return 0; }\n"; + "#include \n#include \n#include \n" + "#include \nint main() { return 0; }\n"; bool waiter_done = false; bool done = false; diff --git a/tests/unit/server/config_tests.cpp b/tests/unit/server/config_tests.cpp index 123cc3b8f..b6dc74a96 100644 --- a/tests/unit/server/config_tests.cpp +++ b/tests/unit/server/config_tests.cpp @@ -33,7 +33,7 @@ TEST_SUITE(Config) { TEST_CASE(ParsePartialProject) { // A partial decode only touches the fields it names; everything else // keeps the field-initializer defaults. - auto result = kota::codec::toml::parse(R"(cache_dir = "/tmp/test")"); + auto result = kota::codec::toml::from_string(R"(cache_dir = "/tmp/test")"); EXPECT_TRUE(result.has_value()); EXPECT_EQ(std::string_view(result->cache_dir), "/tmp/test"); EXPECT_EQ(result->clang_tidy.value, false); @@ -42,7 +42,7 @@ TEST_CASE(ParsePartialProject) { } TEST_CASE(ParseConfigRule) { - auto result = kota::codec::toml::parse(R"( + auto result = kota::codec::toml::from_string(R"( patterns = ["**/*.cpp"] append = ["-std=c++20"] )"); @@ -54,7 +54,7 @@ append = ["-std=c++20"] } TEST_CASE(ParseFullConfig) { - auto result = kota::codec::toml::parse(R"( + auto result = kota::codec::toml::from_string(R"( [project] cache_dir = "/tmp/test" clang_tidy = true @@ -73,7 +73,7 @@ append = ["-std=c++20"] } TEST_CASE(ParseInlayHints) { - auto result = kota::codec::toml::parse(R"( + auto result = kota::codec::toml::from_string(R"( [inlay_hints] block_end = true parameters = false @@ -87,14 +87,14 @@ type_name_limit = 64 } TEST_CASE(ParseEmptyConfig) { - auto result = kota::codec::toml::parse(""); + auto result = kota::codec::toml::from_string(""); EXPECT_TRUE(result.has_value()); EXPECT_TRUE(result->rules.empty()); EXPECT_TRUE(std::string_view(result->project.cache_dir).empty()); } TEST_CASE(ParseOnlyRules) { - auto result = kota::codec::toml::parse(R"( + auto result = kota::codec::toml::from_string(R"( [[rules]] patterns = ["*.h"] remove = ["-Werror"] @@ -649,7 +649,7 @@ append = ["-DFROM_TOML"] EXPECT_EQ(config.compiled_rules.size(), 1u); // Overlay only `idle_timeout_ms` via JSON. - auto ov = kota::codec::json::parse(R"({ "project": { "idle_timeout_ms": 99 } })", config); + auto ov = kota::codec::json::from_string(R"({ "project": { "idle_timeout_ms": 99 } })", config); EXPECT_TRUE(ov.has_value()); config.finalize(tmp.root.str()); @@ -682,7 +682,7 @@ bundle_overloads = false nullptr, nullptr, /*finalized=*/false); - auto ov = kota::codec::json::parse( + auto ov = kota::codec::json::from_string( R"({ "inlay_hints": { "parameters": false, "block_end": false }, "code_completion": { "limit": 5 } })", config); EXPECT_TRUE(ov.has_value()); @@ -713,7 +713,7 @@ append = ["-DTOML_ONLY"] auto config = Config::load_from_workspace(tmp.root.str()); EXPECT_EQ(config.compiled_rules.size(), 1u); - auto ov = kota::codec::json::parse( + auto ov = kota::codec::json::from_string( R"({ "rules": [ { "patterns": ["**/*.cc"], "append": ["-DFROM_JSON"] } ] })", config); EXPECT_TRUE(ov.has_value()); diff --git a/tests/unit/server/module_worker_tests.cpp b/tests/unit/server/module_worker_tests.cpp index 1623652de..71e9c426e 100644 --- a/tests/unit/server/module_worker_tests.cpp +++ b/tests/unit/server/module_worker_tests.cpp @@ -22,11 +22,15 @@ TEST_CASE(BuildPCMThenCompileWithImport) { TempDir tmp; // Module interface: produces PCM. tmp.touch("mod_iface.cppm", - "export module Hello;\n" R"(export const char* hello() { return "world"; })" "\n"); + "export module Hello;\n" + R"(export const char* hello() { return "world"; })" + "\n"); auto iface = tmp.path("mod_iface.cppm"); // Consumer: imports the module. - tmp.touch("consumer.cpp", "import Hello;\n" "int main() { return hello()[0]; }\n"); + tmp.touch("consumer.cpp", + "import Hello;\n" + "int main() { return hello()[0]; }\n"); auto consumer = tmp.path("consumer.cpp"); WorkerHandle sl; @@ -71,7 +75,9 @@ TEST_CASE(BuildPCMThenCompileWithImport) { worker::CompileParams params; params.path = consumer; params.version = 1; - params.text = "import Hello;\n" "int main() { return hello()[0]; }\n"; + params.text = + "import Hello;\n" + "int main() { return hello()[0]; }\n"; params.directory = "/tmp"; params.arguments = {"clang++", "-resource-dir", @@ -101,7 +107,9 @@ TEST_CASE(BuildPCMThenCompileWithImport) { TEST_CASE(BuildPCMChainThenCompile) { TempDir tmp; // Module A: no deps. - tmp.touch("chain_a.cppm", "export module A;\n" "export int val_a() { return 1; }\n"); + tmp.touch("chain_a.cppm", + "export module A;\n" + "export int val_a() { return 1; }\n"); auto mod_a = tmp.path("chain_a.cppm"); // Module B: imports A. tmp.touch("chain_b.cppm", @@ -110,7 +118,9 @@ TEST_CASE(BuildPCMChainThenCompile) { "export int val_b() { return val_a() + 1; }\n"); auto mod_b = tmp.path("chain_b.cppm"); // Consumer: imports B (transitively needs A). - tmp.touch("chain_consumer.cpp", "import B;\n" "int main() { return val_b(); }\n"); + tmp.touch("chain_consumer.cpp", + "import B;\n" + "int main() { return val_b(); }\n"); auto consumer = tmp.path("chain_consumer.cpp"); WorkerHandle sl; @@ -179,7 +189,9 @@ TEST_CASE(BuildPCMChainThenCompile) { worker::CompileParams params; params.path = consumer; params.version = 1; - params.text = "import B;\n" "int main() { return val_b(); }\n"; + params.text = + "import B;\n" + "int main() { return val_b(); }\n"; params.directory = "/tmp"; params.arguments = {"clang++", "-resource-dir", @@ -210,10 +222,14 @@ TEST_CASE(BuildPCMChainThenCompile) { TEST_CASE(ModuleImplementationUnitWithWorker) { TempDir tmp; // Module interface. - tmp.touch("impl_iface.cppm", "export module Calc;\n" "export int add(int a, int b);\n"); + tmp.touch("impl_iface.cppm", + "export module Calc;\n" + "export int add(int a, int b);\n"); auto iface = tmp.path("impl_iface.cppm"); // Module implementation unit (no export). - tmp.touch("impl_unit.cpp", "module Calc;\n" "int add(int a, int b) { return a + b; }\n"); + tmp.touch("impl_unit.cpp", + "module Calc;\n" + "int add(int a, int b) { return a + b; }\n"); auto impl = tmp.path("impl_unit.cpp"); // Build PCM for interface. @@ -257,7 +273,9 @@ TEST_CASE(ModuleImplementationUnitWithWorker) { worker::CompileParams params; params.path = impl; params.version = 1; - params.text = "module Calc;\n" "int add(int a, int b) { return a + b; }\n"; + params.text = + "module Calc;\n" + "int add(int a, int b) { return a + b; }\n"; params.directory = "/tmp"; params.arguments = {"clang++", "-resource-dir", diff --git a/tests/unit/server/pch_worker_tests.cpp b/tests/unit/server/pch_worker_tests.cpp index 8dd5ec364..eb05d3408 100644 --- a/tests/unit/server/pch_worker_tests.cpp +++ b/tests/unit/server/pch_worker_tests.cpp @@ -23,7 +23,9 @@ TEST_SUITE(PCHWorker) { TEST_CASE(BuildPCHThenCompile) { TempDir tmp; - tmp.touch("common.h", R"cpp(struct Point { int x, y; };)cpp" "\n"); + tmp.touch("common.h", + R"cpp(struct Point { int x, y; };)cpp" + "\n"); auto header = tmp.path("common.h"); std::string main_text = "#include \"common.h\"\nPoint p{1,2};\n"; @@ -120,7 +122,9 @@ TEST_CASE(BuildPCHThenCompile) { TEST_CASE(BlobWriteFailure) { TempDir tmp; - tmp.touch("common.h", R"cpp(struct Point { int x, y; };)cpp" "\n"); + tmp.touch("common.h", + R"cpp(struct Point { int x, y; };)cpp" + "\n"); std::string main_text = "#include \"common.h\"\nPoint p{1,2};\n"; tmp.touch("main.cpp", main_text); auto main_file = tmp.path("main.cpp"); @@ -165,7 +169,9 @@ TEST_CASE(BlobWriteFailure) { TEST_CASE(CompileWithoutPCHStillWorks) { TempDir tmp; - tmp.touch("common.h", R"cpp(struct Point { int x, y; };)cpp" "\n"); + tmp.touch("common.h", + R"cpp(struct Point { int x, y; };)cpp" + "\n"); std::string main_text = "#include \"common.h\"\nPoint p{1,2};\n"; tmp.touch("main.cpp", main_text); auto main_file = tmp.path("main.cpp"); diff --git a/tests/unit/server/stateful_worker_tests.cpp b/tests/unit/server/stateful_worker_tests.cpp index 9b0975cbd..fe716e1a2 100644 --- a/tests/unit/server/stateful_worker_tests.cpp +++ b/tests/unit/server/stateful_worker_tests.cpp @@ -454,12 +454,12 @@ TEST_CASE(InlayHintsWithoutCompile) { TEST_CASE(MultipleSequentialRequests) { TempDir tmp; tmp.touch("seq_test.cpp", - "int foo(int x) {\n" - " return x + 1;\n" - "}\n" - "int main() {\n" - " return foo(0);\n" - "}\n"); + "int foo(int x) {\n" + " return x + 1;\n" + "}\n" + "int main() {\n" + " return foo(0);\n" + "}\n"); auto src = tmp.path("seq_test.cpp"); WorkerHandle w; diff --git a/tests/unit/support/markup_tests.cpp b/tests/unit/support/markup_tests.cpp index 4712e1765..e15950d12 100644 --- a/tests/unit/support/markup_tests.cpp +++ b/tests/unit/support/markup_tests.cpp @@ -131,16 +131,28 @@ TEST_CASE(Escaping) { // Code blocks might need more than 3 backticks. Document d; d.add_code_block("foobarbaz `\nqux"); - ASSERT_EQ(d.as_markdown(), "```cpp\n" "foobarbaz `\nqux\n" "```"); + ASSERT_EQ(d.as_markdown(), + "```cpp\n" + "foobarbaz `\nqux\n" + "```"); d = Document(); d.add_code_block("foobarbaz ``\nqux"); - ASSERT_EQ(d.as_markdown(), "```cpp\n" "foobarbaz ``\nqux\n" "```"); + ASSERT_EQ(d.as_markdown(), + "```cpp\n" + "foobarbaz ``\nqux\n" + "```"); d = Document(); d.add_code_block("foobarbaz ```\nqux"); - ASSERT_EQ(d.as_markdown(), "````cpp\n" "foobarbaz ```\nqux\n" "````"); + ASSERT_EQ(d.as_markdown(), + "````cpp\n" + "foobarbaz ```\nqux\n" + "````"); d = Document(); d.add_code_block("foobarbaz ` `` ``` ```` `\nqux"); - ASSERT_EQ(d.as_markdown(), "`````cpp\n" "foobarbaz ` `` ``` ```` `\nqux\n" "`````"); + ASSERT_EQ(d.as_markdown(), + "`````cpp\n" + "foobarbaz ` `` ``` ```` `\nqux\n" + "`````"); } TEST_CASE(ParagraphChunks) { @@ -211,7 +223,12 @@ TEST_CASE(DocumentSeparators) { // Escaped literal: the markdown hard-break " \n" after "foo" is // significant trailing whitespace. - const char* expected_markdown = "foo \n" "```cpp\n" "test\n" "```\n" "bar"; + const char* expected_markdown = + "foo \n" + "```cpp\n" + "test\n" + "```\n" + "bar"; ASSERT_EQ(d.as_markdown(), expected_markdown); const char* expected_text = R"pt(foo @@ -339,13 +356,14 @@ TEST_CASE(BulletListRender) { // Escaped literals: markdown hard-breaks " \n" are significant trailing // whitespace. - const char* expected_markdown = "- foo\n" - "- bar\n" - "- foo \n" - " baz \n" - " - foo \n" - " - baz \n" - " baz"; + const char* expected_markdown = + "- foo\n" + "- bar\n" + "- foo \n" + " baz \n" + " - foo \n" + " - baz \n" + " baz"; ASSERT_EQ(l.as_markdown(), expected_markdown); const char* expected_plain_text = R"pt(- foo - bar @@ -359,15 +377,16 @@ TEST_CASE(BulletListRender) { // Termination inner.add_paragraph().append_text("after"); // Escaped literal: the list-termination line " " is whitespace-only. - expected_markdown = "- foo\n" - "- bar\n" - "- foo \n" - " baz \n" - " - foo \n" - " - baz \n" - " baz\n" - " \n" - " after"; + expected_markdown = + "- foo\n" + "- bar\n" + "- foo \n" + " baz \n" + " - foo \n" + " - baz \n" + " baz\n" + " \n" + " after"; ASSERT_EQ(l.as_markdown(), expected_markdown); expected_plain_text = R"pt(- foo - bar