From bea0fbe0d639db7bb89c03a9d427c801c3e31ffa Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Fri, 26 Jun 2026 07:05:16 +0000 Subject: [PATCH 01/31] extract types from entrypoint --- pp/BUILD | 17 +++++ pp/entrypoint/Makefile | 5 +- pp/entrypoint/go_constants.cpp | 7 +- pp/entrypoint/head_status.cpp | 12 ++-- pp/entrypoint/head_wal.cpp | 30 ++++---- pp/entrypoint/index_writer.cpp | 8 +-- pp/entrypoint/label_set.cpp | 6 +- pp/entrypoint/primitives_lss.cpp | 34 +++++----- pp/entrypoint/prometheus_relabeler.cpp | 68 +++++++++---------- pp/entrypoint/remote_write.cpp | 8 +-- pp/entrypoint/series_data_data_storage.cpp | 40 +++++------ pp/entrypoint/series_data_encoder.cpp | 16 ++--- ...ies_data_serialization_serialized_data.cpp | 20 +++--- pp/entrypoint/wal_decoder.cpp | 28 ++++---- pp/entrypoint/wal_encoder.cpp | 24 +++---- pp/entrypoint/wal_hashdex.cpp | 16 ++--- .../head => entrypoint_types}/data_storage.h | 6 +- .../encoder.h} | 8 ++- .../exception.h} | 6 +- .../go_constants.h | 0 .../hashdex.h} | 7 +- .../series_data => entrypoint_types}/loader.h | 19 ++++-- .../head => entrypoint_types}/lss.h | 13 +++- .../querier.h | 28 +++++--- .../serialized_data.h} | 9 ++- 25 files changed, 243 insertions(+), 192 deletions(-) rename pp/{entrypoint/head => entrypoint_types}/data_storage.h (68%) rename pp/{entrypoint/head/series_data.h => entrypoint_types/encoder.h} (81%) rename pp/{entrypoint/exception.hpp => entrypoint_types/exception.h} (88%) rename pp/{entrypoint => entrypoint_types}/go_constants.h (100%) rename pp/{entrypoint/hashdex.hpp => entrypoint_types/hashdex.h} (81%) rename pp/{entrypoint/series_data => entrypoint_types}/loader.h (69%) rename pp/{entrypoint/head => entrypoint_types}/lss.h (95%) rename pp/{entrypoint/series_data => entrypoint_types}/querier.h (80%) rename pp/{entrypoint/head/serialization.h => entrypoint_types/serialized_data.h} (86%) diff --git a/pp/BUILD b/pp/BUILD index bb57de71dc..d675826452 100644 --- a/pp/BUILD +++ b/pp/BUILD @@ -182,6 +182,22 @@ cc_test( ], ) +cc_library( + name = "entrypoint_types", + hdrs = glob([ + "entrypoint_types/**/*.h", + ]), + linkstatic = True, + deps = [ + ":bare_bones", + ":head", + ":primitives", + ":series_data", + ":series_index", + ":wal", + ], +) + cc_library( name = "entrypoint", srcs = glob( @@ -198,6 +214,7 @@ cc_library( linkstatic = True, deps = [ ":bare_bones", + ":entrypoint_types", ":head", ":metrics", ":primitives", diff --git a/pp/entrypoint/Makefile b/pp/entrypoint/Makefile index 4bd9627cfb..45493b45aa 100644 --- a/pp/entrypoint/Makefile +++ b/pp/entrypoint/Makefile @@ -75,6 +75,9 @@ install: $(result_dir)/$(platform)_entrypoint_init_aio_$(result_suffix).a install: $(prefixed_archives) @install $^ $(install_dir) -$(result_dir)/entrypoint.h: $(headers) entrypoint.h.template ## Concat all headers in one +# Shared type/layout contracts that the Go side reads via cgo (e.g. Sizeof_* macros). +type_headers := ../entrypoint_types/go_constants.h + +$(result_dir)/entrypoint.h: $(type_headers) $(headers) entrypoint.h.template ## Concat all headers in one @mkdir -p ${@D} @cat $^ > $@ diff --git a/pp/entrypoint/go_constants.cpp b/pp/entrypoint/go_constants.cpp index 9b6164e8b3..a9328ab5b7 100644 --- a/pp/entrypoint/go_constants.cpp +++ b/pp/entrypoint/go_constants.cpp @@ -1,6 +1,5 @@ -#include "go_constants.h" - -#include "head/serialization.h" +#include "entrypoint_types/go_constants.h" +#include "entrypoint_types/serialized_data.h" #include "metrics/storage.h" #include "prometheus/relabeler.h" #include "wal/output_decoder.h" @@ -14,7 +13,7 @@ static_assert(sizeof(roaring::Roaring) == Sizeof_RoaringBitset); static_assert(sizeof(PromPP::Prometheus::Relabel::InnerSeries) == Sizeof_InnerSeries); -static_assert(sizeof(entrypoint::head::SerializedDataIterator) == Sizeof_SerializedDataIterator); +static_assert(sizeof(entrypoint_types::SerializedDataIterator) == Sizeof_SerializedDataIterator); static_assert(sizeof(metrics::Storage::Iterator) == Sizeof_MetricsIterator); diff --git a/pp/entrypoint/head_status.cpp b/pp/entrypoint/head_status.cpp index 7f2c452692..223f902ba7 100644 --- a/pp/entrypoint/head_status.cpp +++ b/pp/entrypoint/head_status.cpp @@ -1,12 +1,12 @@ #include "head_status.h" -#include "head/data_storage.h" -#include "head/lss.h" +#include "entrypoint_types/data_storage.h" +#include "entrypoint_types/lss.h" #include "head/status.h" #include "primitives/go_slice.h" -using entrypoint::head::DataStoragePtr; -using entrypoint::head::LssVariantPtr; +using entrypoint_types::DataStoragePtr; +using entrypoint_types::LssVariantPtr; using Status = head::Status; @@ -17,9 +17,9 @@ extern "C" void prompp_get_head_status_lss(void* args, void* res) { }; const auto in = static_cast(args); - const auto& lss = std::get(*in->lss); + const auto& lss = std::get(*in->lss); - head::StatusGetterLSS{lss, in->limit}.get(*static_cast(res)); + head::StatusGetterLSS{lss, in->limit}.get(*static_cast(res)); } extern "C" void prompp_get_head_status_data_storage(void* args, void* res) { diff --git a/pp/entrypoint/head_wal.cpp b/pp/entrypoint/head_wal.cpp index ed15fb41ae..9a0fbb4746 100644 --- a/pp/entrypoint/head_wal.cpp +++ b/pp/entrypoint/head_wal.cpp @@ -2,24 +2,24 @@ #include -#include "exception.hpp" -#include "hashdex.hpp" -#include "head/lss.h" -#include "head/series_data.h" +#include "entrypoint_types/encoder.h" +#include "entrypoint_types/exception.h" +#include "entrypoint_types/hashdex.h" +#include "entrypoint_types/lss.h" #include "primitives/go_slice.h" #include "wal/decoder.h" #include "wal/encoder.h" #include "wal/wal.h" -using Encoder = PromPP::WAL::GenericEncoder>; +using Encoder = PromPP::WAL::GenericEncoder>; using EncoderPtr = std::unique_ptr; -using Decoder = PromPP::WAL::GenericDecoder; +using Decoder = PromPP::WAL::GenericDecoder; using DecoderPtr = std::unique_ptr; static_assert(sizeof(EncoderPtr) == sizeof(void*)); static_assert(sizeof(DecoderPtr) == sizeof(void*)); extern "C" void prompp_head_wal_encoder_ctor(void* args, void* res) { - using entrypoint::head::LssVariantPtr; + using entrypoint_types::LssVariantPtr; struct Arguments { uint16_t shard_id; @@ -32,7 +32,7 @@ extern "C" void prompp_head_wal_encoder_ctor(void* args, void* res) { }; const auto in = static_cast(args); - auto& lss = std::get(*in->lss); + auto& lss = std::get(*in->lss); new (res) Result{.encoder = std::make_unique(lss, in->shard_id, in->log_shards)}; } @@ -86,7 +86,7 @@ extern "C" void prompp_head_wal_encoder_add_inner_series(void* args, void* res) in->encoder->add_inner_series(in->incoming_inner_series, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -110,7 +110,7 @@ extern "C" void prompp_head_wal_encoder_finalize(void* args, void* res) { in->encoder->finalize(out, out_stream); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -129,7 +129,7 @@ extern "C" void prompp_head_wal_encoder_max_written_item_index(void* args, void* } extern "C" void prompp_head_wal_decoder_ctor(void* args, void* res) { - using entrypoint::head::LssVariantPtr; + using entrypoint_types::LssVariantPtr; struct Arguments { LssVariantPtr lss; @@ -141,7 +141,7 @@ extern "C" void prompp_head_wal_decoder_ctor(void* args, void* res) { }; const auto in = static_cast(args); - auto& lss = std::get(*in->lss); + auto& lss = std::get(*in->lss); new (res) Result{.decoder = std::make_unique(lss, in->encoder_version)}; } @@ -182,7 +182,7 @@ extern "C" void prompp_head_wal_decoder_decode(void* args, void* res) { } } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -190,7 +190,7 @@ extern "C" void prompp_head_wal_decoder_decode_to_data_storage(void* args, void* struct Arguments { DecoderPtr decoder; PromPP::Primitives::Go::SliceView segment; - entrypoint::head::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; }; struct Result { @@ -213,6 +213,6 @@ extern "C" void prompp_head_wal_decoder_decode_to_data_storage(void* args, void* out->encode_timestamp = in->decoder->decoder().encoded_at_tsns(); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } diff --git a/pp/entrypoint/index_writer.cpp b/pp/entrypoint/index_writer.cpp index 4da813d6eb..bb41b33db5 100644 --- a/pp/entrypoint/index_writer.cpp +++ b/pp/entrypoint/index_writer.cpp @@ -2,13 +2,13 @@ #include -#include "head/lss.h" +#include "entrypoint_types/lss.h" #include "primitives/go_slice.h" #include "series_index/prometheus/tsdb/index/index_writer.h" using PromPP::Primitives::Go::SliceView; using series_index::prometheus::tsdb::index::ChunkMetadata; -using IndexWriter = series_index::prometheus::tsdb::index::IndexWriter; +using IndexWriter = series_index::prometheus::tsdb::index::IndexWriter; using IndexWriterPtr = std::unique_ptr; static_assert(sizeof(IndexWriterPtr) == sizeof(void*)); @@ -20,14 +20,14 @@ static PromPP::Primitives::Go::BytesStream create_bytes_stream(PromPP::Primitive extern "C" void prompp_index_writer_ctor(void* args, void* res) { struct Arguments { - entrypoint::head::LssVariantPtr lss; + entrypoint_types::LssVariantPtr lss; }; struct Result { IndexWriterPtr writer; }; const auto in = static_cast(args); - new (res) Result{.writer = std::make_unique(std::get(*in->lss))}; + new (res) Result{.writer = std::make_unique(std::get(*in->lss))}; } extern "C" void prompp_index_writer_dtor(void* args) { diff --git a/pp/entrypoint/label_set.cpp b/pp/entrypoint/label_set.cpp index 37c1b3d79d..140b271477 100644 --- a/pp/entrypoint/label_set.cpp +++ b/pp/entrypoint/label_set.cpp @@ -2,12 +2,12 @@ #include "bare_bones/algorithm.h" #include "bare_bones/iterator.h" -#include "entrypoint/head/lss.h" +#include "entrypoint_types/lss.h" #include "primitives/go_model.h" #include "primitives/go_slice.h" -using entrypoint::head::LssVariantPtr; -using entrypoint::head::SnapshotLSSVariantPtr; +using entrypoint_types::LssVariantPtr; +using entrypoint_types::SnapshotLSSVariantPtr; using PromPP::Primitives::Go::Slice; using PromPP::Primitives::Go::SliceView; diff --git a/pp/entrypoint/primitives_lss.cpp b/pp/entrypoint/primitives_lss.cpp index 23754308fa..7c4443764e 100644 --- a/pp/entrypoint/primitives_lss.cpp +++ b/pp/entrypoint/primitives_lss.cpp @@ -4,7 +4,7 @@ #include "bare_bones/bitset.h" #include "bare_bones/vector.h" -#include "head/lss.h" +#include "entrypoint_types/lss.h" #include "primitives/go_model.h" #include "primitives/go_slice.h" #include "series_index/querier/label_names_querier.h" @@ -15,12 +15,12 @@ using GoLabelMatchers = PromPP::Primitives::Go::SliceView>; using GoSliceOfString = PromPP::Primitives::Go::Slice; using GoSliceViewString = PromPP::Primitives::Go::SliceView; -using entrypoint::head::LsIdsSlice; -using entrypoint::head::LsIdsSlicePtr; -using entrypoint::head::LssType; -using entrypoint::head::LssVariantPtr; -using entrypoint::head::QueryableEncodingBimap; -using entrypoint::head::SnapshotLSSVariantPtr; +using entrypoint_types::LsIdsSlice; +using entrypoint_types::LsIdsSlicePtr; +using entrypoint_types::LssType; +using entrypoint_types::LssVariantPtr; +using entrypoint_types::QueryableEncodingBimap; +using entrypoint_types::SnapshotLSSVariantPtr; extern "C" void prompp_primitives_lss_ctor(void* args, void* res) { struct Arguments { @@ -62,7 +62,7 @@ PROMPP_ALWAYS_INLINE FindOrEmplaceResult find_or_emplace(auto& lss, const auto& if constexpr (Lss::kIsReadOnly) { throw BareBones::Exception(0x1b877a0ab46a69a6, "lss is readonly"); } else { - const entrypoint::head::ReallocationsDetector reallocation_detector(lss); + const entrypoint_types::ReallocationsDetector reallocation_detector(lss); const auto ls_id = lss.find_or_emplace(label_set); return {.ls_id = ls_id, .lss_has_reallocations = reallocation_detector.has_reallocations()}; } @@ -95,8 +95,8 @@ extern "C" void prompp_primitives_lss_find_or_emplace_builder(void* args, void* const auto in = static_cast(args); new (res) FindOrEmplaceResult(std::visit( [&builder = in->builder](Lss& lss) { - static const entrypoint::head::SnapshotLSS::value_type empty_label_set; - const auto& label_set = builder.snapshot ? std::get(*builder.snapshot)[builder.ls_id] : empty_label_set; + static const entrypoint_types::SnapshotLSS::value_type empty_label_set; + const auto& label_set = builder.snapshot ? std::get(*builder.snapshot)[builder.ls_id] : empty_label_set; return find_or_emplace(lss, LabelSetBuilder{label_set, builder.sorted_add, builder.sorted_del}); }, @@ -180,8 +180,8 @@ extern "C" void prompp_primitives_group_series_by_label_names(void* args, void* const auto in = static_cast(args); const auto out = new (res) GroupSeriesByLabelNamesResult(); - series_index::querier::group_series_by_label_names( - std::get(*in->snapshot), in->series_ids.span(), in->label_name_ids.span(), out->groups); + series_index::querier::group_series_by_label_names( + std::get(*in->snapshot), in->series_ids.span(), in->label_name_ids.span(), out->groups); } extern "C" void prompp_primitives_group_series_by_label_names_result_free(void* args) { @@ -306,7 +306,7 @@ extern "C" void prompp_create_snapshot_lss(void* args, void* res) { SnapshotLSSVariantPtr snapshot; }; - new (res) Result{.snapshot = entrypoint::head::create_snapshot_lss(*static_cast(args)->lss)}; + new (res) Result{.snapshot = entrypoint_types::create_snapshot_lss(*static_cast(args)->lss)}; } extern "C" void prompp_primitives_snapshot_dtor(void* args) { @@ -343,10 +343,10 @@ extern "C" void prompp_primitives_snapshot_lss_copy_added_series(uint64_t source uint64_t source_bitset, uint64_t destination_lss, uint64_t ids_mapping) { - const auto& src_snapshot_variant = *std::bit_cast(source_snapshot); - const auto& src = std::get(src_snapshot_variant); + const auto& src_snapshot_variant = *std::bit_cast(source_snapshot); + const auto& src = std::get(src_snapshot_variant); const auto& src_bitset = *std::bit_cast(source_bitset); - auto& dst = std::get(*std::bit_cast(destination_lss)); + auto& dst = std::get(*std::bit_cast(destination_lss)); const auto dst_src_ids_mapping = std::bit_cast(ids_mapping); *dst_src_ids_mapping = std::make_unique(); @@ -372,7 +372,7 @@ extern "C" void prompp_primitives_lss_finalize_copy_and_shrink(void* args) { }; const auto* in = static_cast(args); auto& lss = std::get(*in->lss); - auto& resolve_snapshot = std::get(*in->resolve_snapshot); + auto& resolve_snapshot = std::get(*in->resolve_snapshot); lss.finalize_copy_and_shrink(resolve_snapshot, *in->new_to_old_mapping); } diff --git a/pp/entrypoint/prometheus_relabeler.cpp b/pp/entrypoint/prometheus_relabeler.cpp index 2bb0e49482..41865c2db5 100644 --- a/pp/entrypoint/prometheus_relabeler.cpp +++ b/pp/entrypoint/prometheus_relabeler.cpp @@ -1,13 +1,13 @@ #include "prometheus_relabeler.h" -#include "exception.hpp" -#include "hashdex.hpp" -#include "head/lss.h" +#include "entrypoint_types/exception.h" +#include "entrypoint_types/hashdex.h" +#include "entrypoint_types/lss.h" #include "primitives/go_slice.h" #include "prometheus/relabeler.h" -using entrypoint::head::LssVariantPtr; +using entrypoint_types::LssVariantPtr; using PromPP::Primitives::Go::SliceView; using PromPP::Prometheus::Relabel::InnerSeries; using PromPP::Prometheus::Relabel::RelabeledSeries; @@ -39,7 +39,7 @@ extern "C" void prompp_prometheus_stateless_relabeler_ctor(void* args, void* res out->stateless_relabeler = std::make_unique(in->go_rcfgs); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -67,7 +67,7 @@ extern "C" void prompp_prometheus_stateless_relabeler_reset_to(void* args, void* } catch (...) { auto* out = new (res) Result(); auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -196,7 +196,7 @@ extern "C" void prompp_prometheus_per_shard_relabeler_ctor(void* args, void* res out->per_shard_relabeler = std::make_unique(in->external_labels, in->stateless_relabeler, in->number_of_shards, in->shard_id); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -245,7 +245,7 @@ extern "C" void prompp_prometheus_per_shard_single_relabeler_update_relabeler_st } catch (...) { auto* out = new (res) Result(); auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -265,12 +265,12 @@ extern "C" void prompp_prometheus_per_shard_relabeler_output_relabeling(void* ar const auto in = static_cast(args); try { - const auto& lss = std::get(*in->lss); + const auto& lss = std::get(*in->lss); in->per_shard_relabeler->output_relabeling(lss, *in->cache, in->relabeled_series, in->incoming_inner_series, in->encoders_inner_series); } catch (...) { const auto out = new (res) Result(); auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -341,7 +341,7 @@ extern "C" void prompp_prometheus_cache_update(void* args, void* res) { } catch (...) { auto* out = new (res) Result(); auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -401,10 +401,10 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling(void* try { std::visit( [in, out](auto& hashdex) { - auto& input_lss = std::get(*in->input_lss); - auto& target_lss = std::get(*in->target_lss); + auto& input_lss = std::get(*in->input_lss); + auto& target_lss = std::get(*in->target_lss); - const entrypoint::head::ReallocationsDetector reallocation_detector(target_lss); + const entrypoint_types::ReallocationsDetector reallocation_detector(target_lss); in->per_goroutine_relabeler->input_relabeling(input_lss, target_lss, *in->cache, hashdex, in->options, *in->stateless_relabeler, *out, in->shards_inner_series, in->shards_relabeled_series); target_lss.build_deferred_indexes(); @@ -413,7 +413,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling(void* *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -441,8 +441,8 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_from_ try { std::visit( [in, out](auto& hashdex) { - auto& input_lss = std::get(*in->input_lss); - auto& target_lss = std::get(*in->target_lss); + auto& input_lss = std::get(*in->input_lss); + auto& target_lss = std::get(*in->target_lss); out->ok = in->per_goroutine_relabeler->input_relabeling_from_cache(input_lss, target_lss, *in->cache, hashdex, in->options, *out, in->shards_inner_series); @@ -450,7 +450,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_from_ *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -481,10 +481,10 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_with_ try { std::visit( [in, out](auto& hashdex) { - auto& input_lss = std::get(*in->input_lss); - auto& target_lss = std::get(*in->target_lss); + auto& input_lss = std::get(*in->input_lss); + auto& target_lss = std::get(*in->target_lss); - const entrypoint::head::ReallocationsDetector reallocation_detector(target_lss); + const entrypoint_types::ReallocationsDetector reallocation_detector(target_lss); in->per_goroutine_relabeler->input_relabeling_with_stalenans(input_lss, target_lss, *in->cache, hashdex, in->options, *in->stateless_relabeler, *out, in->shards_inner_series, in->shards_relabeled_series, in->def_timestamp); target_lss.build_deferred_indexes(); @@ -493,7 +493,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_with_ *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -522,8 +522,8 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_with_ try { std::visit( [in, out](auto& hashdex) { - auto& input_lss = std::get(*in->input_lss); - auto& target_lss = std::get(*in->target_lss); + auto& input_lss = std::get(*in->input_lss); + auto& target_lss = std::get(*in->target_lss); out->ok = in->per_goroutine_relabeler->input_relabeling_with_stalenans_from_cache(input_lss, target_lss, *in->cache, hashdex, in->options, *out, in->shards_inner_series, in->def_timestamp); @@ -531,7 +531,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_with_ *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -556,9 +556,9 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_transition_relab try { std::visit( [in, out](auto& hashdex) { - auto& target_lss = std::get(*in->target_lss); + auto& target_lss = std::get(*in->target_lss); - const entrypoint::head::ReallocationsDetector reallocation_detector(target_lss); + const entrypoint_types::ReallocationsDetector reallocation_detector(target_lss); in->per_goroutine_relabeler->input_transition_relabeling(target_lss, hashdex, *out, in->shards_inner_series); target_lss.build_deferred_indexes(); out->target_lss_has_reallocations = reallocation_detector.has_reallocations(); @@ -566,7 +566,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_transition_relab *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -591,14 +591,14 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_transition_relab try { std::visit( [in, out](auto& hashdex) { - auto& target_lss = std::get(*in->target_lss); + auto& target_lss = std::get(*in->target_lss); out->ok = in->per_goroutine_relabeler->input_transition_relabeling_only_read(target_lss, hashdex, *out, in->shards_inner_series); }, *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -619,8 +619,8 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_append_relabeler_serie const auto out = new (res) Result(); try { - auto& lss = std::get(*in->target_lss); - const entrypoint::head::ReallocationsDetector reallocation_detector(lss); + auto& lss = std::get(*in->target_lss); + const entrypoint_types::ReallocationsDetector reallocation_detector(lss); for (size_t id = 0; id != in->shards_relabeled_series.size(); ++id) { if (in->shards_relabeled_series[id].size() == 0) { @@ -634,7 +634,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_append_relabeler_serie out->target_lss_has_reallocations = reallocation_detector.has_reallocations(); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -652,7 +652,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_track_stale_nans(void* extern "C" void prompp_remap_stale_nans_state(void* args) { struct Arguments { StaleNaNsStatePtr stale_nans_state; - entrypoint::head::LsIdsSlicePtr dst_src_ls_ids_mapping; + entrypoint_types::LsIdsSlicePtr dst_src_ls_ids_mapping; }; const auto in = static_cast(args); diff --git a/pp/entrypoint/remote_write.cpp b/pp/entrypoint/remote_write.cpp index 7052ff0810..2f58ebdcb4 100644 --- a/pp/entrypoint/remote_write.cpp +++ b/pp/entrypoint/remote_write.cpp @@ -1,4 +1,4 @@ -#include "head/lss.h" +#include "entrypoint_types/lss.h" #include "wal/output_decoder.h" extern "C" void prompp_remote_write_message_list_dtor(void* args) { @@ -35,7 +35,7 @@ extern "C" void prompp_remote_write_message_encoders_dtor(void* args) { extern "C" void prompp_remote_write_encode_message(void* args) { struct Arguments { MessageEncoder* encoder; - PromPP::Primitives::Go::SliceView snapshot_list; + PromPP::Primitives::Go::SliceView snapshot_list; uint64_t message_index; uint64_t messages_count; PromPP::Primitives::Go::SliceView messages; @@ -43,8 +43,8 @@ extern "C" void prompp_remote_write_encode_message(void* args) { const auto in = static_cast(args); - const auto snapshot_getter = [in](uint32_t shard_id) -> const entrypoint::head::SnapshotLSS& { - return std::get(*in->snapshot_list[shard_id]); + const auto snapshot_getter = [in](uint32_t shard_id) -> const entrypoint_types::SnapshotLSS& { + return std::get(*in->snapshot_list[shard_id]); }; in->encoder->encode(snapshot_getter, in->message_index, in->messages_count, in->messages); diff --git a/pp/entrypoint/series_data_data_storage.cpp b/pp/entrypoint/series_data_data_storage.cpp index e46ba77273..2e6cd088b0 100644 --- a/pp/entrypoint/series_data_data_storage.cpp +++ b/pp/entrypoint/series_data_data_storage.cpp @@ -3,24 +3,24 @@ #include #include +#include "entrypoint_types/data_storage.h" +#include "entrypoint_types/loader.h" +#include "entrypoint_types/lss.h" +#include "entrypoint_types/querier.h" +#include "entrypoint_types/serialized_data.h" #include "head/chunk_recoder.h" -#include "head/data_storage.h" -#include "head/lss.h" -#include "head/serialization.h" #include "primitives/go_slice.h" #include "series_data/data_storage.h" #include "series_data/decoder.h" -#include "series_data/loader.h" -#include "series_data/querier.h" #include "series_data/querier/instant_querier.h" #include "series_data/querier/querier.h" #include "series_data/unloading/loader.h" #include "series_data/unloading/unloader.h" #include "series_index/querier/selector_querier.h" -using entrypoint::head::DataStoragePtr; -using entrypoint::head::QueryableEncodingBimap; -using entrypoint::series_data::QueryStatus; +using entrypoint_types::DataStoragePtr; +using entrypoint_types::QueryableEncodingBimap; +using entrypoint_types::QueryStatus; using PromPP::Primitives::LabelSetID; using PromPP::Primitives::Go::BytesStream; using PromPP::Primitives::Go::Slice; @@ -34,15 +34,15 @@ using SerializedChunkRecoder = head::ChunkRecoder; using ChunkRecoderVariantPtr = std::unique_ptr; -using entrypoint::series_data::RevertableLoader; +using entrypoint_types::RevertableLoader; using LoaderVariant = std::variant; using LoaderVariantPtr = std::unique_ptr; static_assert(sizeof(LoaderVariantPtr) == sizeof(void*)); -using entrypoint::series_data::QuerierType; -using entrypoint::series_data::QuerierVariant; -using entrypoint::series_data::QuerierVariantPtr; +using entrypoint_types::QuerierType; +using entrypoint_types::QuerierVariant; +using entrypoint_types::QuerierVariantPtr; extern "C" void prompp_series_data_data_storage_ctor(void* res) { using Result = struct { @@ -117,7 +117,7 @@ extern "C" void prompp_series_data_data_storage_queried_series_set_bitset(void* extern "C" void prompp_series_data_data_storage_query_v2(void* args, void* res) { using Query = series_data::querier::Query>; - using entrypoint::series_data::RangeQuerierWithArgumentsWrapperV2; + using entrypoint_types::RangeQuerierWithArgumentsWrapperV2; using series_data::querier::Querier; struct Arguments { @@ -128,7 +128,7 @@ extern "C" void prompp_series_data_data_storage_query_v2(void* args, void* res) struct Result { QuerierVariantPtr querier{}; QueryStatus status{}; - entrypoint::head::SerializedDataPtr* serialized_data{}; + entrypoint_types::SerializedDataPtr* serialized_data{}; }; const auto in = static_cast(args); @@ -146,7 +146,7 @@ extern "C" void prompp_series_data_data_storage_query_v2(void* args, void* res) } extern "C" void prompp_series_data_data_storage_instant_query(void* args, void* res) { - using entrypoint::series_data::InstantQuerierWithArgumentsWrapperEntrypoint; + using entrypoint_types::InstantQuerierWithArgumentsWrapperEntrypoint; using PromPP::Primitives::Timestamp; using series_data::InstantQuerier; @@ -154,7 +154,7 @@ extern "C" void prompp_series_data_data_storage_instant_query(void* args, void* DataStoragePtr data_storage; SliceView label_set_ids; Timestamp timestamp; - entrypoint::series_data::SampleWithGoLabels* samples; + entrypoint_types::SampleWithGoLabels* samples; }; using Result = struct { @@ -179,7 +179,7 @@ extern "C" void prompp_series_data_data_storage_instant_query(void* args, void* } extern "C" void prompp_series_data_data_storage_query_final(void* args) { - using entrypoint::series_data::QuerierVariantPtr; + using entrypoint_types::QuerierVariantPtr; struct Arguments { Slice queriers; @@ -240,7 +240,7 @@ extern "C" void prompp_series_data_data_storage_dtor(void* args) { extern "C" void prompp_series_data_chunk_recoder_ctor(void* args, void* res) { struct Arguments { - entrypoint::head::LssVariantPtr lss; + entrypoint_types::LssVariantPtr lss; uint32_t ls_id_batch_size; DataStoragePtr data_storage; PromPP::Primitives::TimeInterval time_interval; @@ -261,7 +261,7 @@ extern "C" void prompp_series_data_chunk_recoder_ctor(void* args, void* res) { extern "C" void prompp_series_data_serialized_chunk_recoder_ctor(void* args, void* res) { struct Arguments { - entrypoint::head::SerializedDataPtr* serialized_data; + entrypoint_types::SerializedDataPtr* serialized_data; PromPP::Primitives::TimeInterval time_interval; }; struct Result { @@ -408,7 +408,7 @@ extern "C" void prompp_series_data_data_storage_loader_ctor(void* args, void* re extern "C" void prompp_series_data_data_storage_revertable_loader_ctor(void* args, void* res) { struct Arguments { - entrypoint::head::LssVariantPtr lss; + entrypoint_types::LssVariantPtr lss; uint32_t ls_id_batch_size; DataStoragePtr data_storage; }; diff --git a/pp/entrypoint/series_data_encoder.cpp b/pp/entrypoint/series_data_encoder.cpp index 6b369ee04a..730f6e9214 100644 --- a/pp/entrypoint/series_data_encoder.cpp +++ b/pp/entrypoint/series_data_encoder.cpp @@ -1,6 +1,6 @@ #include "series_data_encoder.h" -#include "head/series_data.h" +#include "entrypoint_types/encoder.h" #include "primitives/primitives.h" #include "prometheus/relabeler.h" #include "series_data/data_storage.h" @@ -10,15 +10,15 @@ extern "C" void prompp_series_data_encoder_ctor(void* args, void* res) { series_data::DataStorage* data_storage; }; using Result = struct { - entrypoint::head::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; }; - new (res) Result{.encoder_wrapper = std::make_unique(*static_cast(args)->data_storage)}; + new (res) Result{.encoder_wrapper = std::make_unique(*static_cast(args)->data_storage)}; } extern "C" void prompp_series_data_encoder_encode(void* args) { struct Arguments { - entrypoint::head::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; uint32_t series_id; int64_t timestamp; double value; @@ -32,7 +32,7 @@ extern "C" void prompp_series_data_encoder_encode(void* args) { extern "C" void prompp_series_data_encoder_encode_inner_series_slice(void* args) { struct Arguments { - entrypoint::head::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; PromPP::Primitives::Go::SliceView inner_series_slice; }; @@ -52,18 +52,18 @@ extern "C" void prompp_series_data_encoder_encode_inner_series_slice(void* args) extern "C" void prompp_series_data_encoder_merge_out_of_order_chunks(void* args) { struct Arguments { - entrypoint::head::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; }; auto& encoder = static_cast(args)->encoder_wrapper->encoder; const auto arena_guard = encoder.storage().thread_arena_guard(); - entrypoint::head::OutdatedChunkMerger{encoder}.merge(); + entrypoint_types::OutdatedChunkMerger{encoder}.merge(); } extern "C" void prompp_series_data_encoder_dtor(void* args) { struct Arguments { - entrypoint::head::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; }; static_cast(args)->~Arguments(); diff --git a/pp/entrypoint/series_data_serialization_serialized_data.cpp b/pp/entrypoint/series_data_serialization_serialized_data.cpp index cf42d872ec..41aeb11778 100644 --- a/pp/entrypoint/series_data_serialization_serialized_data.cpp +++ b/pp/entrypoint/series_data_serialization_serialized_data.cpp @@ -1,10 +1,10 @@ #include "series_data_serialization_serialized_data.h" -#include "head/serialization.h" +#include "entrypoint_types/serialized_data.h" extern "C" void prompp_series_data_serialization_serialized_data_next(void* args, void* res) { struct Arguments { - entrypoint::head::SerializedDataPtr serialized_data; + entrypoint_types::SerializedDataPtr serialized_data; }; using Result = struct { @@ -17,26 +17,26 @@ extern "C" void prompp_series_data_serialization_serialized_data_next(void* args extern "C" void prompp_series_data_serialization_serialized_data_iterator_ctor(void* args) { struct Arguments { - entrypoint::head::SerializedDataIterator* iterator; - entrypoint::head::SerializedDataPtr serialized_data; + entrypoint_types::SerializedDataIterator* iterator; + entrypoint_types::SerializedDataPtr serialized_data; uint32_t chunk_ref; }; const auto in = static_cast(args); - new (in->iterator) entrypoint::head::SerializedDataIterator(in->serialized_data->iterator(in->chunk_ref)); + new (in->iterator) entrypoint_types::SerializedDataIterator(in->serialized_data->iterator(in->chunk_ref)); } extern "C" void prompp_series_data_serialization_serialized_data_iterator_next(void* iterator) { using series_data::decoder::DecodeIteratorSentinel; - ++(*static_cast(iterator)); + ++(*static_cast(iterator)); } extern "C" void prompp_series_data_serialization_serialized_data_iterator_seek(void* args) { using series_data::decoder::DecodeIteratorSentinel; struct Arguments { - entrypoint::head::SerializedDataIterator* iterator; + entrypoint_types::SerializedDataIterator* iterator; int64_t target_timestamp; }; @@ -46,8 +46,8 @@ extern "C" void prompp_series_data_serialization_serialized_data_iterator_seek(v extern "C" void prompp_series_data_serialization_serialized_data_iterator_reset(void* args) { struct Arguments { - entrypoint::head::SerializedDataIterator* iterator; - entrypoint::head::SerializedDataPtr serialized_data; + entrypoint_types::SerializedDataIterator* iterator; + entrypoint_types::SerializedDataPtr serialized_data; uint32_t chunk_ref; }; @@ -57,7 +57,7 @@ extern "C" void prompp_series_data_serialization_serialized_data_iterator_reset( extern "C" void prompp_series_data_serialization_serialized_data_dtor(void* args) { struct Arguments { - entrypoint::head::SerializedDataPtr serialized_data; + entrypoint_types::SerializedDataPtr serialized_data; }; static_cast(args)->~Arguments(); diff --git a/pp/entrypoint/wal_decoder.cpp b/pp/entrypoint/wal_decoder.cpp index 0a2f571508..8227fb72ef 100644 --- a/pp/entrypoint/wal_decoder.cpp +++ b/pp/entrypoint/wal_decoder.cpp @@ -1,8 +1,8 @@ #include "wal_decoder.h" -#include "exception.hpp" -#include "hashdex.hpp" -#include "head/lss.h" +#include "entrypoint_types/exception.h" +#include "entrypoint_types/hashdex.h" +#include "entrypoint_types/lss.h" #include "primitives/go_slice.h" #include "primitives/go_slice_protozero.h" #include "wal/decoder.h" @@ -54,7 +54,7 @@ extern "C" void prompp_wal_decoder_decode(void* args, void* res) { in->decoder->decode(in->segment, out->protobuf, *out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -90,7 +90,7 @@ extern "C" void prompp_wal_decoder_decode_to_hashdex(void* args, void* res) { out->replica.reset_to(replica.data(), replica.size()); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -143,7 +143,7 @@ extern "C" void prompp_wal_decoder_decode_to_hashdex_with_metric_injection(void* out->replica.reset_to(replica.data(), replica.size()); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -164,7 +164,7 @@ extern "C" void prompp_wal_decoder_decode_dry(void* args, void* res) { in->decoder->decode_dry(in->segment, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -187,7 +187,7 @@ extern "C" void prompp_wal_decoder_restore_from_stream(void* args, void* res) { in->decoder->restore_from_stream(in->stream, in->segment_id, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -195,9 +195,9 @@ extern "C" void prompp_wal_decoder_restore_from_stream(void* args, void* res) { // OutputDecoder // -using entrypoint::head::LssVariantPtr; +using entrypoint_types::LssVariantPtr; -using OutputDecoder = PromPP::WAL::OutputDecoder; +using OutputDecoder = PromPP::WAL::OutputDecoder; using OutputDecoderPtr = std::unique_ptr; static_assert(sizeof(OutputDecoderPtr) == sizeof(void*)); @@ -263,7 +263,7 @@ extern "C" void prompp_wal_output_decoder_ctor(void* args, void* res) { }; auto* in = static_cast(args); - auto& output_lss = std::get(*in->output_lss); + auto& output_lss = std::get(*in->output_lss); new (res) Result{.decoder = std::make_unique(*in->stateless_relabeler, output_lss, in->external_labels, static_cast(in->encoder_version))}; } @@ -294,7 +294,7 @@ extern "C" void prompp_wal_output_decoder_dump_to(void* args, void* res) { in->decoder->dump_to(bytes_stream); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -316,7 +316,7 @@ extern "C" void prompp_wal_output_decoder_load_from(void* args, void* res) { in->decoder->load_from(bytes_stream); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -366,6 +366,6 @@ extern "C" void prompp_wal_output_decoder_decode(void* args, void* res) { out->sample_count = in->samples_storage->samples_count() - prev_sample_count; } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } diff --git a/pp/entrypoint/wal_encoder.cpp b/pp/entrypoint/wal_encoder.cpp index 498da443ca..7d17a76e6a 100644 --- a/pp/entrypoint/wal_encoder.cpp +++ b/pp/entrypoint/wal_encoder.cpp @@ -2,8 +2,8 @@ #include -#include "exception.hpp" -#include "hashdex.hpp" +#include "entrypoint_types/exception.h" +#include "entrypoint_types/hashdex.h" #include "primitives/go_slice.h" #include "wal/encoder.h" #include "wal/wal.h" @@ -63,7 +63,7 @@ extern "C" void prompp_wal_encoder_add(void* args, void* res) { std::visit(lmb, *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -89,7 +89,7 @@ extern "C" void prompp_wal_encoder_add_inner_series(void* args, void* res) { in->encoder->add_inner_series(in->incoming_inner_series, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -116,7 +116,7 @@ extern "C" void prompp_wal_encoder_add_relabeled_series(void* args, void* res) { in->encoder->add_relabeled_series(in->incoming_relabeled_series, in->relabeler_state_update, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -146,7 +146,7 @@ extern "C" void prompp_wal_encoder_add_with_stale_nans(void* args, void* res) { std::visit(lmb, *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -173,7 +173,7 @@ extern "C" void prompp_wal_encoder_collect_source(void* args, void* res) { in->encoder->collect_source(out, in->stale_ts, in->source_state); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -201,7 +201,7 @@ extern "C" void prompp_wal_encoder_finalize(void* args, void* res) { in->encoder->finalize(out, out_stream); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -273,7 +273,7 @@ extern "C" void prompp_wal_encoder_lightweight_add(void* args, void* res) { std::visit(lmb, *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -316,7 +316,7 @@ extern "C" void prompp_wal_encoder_lightweight_add_inner_series(void* args, void in->encoder->add_inner_series(in->incoming_inner_series, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -361,7 +361,7 @@ extern "C" void prompp_wal_encoder_lightweight_add_relabeled_series(void* args, in->encoder->add_relabeled_series(in->incoming_relabeled_series, in->relabeler_state_update, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -405,6 +405,6 @@ extern "C" void prompp_wal_encoder_lightweight_finalize(void* args, void* res) { in->encoder->finalize(out, out_stream); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } diff --git a/pp/entrypoint/wal_hashdex.cpp b/pp/entrypoint/wal_hashdex.cpp index 6e4634d01a..8eb3500e6a 100644 --- a/pp/entrypoint/wal_hashdex.cpp +++ b/pp/entrypoint/wal_hashdex.cpp @@ -1,8 +1,8 @@ #include "wal_hashdex.h" -#include "entrypoint/head/data_storage.h" -#include "exception.hpp" -#include "hashdex.hpp" +#include "entrypoint_types/data_storage.h" +#include "entrypoint_types/exception.h" +#include "entrypoint_types/hashdex.h" #include "primitives/go_slice.h" #include "wal/decoder.h" @@ -89,7 +89,7 @@ extern "C" void prompp_wal_protobuf_hashdex_snappy_presharding(void* args, void* out->replica.reset_to(replica.data(), replica.size()); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -136,7 +136,7 @@ extern "C" void prompp_wal_go_model_hashdex_presharding(void* args, void* res) { out->replica.reset_to(replica.data(), replica.size()); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint::handle_current_exception(err_stream); + entrypoint_types::handle_current_exception(err_stream); } } @@ -206,10 +206,10 @@ extern "C" void prompp_wal_go_head_hashdex_ctor(void* res) { extern "C" void prompp_wal_go_head_hashdex_presharding(void* args) { struct Arguments { HashdexVariant* hashdex; - entrypoint::head::LssVariantPtr lss; - entrypoint::head::DataStoragePtr data_storage; + entrypoint_types::LssVariantPtr lss; + entrypoint_types::DataStoragePtr data_storage; }; const auto in = static_cast(args); - std::get(*in->hashdex).presharding(&std::get(*in->lss), in->data_storage.get()); + std::get(*in->hashdex).presharding(&std::get(*in->lss), in->data_storage.get()); } diff --git a/pp/entrypoint/head/data_storage.h b/pp/entrypoint_types/data_storage.h similarity index 68% rename from pp/entrypoint/head/data_storage.h rename to pp/entrypoint_types/data_storage.h index f42ee0490f..d04af5f878 100644 --- a/pp/entrypoint/head/data_storage.h +++ b/pp/entrypoint_types/data_storage.h @@ -1,11 +1,13 @@ #pragma once +#include + #include "series_data/data_storage.h" -namespace entrypoint::head { +namespace entrypoint_types { using DataStoragePtr = std::unique_ptr; static_assert(sizeof(DataStoragePtr) == sizeof(void*)); -} // namespace entrypoint::head +} // namespace entrypoint_types diff --git a/pp/entrypoint/head/series_data.h b/pp/entrypoint_types/encoder.h similarity index 81% rename from pp/entrypoint/head/series_data.h rename to pp/entrypoint_types/encoder.h index 0b76fc2939..b135f4df98 100644 --- a/pp/entrypoint/head/series_data.h +++ b/pp/entrypoint_types/encoder.h @@ -1,10 +1,12 @@ #pragma once -#include "data_storage.h" +#include + +#include "series_data/data_storage.h" #include "series_data/encoder.h" #include "series_data/outdated_chunk_merger.h" -namespace entrypoint::head { +namespace entrypoint_types { using Encoder = series_data::Encoder<>; using OutdatedChunkMerger = series_data::OutdatedChunkMerger; @@ -19,4 +21,4 @@ using SeriesDataEncoderWrapperPtr = std::unique_ptr; static_assert(sizeof(SeriesDataEncoderWrapperPtr) == sizeof(void*)); -} // namespace entrypoint::head \ No newline at end of file +} // namespace entrypoint_types diff --git a/pp/entrypoint/exception.hpp b/pp/entrypoint_types/exception.h similarity index 88% rename from pp/entrypoint/exception.hpp rename to pp/entrypoint_types/exception.h index 2c52bade15..33085643c3 100644 --- a/pp/entrypoint/exception.hpp +++ b/pp/entrypoint_types/exception.h @@ -1,13 +1,13 @@ #pragma once +#include #include #include -#include #include "bare_bones/exception.h" #include "bare_bones/preprocess.h" -namespace entrypoint { +namespace entrypoint_types { PROMPP_ALWAYS_INLINE void handle_current_exception(std::ostream& out, const std::source_location location = std::source_location::current()) { out << location.function_name() << ": "; @@ -23,4 +23,4 @@ PROMPP_ALWAYS_INLINE void handle_current_exception(std::ostream& out, const std: } } -} // namespace entrypoint +} // namespace entrypoint_types diff --git a/pp/entrypoint/go_constants.h b/pp/entrypoint_types/go_constants.h similarity index 100% rename from pp/entrypoint/go_constants.h rename to pp/entrypoint_types/go_constants.h diff --git a/pp/entrypoint/hashdex.hpp b/pp/entrypoint_types/hashdex.h similarity index 81% rename from pp/entrypoint/hashdex.hpp rename to pp/entrypoint_types/hashdex.h index 0cef4badb4..aafe7e9481 100644 --- a/pp/entrypoint/hashdex.hpp +++ b/pp/entrypoint_types/hashdex.h @@ -1,8 +1,9 @@ #pragma once +#include #include -#include "head/lss.h" +#include "entrypoint_types/lss.h" #include "wal/hashdex/basic_decoder.h" #include "wal/hashdex/go_head.h" #include "wal/hashdex/go_model.h" @@ -12,7 +13,7 @@ /** * used for indexing HashdexVariant. */ -enum HashdexType : uint8_t { +enum HashdexType : uint8_t { kProtobuf = 0, kGoModel, kDecoder, @@ -21,7 +22,7 @@ enum HashdexType : uint8_t { kGoHead, }; -using GoHeadHashdex = PromPP::WAL::hashdex::GoHead; +using GoHeadHashdex = PromPP::WAL::hashdex::GoHead; using HashdexVariant = std::variant +#include + #include "bare_bones/iterator.h" -#include "entrypoint/head/lss.h" +#include "bare_bones/preprocess.h" +#include "entrypoint_types/lss.h" +#include "series_data/data_storage.h" #include "series_data/unloading/loader.h" #include "series_data/unloading/reverter.h" -namespace entrypoint::series_data { +namespace entrypoint_types { class RevertableLoader { public: RevertableLoader(::series_data::DataStorage& storage, - head::QueryableEncodingBimap::LsIdSetIterator ls_id_begin, - head::QueryableEncodingBimap::LsIdSetIterator ls_id_end, + QueryableEncodingBimap::LsIdSetIterator ls_id_begin, + QueryableEncodingBimap::LsIdSetIterator ls_id_end, uint32_t ls_id_batch_size) : loader_(storage), reverter_(storage), iterator_(ls_id_begin, ls_id_batch_size), end_iterator_(ls_id_end) { add_series(); @@ -37,8 +42,8 @@ class RevertableLoader { private: ::series_data::unloading::Loader loader_; ::series_data::unloading::LoadReverter reverter_; - BareBones::iterator::BatchIterator iterator_; - [[no_unique_address]] head::QueryableEncodingBimap::LsIdSetIterator end_iterator_; + BareBones::iterator::BatchIterator iterator_; + [[no_unique_address]] QueryableEncodingBimap::LsIdSetIterator end_iterator_; void add_series() { loader_.add_series_to_load(iterator_, end_iterator_, iterator_.batch_size()); @@ -46,4 +51,4 @@ class RevertableLoader { } }; -} // namespace entrypoint::series_data \ No newline at end of file +} // namespace entrypoint_types diff --git a/pp/entrypoint/head/lss.h b/pp/entrypoint_types/lss.h similarity index 95% rename from pp/entrypoint/head/lss.h rename to pp/entrypoint_types/lss.h index ea20f28528..c7e6012de1 100644 --- a/pp/entrypoint/head/lss.h +++ b/pp/entrypoint_types/lss.h @@ -1,15 +1,22 @@ #pragma once -#include +#include #include +#include #include +#include "bare_bones/allocator.h" +#include "bare_bones/bitset.h" #include "bare_bones/exception.h" +#include "bare_bones/memory.h" +#include "bare_bones/preprocess.h" +#include "bare_bones/vector.h" #include "primitives/primitives.h" #include "primitives/snug_composites.h" #include "series_index/queryable_encoding_bimap.h" +#include "series_index/sorting_index.h" -namespace entrypoint::head { +namespace entrypoint_types { using LsIdsSlice = BareBones::Vector; using LsIdsSlicePtr = std::unique_ptr; @@ -185,4 +192,4 @@ inline SnapshotLSSVariantPtr create_snapshot_lss(LssVariant& lss_variant) { } } -} // namespace entrypoint::head +} // namespace entrypoint_types diff --git a/pp/entrypoint/series_data/querier.h b/pp/entrypoint_types/querier.h similarity index 80% rename from pp/entrypoint/series_data/querier.h rename to pp/entrypoint_types/querier.h index 58d9c6060c..075157eb79 100644 --- a/pp/entrypoint/series_data/querier.h +++ b/pp/entrypoint_types/querier.h @@ -1,14 +1,24 @@ #pragma once +#include +#include +#include +#include +#include + #include "bare_bones/bitset.h" -#include "entrypoint/go_constants.h" +#include "bare_bones/preprocess.h" +#include "entrypoint_types/go_constants.h" +#include "entrypoint_types/serialized_data.h" #include "primitives/go_slice.h" #include "primitives/primitives.h" +#include "series_data/data_storage.h" +#include "series_data/encoder/sample.h" #include "series_data/querier/instant_querier.h" #include "series_data/querier/querier.h" -#include "series_data/serialization/serialized_data.h" +#include "series_data/querier/query.h" -namespace entrypoint::series_data { +namespace entrypoint_types { template concept QuerierInterface = requires(Querier querier) { @@ -64,7 +74,7 @@ class RangeQuerierWithArgumentsWrapperV2 { using BytesStream = PromPP::Primitives::Go::BytesStream; public: - RangeQuerierWithArgumentsWrapperV2(DataStorage& storage, const Query& query, head::SerializedDataPtr* serialized_data) + RangeQuerierWithArgumentsWrapperV2(DataStorage& storage, const Query& query, SerializedDataPtr* serialized_data) : querier_(storage), query_(&query), serialized_data_(serialized_data) {} void query() noexcept { @@ -83,10 +93,10 @@ class RangeQuerierWithArgumentsWrapperV2 { private: ::series_data::querier::Querier querier_; const Query* query_; - head::SerializedDataPtr* serialized_data_; + SerializedDataPtr* serialized_data_; PROMPP_ALWAYS_INLINE void serialize_chunks() const noexcept { - std::construct_at(serialized_data_, std::make_unique(querier_.get_storage(), querier_.chunks())); + std::construct_at(serialized_data_, std::make_unique(querier_.get_storage(), querier_.chunks())); } }; @@ -95,7 +105,7 @@ enum class QuerierType : uint8_t { kInstantQuerier = 0, kRangeQuerier, kRangeQue using QuerierVariant = std::variant; using QuerierVariantPtr = std::unique_ptr; -} // namespace entrypoint::series_data +} // namespace entrypoint_types -static_assert(entrypoint::series_data::QuerierInterface); -static_assert(entrypoint::series_data::QuerierInterface); \ No newline at end of file +static_assert(entrypoint_types::QuerierInterface); +static_assert(entrypoint_types::QuerierInterface); diff --git a/pp/entrypoint/head/serialization.h b/pp/entrypoint_types/serialized_data.h similarity index 86% rename from pp/entrypoint/head/serialization.h rename to pp/entrypoint_types/serialized_data.h index 3168a8ff03..576c146a2d 100644 --- a/pp/entrypoint/head/serialization.h +++ b/pp/entrypoint_types/serialized_data.h @@ -1,8 +1,13 @@ #pragma once +#include +#include + +#include "bare_bones/preprocess.h" +#include "series_data/querier/querier.h" #include "series_data/serialization/serialized_data.h" -namespace entrypoint::head { +namespace entrypoint_types { class SerializedDataGo { public: @@ -25,4 +30,4 @@ using SerializedDataIterator = series_data::serialization::SerializedDataView::S static_assert(sizeof(SerializedDataPtr) == sizeof(void*)); -} // namespace entrypoint::head \ No newline at end of file +} // namespace entrypoint_types From 1a541593d26431a94b7ba838cad7e9cb8c78d56c Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Fri, 26 Jun 2026 07:31:18 +0000 Subject: [PATCH 02/31] format fix --- pp/entrypoint_types/hashdex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pp/entrypoint_types/hashdex.h b/pp/entrypoint_types/hashdex.h index aafe7e9481..30cc641b9d 100644 --- a/pp/entrypoint_types/hashdex.h +++ b/pp/entrypoint_types/hashdex.h @@ -13,7 +13,7 @@ /** * used for indexing HashdexVariant. */ -enum HashdexType : uint8_t { +enum HashdexType : uint8_t { kProtobuf = 0, kGoModel, kDecoder, From 205d1c4c298bbe45f4c34f2c6cfca61d0d4342e0 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Fri, 26 Jun 2026 13:04:05 +0000 Subject: [PATCH 03/31] entrypoint tests --- pp/BUILD | 9 + pp/entrypoint_types/loader_tests.cpp | 82 +++++++ pp/entrypoint_types/lss_tests.cpp | 221 ++++++++++++++++++ pp/entrypoint_types/querier_tests.cpp | 188 +++++++++++++++ pp/entrypoint_types/serialized_data_tests.cpp | 88 +++++++ 5 files changed, 588 insertions(+) create mode 100644 pp/entrypoint_types/loader_tests.cpp create mode 100644 pp/entrypoint_types/lss_tests.cpp create mode 100644 pp/entrypoint_types/querier_tests.cpp create mode 100644 pp/entrypoint_types/serialized_data_tests.cpp diff --git a/pp/BUILD b/pp/BUILD index d675826452..1ae8e34daf 100644 --- a/pp/BUILD +++ b/pp/BUILD @@ -198,6 +198,15 @@ cc_library( ], ) +cc_test( + name = "entrypoint_types_test", + srcs = glob(["entrypoint_types/**/*_tests.cpp"]), + deps = [ + ":entrypoint_types", + "@gtest//:gtest_main", + ], +) + cc_library( name = "entrypoint", srcs = glob( diff --git a/pp/entrypoint_types/loader_tests.cpp b/pp/entrypoint_types/loader_tests.cpp new file mode 100644 index 0000000000..844c791238 --- /dev/null +++ b/pp/entrypoint_types/loader_tests.cpp @@ -0,0 +1,82 @@ +#include + +#include "bare_bones/streams.h" +#include "entrypoint_types/loader.h" +#include "primitives/label_set.h" +#include "series_data/decoder.h" +#include "series_data/encoder.h" +#include "series_data/encoder/sample.h" +#include "series_data/unloading/unloader.h" + +namespace { + +using entrypoint_types::QueryableEncodingBimap; +using PromPP::Primitives::LabelViewSet; +using series_data::DataStorage; +using series_data::Decoder; +using series_data::Encoder; +using series_data::chunk::DataChunk; +using series_data::encoder::SampleList; +using series_data::unloading::Unloader; + +class RevertableLoaderFixture : public testing::Test { + protected: + DataStorage storage_; + Encoder<> encoder_{storage_}; + Unloader unloader_{storage_}; + BareBones::ShrinkedToFitOStringStream stream_; + QueryableEncodingBimap lss_; + + void SetUp() override { + encoder_.encode(0, 1, 1.0); + encoder_.encode(0, 2, 2.0); + encoder_.encode(0, 3, 3.0); + encoder_.encode(0, 4, 4.0); + encoder_.encode(0, 5, 5.0); + + lss_.find_or_emplace(LabelViewSet{{"job", "a"}}); + lss_.build_deferred_indexes(); + } + + [[nodiscard]] auto open_chunk_stream() const { + return storage_.get_asc_integer_stream(storage_.open_chunks[0].encoder.external_index); + } + + [[nodiscard]] SampleList decode_open_chunk() const { return Decoder::decode_chunk(storage_, storage_.open_chunks[0]); } +}; + +TEST_F(RevertableLoaderFixture, LoadFinalizeRestoresUnloadedOpenChunk) { + // Arrange + unloader_.create_snapshot(stream_); + unloader_.unload(); + + // Act + entrypoint_types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 1}; + loader.load_next(stream_.span()); + loader.load_finalize(); + + // Assert + EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}}), decode_open_chunk()); + EXPECT_FALSE(storage_.unloaded_series_bitmap.is_set(0)); +} + +TEST_F(RevertableLoaderFixture, RevertRestoresUnloadedOpenChunk) { + // Arrange + unloader_.create_snapshot(stream_); + unloader_.unload(); + const auto trimmed_stream = open_chunk_stream(); + + entrypoint_types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 1}; + loader.load_next(stream_.span()); + loader.load_finalize(); + + // Act + loader.revert(); + const auto restored_stream = open_chunk_stream(); + + // Assert + EXPECT_EQ(trimmed_stream, restored_stream); + EXPECT_TRUE(storage_.unloaded_series_bitmap.is_set(0)); +} + +} // namespace diff --git a/pp/entrypoint_types/lss_tests.cpp b/pp/entrypoint_types/lss_tests.cpp new file mode 100644 index 0000000000..8ae4c0af09 --- /dev/null +++ b/pp/entrypoint_types/lss_tests.cpp @@ -0,0 +1,221 @@ +#include + +#include +#include +#include + +#include "bare_bones/exception.h" +#include "bare_bones/vector.h" +#include "entrypoint_types/lss.h" +#include "primitives/label_set.h" +#include "series_index/queryable_encoding_bimap.h" + +namespace { + +using entrypoint_types::create_lss; +using entrypoint_types::create_snapshot_lss; +using entrypoint_types::EncodingBimap; +using entrypoint_types::LssType; +using entrypoint_types::QueryableEncodingBimap; +using entrypoint_types::ReallocationsDetector; +using entrypoint_types::ShrinkAwareSnapshotLSS; +using entrypoint_types::SnapshotLSS; +using PromPP::Primitives::LabelViewSet; + +template +using QueryableEncodingBimapCopier = series_index::QueryableEncodingBimapCopier; + +class SnapshotFixture : public testing::Test { + protected: + static constexpr uint32_t kShrinkBoundary = 3U; + + const LabelViewSet ls0_{{"job", "a"}}; + const LabelViewSet ls1_{{"job", "b"}}; + const LabelViewSet ls2_{{"job", "c"}}; + const LabelViewSet ls3_{{"job", "d"}}; + const LabelViewSet ls4_{{"job", "e"}}; + + entrypoint_types::LssVariantPtr create_queryable_lss() const { + auto lss = create_lss(LssType::kQueryableEncodingBimap); + auto& bimap = std::get(*lss); + + bimap.find_or_emplace(ls0_); + bimap.find_or_emplace(ls1_); + bimap.find_or_emplace(ls2_); + bimap.find_or_emplace(ls3_); + bimap.find_or_emplace(ls4_); + + bimap.build_deferred_indexes(); + + return lss; + } + + entrypoint_types::LssVariantPtr create_fixed_lss() const { + auto lss = create_queryable_lss(); + std::get(*lss).set_pending_shrink_boundary(kShrinkBoundary); + + return lss; + } + + entrypoint_types::LssVariantPtr create_shrunk_lss() const { + QueryableEncodingBimap seeded_lss; + seeded_lss.find_or_emplace(ls0_); + seeded_lss.find_or_emplace(ls1_); + seeded_lss.find_or_emplace(ls2_); + seeded_lss.find_or_emplace(ls3_); + seeded_lss.find_or_emplace(ls4_); + + seeded_lss.build_deferred_indexes(); + + auto lss = create_lss(LssType::kQueryableEncodingBimap); + auto& bimap = std::get(*lss); + BareBones::Vector dst_src_ids_mapping; + QueryableEncodingBimapCopier copier(seeded_lss, seeded_lss.sorting_index(), seeded_lss.added_series(), bimap, dst_src_ids_mapping); + copier.copy_added_series_and_build_indexes(); + + std::ignore = bimap.find_or_emplace(ls1_); + std::ignore = bimap.find_or_emplace(ls3_); + std::ignore = bimap.find_or_emplace(ls4_); + bimap.build_deferred_indexes(); + + dst_src_ids_mapping.clear(); + QueryableEncodingBimap lss_copy; + QueryableEncodingBimapCopier shrink_copier(bimap, bimap.sorting_index(), bimap.added_series(), lss_copy, dst_src_ids_mapping); + shrink_copier.copy_added_series_and_build_indexes(); + bimap.set_pending_shrink_boundary(kShrinkBoundary); + const SnapshotLSS resolve_snapshot(lss_copy); + bimap.finalize_copy_and_shrink(resolve_snapshot, dst_src_ids_mapping); + return lss; + } +}; + +TEST(LssTest, CreateLssEncodingBimapSelectsExpectedAlternative) { + // Arrange + + // Act + const auto lss = create_lss(LssType::kEncodingBimap); + + // Assert + EXPECT_TRUE(std::holds_alternative(*lss)); +} + +TEST(LssTest, CreateLssQueryableEncodingBimapSelectsExpectedAlternative) { + // Arrange + + // Act + const auto lss = create_lss(LssType::kQueryableEncodingBimap); + + // Assert + EXPECT_TRUE(std::holds_alternative(*lss)); +} + +TEST(LssTest, CreateLssRejectsUnknownType) { + // Arrange + const auto unknown_type = static_cast(-1); + + // Act + + // Assert + EXPECT_THROW((void)create_lss(unknown_type), BareBones::Exception); +} + +TEST(LssTest, CreateSnapshotFromEncodingBimapProducesPlainSnapshot) { + // Arrange + auto lss = create_lss(LssType::kEncodingBimap); + std::get(*lss).find_or_emplace(LabelViewSet{{"job", "a"}}); + + // Act + const auto snapshot = create_snapshot_lss(*lss); + + // Assert + EXPECT_TRUE(std::holds_alternative(*snapshot)); +} + +TEST_F(SnapshotFixture, SnapshotResolvesNormalQueryableLss) { + // Arrange + auto lss = create_queryable_lss(); + + // Act + const auto snapshot = create_snapshot_lss(*lss); + + // Assert + ASSERT_TRUE(std::holds_alternative(*snapshot)); + EXPECT_EQ(ls0_, std::get(*snapshot)[0]); + EXPECT_EQ(ls4_, std::get(*snapshot)[4]); +} + +TEST_F(SnapshotFixture, SnapshotFromFixedQueryableLssIsShrinkAware) { + // Arrange + auto lss = create_fixed_lss(); + + // Act + const auto snapshot = create_snapshot_lss(*lss); + + // Assert + EXPECT_TRUE(std::holds_alternative(*snapshot)); +} + +TEST_F(SnapshotFixture, ShrinkAwareSnapshotResolvesSurvivingPreBoundarySeries) { + // Arrange + auto lss = create_shrunk_lss(); + + // Act + const auto snapshot = create_snapshot_lss(*lss); + + // Assert + ASSERT_TRUE(std::holds_alternative(*snapshot)); + EXPECT_EQ(ls1_, std::get(*snapshot)[1]); +} + +TEST_F(SnapshotFixture, ShrinkAwareSnapshotHidesDroppedPreBoundarySeries) { + // Arrange + auto lss = create_shrunk_lss(); + + // Act + const auto snapshot = create_snapshot_lss(*lss); + + // Assert + ASSERT_TRUE(std::holds_alternative(*snapshot)); + EXPECT_EQ(0U, std::get(*snapshot)[0].size()); + EXPECT_EQ(0U, std::get(*snapshot)[2].size()); +} + +TEST_F(SnapshotFixture, ShrinkAwareSnapshotResolvesPostBoundarySeries) { + // Arrange + auto lss = create_shrunk_lss(); + + // Act + const auto snapshot = create_snapshot_lss(*lss); + + // Assert + ASSERT_TRUE(std::holds_alternative(*snapshot)); + EXPECT_EQ(ls3_, std::get(*snapshot)[3]); + EXPECT_EQ(ls4_, std::get(*snapshot)[4]); +} + +TEST(LssTest, ReallocationsDetectorReportsReallocOnEmplace) { + // Arrange + QueryableEncodingBimap lss; + ReallocationsDetector detector(lss); + + // Act + lss.find_or_emplace(LabelViewSet{{"job", "a"}}); + + // Assert + EXPECT_TRUE(detector.has_reallocations()); +} + +TEST(LssTest, ReallocationsDetectorStaysQuietWithoutChanges) { + // Arrange + QueryableEncodingBimap lss; + lss.find_or_emplace(LabelViewSet{{"job", "a"}}); + lss.build_deferred_indexes(); + + // Act + ReallocationsDetector detector(lss); + + // Assert + EXPECT_FALSE(detector.has_reallocations()); +} + +} // namespace diff --git a/pp/entrypoint_types/querier_tests.cpp b/pp/entrypoint_types/querier_tests.cpp new file mode 100644 index 0000000000..1da40fc469 --- /dev/null +++ b/pp/entrypoint_types/querier_tests.cpp @@ -0,0 +1,188 @@ +#include + +#include +#include +#include +#include +#include + +#include "bare_bones/streams.h" +#include "entrypoint_types/querier.h" +#include "series_data/decoder.h" +#include "series_data/encoder.h" +#include "series_data/unloading/loader.h" +#include "series_data/unloading/unloader.h" + +namespace { + +using BareBones::Encoding::Gorilla::STALE_NAN; +using PromPP::Primitives::LabelSetID; +using PromPP::Primitives::Go::Slice; +using series_data::DataStorage; +using series_data::Decoder; +using series_data::Encoder; +using series_data::decoder::DecodeIteratorSentinel; +using series_data::encoder::Sample; +using series_data::encoder::SampleList; +using series_data::unloading::Loader; +using series_data::unloading::Unloader; +using InstantQuerierWrapper = entrypoint_types::InstantQuerierWithArgumentsWrapper, std::span>; +using RangeQuery = series_data::querier::Query>; + +class SerializedDataPtrStorage { + public: + ~SerializedDataPtrStorage() { + if (constructed_) { + std::destroy_at(ptr()); + } + } + + [[nodiscard]] entrypoint_types::SerializedDataPtr* ptr() noexcept { return reinterpret_cast(storage_); } + [[nodiscard]] const entrypoint_types::SerializedDataPtr* ptr() const noexcept { + return reinterpret_cast(storage_); + } + [[nodiscard]] const entrypoint_types::SerializedDataGo* get() const noexcept { return ptr()->get(); } + + void mark_constructed() noexcept { constructed_ = true; } + + private: + alignas(entrypoint_types::SerializedDataPtr) std::byte storage_[sizeof(entrypoint_types::SerializedDataPtr)]; + bool constructed_{false}; +}; + +class InstantQuerierWrapperFixture : public testing::Test { + protected: + DataStorage storage_; + Encoder<> encoder_{storage_}; + BareBones::ShrinkedToFitOStringStream unloaded_chunks_; + std::vector label_set_ids_{0}; + std::vector samples_{Sample{.timestamp = -1, .value = STALE_NAN}}; + + void encode_open_chunk() { + encoder_.encode(0, 1, 1.0); + encoder_.encode(0, 2, 2.0); + encoder_.encode(0, 3, 3.0); + encoder_.encode(0, 4, 4.0); + encoder_.encode(0, 5, 5.0); + } + + void unload_open_chunks() { + Unloader unloader{storage_}; + unloader.create_snapshot(unloaded_chunks_); + unloader.unload(); + } + + void load_unloaded_chunks() { + Loader loader{storage_, label_set_ids_, static_cast(label_set_ids_.size())}; + loader.load_next(unloaded_chunks_.span()); + loader.load_finalize(); + } +}; + +TEST_F(InstantQuerierWrapperFixture, QueryReturnsSampleBeforeTimestamp) { + // Arrange + encode_open_chunk(); + std::span samples_view{samples_}; + InstantQuerierWrapper wrapper{storage_, label_set_ids_, 3, samples_view}; + + // Act + wrapper.query(); + + // Assert + EXPECT_EQ((Sample{.timestamp = 3, .value = 3.0}), samples_[0]); + EXPECT_FALSE(wrapper.need_loading()); +} + +TEST_F(InstantQuerierWrapperFixture, QueryKeepsDefaultSampleWhenSeriesHasNoPointBeforeTimestamp) { + // Arrange + encoder_.encode(0, 10, 10.0); + std::span samples_view{samples_}; + InstantQuerierWrapper wrapper{storage_, label_set_ids_, 5, samples_view}; + + // Act + wrapper.query(); + + // Assert + EXPECT_EQ((Sample{.timestamp = -1, .value = STALE_NAN}), samples_[0]); + EXPECT_FALSE(wrapper.need_loading()); +} + +TEST_F(InstantQuerierWrapperFixture, QueryRequestsLoadingForUnloadedSeriesThenFinalizeReturnsSample) { + // Arrange + encode_open_chunk(); + unload_open_chunks(); + + std::span samples_view{samples_}; + InstantQuerierWrapper wrapper{storage_, label_set_ids_, 3, samples_view}; + + // Act + wrapper.query(); + const auto need_loading = wrapper.need_loading(); + const auto series_to_load_0 = wrapper.series_to_load().is_set(0); + load_unloaded_chunks(); + wrapper.query_finalize(); + + // Assert + ASSERT_TRUE(need_loading); + EXPECT_TRUE(series_to_load_0); + EXPECT_EQ((Sample{.timestamp = 3, .value = 3.0}), samples_[0]); +} + +class RangeQuerierWrapperFixture : public testing::Test { + protected: + DataStorage storage_; + Encoder<> encoder_{storage_}; + SerializedDataPtrStorage serialized_data_; + + RangeQuery query_for(LabelSetID label_set_id, int64_t min, int64_t max) { + Slice label_set_ids; + label_set_ids.push_back(label_set_id); + return RangeQuery{.time_interval{.min = min, .max = max}, .label_set_ids = std::move(label_set_ids)}; + } + + [[nodiscard]] SampleList decode_chunk(uint32_t chunk_id) const { + SampleList decoded; + std::ranges::copy((*serialized_data_.ptr())->iterator(chunk_id), DecodeIteratorSentinel{}, std::back_inserter(decoded)); + return decoded; + } +}; + +TEST_F(RangeQuerierWrapperFixture, QuerySerializesMatchingOpenChunk) { + // Arrange + encoder_.encode(0, 1, 1.0); + encoder_.encode(0, 2, 2.0); + encoder_.encode(0, 3, 3.0); + encoder_.encode(0, 4, 4.0); + encoder_.encode(0, 5, 5.0); + auto query = query_for(0, 2, 4); + entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_.ptr()}; + + // Act + wrapper.query(); + serialized_data_.mark_constructed(); + const auto decoded = decode_chunk(0); + + // Assert + ASSERT_FALSE(wrapper.need_loading()); + ASSERT_NE(nullptr, serialized_data_.get()); + ASSERT_EQ(1U, serialized_data_.get()->get_chunks_view().size()); + EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}}), decoded); +} + +TEST_F(RangeQuerierWrapperFixture, QuerySerializesEmptyResultWhenSeriesDoesNotMatchInterval) { + // Arrange + encoder_.encode(0, 10, 10.0); + auto query = query_for(0, 1, 5); + entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_.ptr()}; + + // Act + wrapper.query(); + serialized_data_.mark_constructed(); + + // Assert + ASSERT_FALSE(wrapper.need_loading()); + ASSERT_NE(nullptr, serialized_data_.get()); + EXPECT_EQ(0U, serialized_data_.get()->get_chunks_view().size()); +} + +} // namespace diff --git a/pp/entrypoint_types/serialized_data_tests.cpp b/pp/entrypoint_types/serialized_data_tests.cpp new file mode 100644 index 0000000000..5dc2398517 --- /dev/null +++ b/pp/entrypoint_types/serialized_data_tests.cpp @@ -0,0 +1,88 @@ +#include + +#include + +#include "entrypoint_types/serialized_data.h" +#include "series_data/chunk_finalizer.h" +#include "series_data/decoder/traits.h" +#include "series_data/encoder.h" +#include "series_data/encoder/sample.h" +#include "series_data/querier/querier.h" + +namespace { + +using series_data::ChunkFinalizer; +using series_data::DataStorage; +using series_data::Encoder; +using series_data::decoder::DecodeIteratorSentinel; +using series_data::encoder::Sample; +using series_data::encoder::SampleList; +using series_data::querier::Querier; +using Query = series_data::querier::Query>; + +class SerializedDataFixture : public testing::Test { + protected: + DataStorage storage_; + Encoder<> encoder_{storage_}; + Querier querier_{storage_}; + + [[nodiscard]] static SampleList decode_chunk(const entrypoint_types::SerializedDataGo& data, uint32_t chunk_id) { + SampleList decoded; + std::ranges::copy(data.iterator(chunk_id), DecodeIteratorSentinel{}, std::back_inserter(decoded)); + return decoded; + } +}; + +TEST_F(SerializedDataFixture, EmptyQueriedChunkListProducesNoChunks) { + // Arrange + + // Act + entrypoint_types::SerializedDataGo data{storage_, {}}; + + // Assert + EXPECT_EQ(0U, data.get_chunks_view().size()); +} + +TEST_F(SerializedDataFixture, RoundTripsQueriedOpenChunk) { + // Arrange + encoder_.encode(0, 1, 1.0); + encoder_.encode(0, 2, 2.0); + encoder_.encode(0, 3, 3.0); + encoder_.encode(0, 4, 4.0); + encoder_.encode(0, 5, 5.0); + const auto& queried_chunks = querier_.query(Query{.time_interval{.min = 1, .max = 5}, .label_set_ids = {0}}); + + // Act + entrypoint_types::SerializedDataGo data{storage_, queried_chunks}; + const auto next_series = data.next(); + const auto decoded = decode_chunk(data, 0); + + // Assert + ASSERT_EQ(1U, data.get_chunks_view().size()); + EXPECT_EQ(0U, next_series.first); + EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}}), decoded); +} + +TEST_F(SerializedDataFixture, RoundTripsQueriedFinalizedChunk) { + // Arrange + encoder_.encode(0, 1, 1.0); + encoder_.encode(0, 2, 2.0); + encoder_.encode(0, 3, 3.0); + encoder_.encode(0, 4, 4.0); + encoder_.encode(0, 5, 5.0); + ChunkFinalizer::finalize(storage_, 0, storage_.open_chunks[0]); + encoder_.encode(0, 10, 10.0); + const auto& queried_chunks = querier_.query(Query{.time_interval{.min = 1, .max = 5}, .label_set_ids = {0}}); + + // Act + entrypoint_types::SerializedDataGo data{storage_, queried_chunks}; + const auto next_series = data.next(); + const auto decoded = decode_chunk(data, 0); + + // Assert + ASSERT_EQ(1U, data.get_chunks_view().size()); + EXPECT_EQ(0U, next_series.first); + EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}}), decoded); +} + +} // namespace From c0f0c48baacbcf32c51e5443ab0cf2698c16035c Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Mon, 29 Jun 2026 13:25:31 +0000 Subject: [PATCH 04/31] refine tests --- pp/entrypoint/go_constants.cpp | 24 --- pp/entrypoint/series_data_data_storage.cpp | 1 - pp/entrypoint_types/go_constants_tests.cpp | 33 ++++ pp/entrypoint_types/loader_tests.cpp | 94 +++++++++- pp/entrypoint_types/lss_tests.cpp | 100 +++++------ pp/entrypoint_types/querier_tests.cpp | 170 +++++++++++++++--- pp/entrypoint_types/serialized_data_tests.cpp | 40 ++++- 7 files changed, 355 insertions(+), 107 deletions(-) delete mode 100644 pp/entrypoint/go_constants.cpp create mode 100644 pp/entrypoint_types/go_constants_tests.cpp diff --git a/pp/entrypoint/go_constants.cpp b/pp/entrypoint/go_constants.cpp deleted file mode 100644 index a9328ab5b7..0000000000 --- a/pp/entrypoint/go_constants.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "entrypoint_types/go_constants.h" -#include "entrypoint_types/serialized_data.h" -#include "metrics/storage.h" -#include "prometheus/relabeler.h" -#include "wal/output_decoder.h" -#include "wal/segment_samples_storage.h" - -namespace { - -static_assert(sizeof(std::vector) == Sizeof_StdVector); -static_assert(sizeof(BareBones::Vector) == Sizeof_BareBonesVector); -static_assert(sizeof(roaring::Roaring) == Sizeof_RoaringBitset); - -static_assert(sizeof(PromPP::Prometheus::Relabel::InnerSeries) == Sizeof_InnerSeries); - -static_assert(sizeof(entrypoint_types::SerializedDataIterator) == Sizeof_SerializedDataIterator); - -static_assert(sizeof(metrics::Storage::Iterator) == Sizeof_MetricsIterator); - -static_assert(sizeof(PromPP::WAL::SegmentSamplesStorage) == Sizeof_SegmentSamplesStorage); -static_assert(sizeof(PromPP::WAL::ProtobufEncoder) == Sizeof_RemoteWriteMessageEncoder); -static_assert(sizeof(PromPP::WAL::SegmentSamplesStorageList::Iterator) == Sizeof_SegmentSamplesStorageListIterator); - -} // namespace \ No newline at end of file diff --git a/pp/entrypoint/series_data_data_storage.cpp b/pp/entrypoint/series_data_data_storage.cpp index 2e6cd088b0..19f07a9399 100644 --- a/pp/entrypoint/series_data_data_storage.cpp +++ b/pp/entrypoint/series_data_data_storage.cpp @@ -16,7 +16,6 @@ #include "series_data/querier/querier.h" #include "series_data/unloading/loader.h" #include "series_data/unloading/unloader.h" -#include "series_index/querier/selector_querier.h" using entrypoint_types::DataStoragePtr; using entrypoint_types::QueryableEncodingBimap; diff --git a/pp/entrypoint_types/go_constants_tests.cpp b/pp/entrypoint_types/go_constants_tests.cpp new file mode 100644 index 0000000000..0ccf874e20 --- /dev/null +++ b/pp/entrypoint_types/go_constants_tests.cpp @@ -0,0 +1,33 @@ +#include + +#include + +#include "bare_bones/vector.h" +#include "entrypoint_types/go_constants.h" +#include "entrypoint_types/serialized_data.h" +#include "metrics/storage.h" +#include "prometheus/relabeler.h" +#include "wal/output_decoder.h" +#include "wal/segment_samples_storage.h" + +namespace { + +TEST(GoConstantsTest, CompileTimeSizesMatchConstants) { + static_assert(sizeof(std::vector) == Sizeof_StdVector); + static_assert(sizeof(BareBones::Vector) == Sizeof_BareBonesVector); + static_assert(sizeof(roaring::Roaring) == Sizeof_RoaringBitset); + + static_assert(sizeof(PromPP::Prometheus::Relabel::InnerSeries) == Sizeof_InnerSeries); + + static_assert(sizeof(entrypoint_types::SerializedDataIterator) == Sizeof_SerializedDataIterator); + + static_assert(sizeof(metrics::Storage::Iterator) == Sizeof_MetricsIterator); + + static_assert(sizeof(PromPP::WAL::SegmentSamplesStorage) == Sizeof_SegmentSamplesStorage); + static_assert(sizeof(PromPP::WAL::ProtobufEncoder) == Sizeof_RemoteWriteMessageEncoder); + static_assert(sizeof(PromPP::WAL::SegmentSamplesStorageList::Iterator) == Sizeof_SegmentSamplesStorageListIterator); + + SUCCEED(); +} + +} // namespace diff --git a/pp/entrypoint_types/loader_tests.cpp b/pp/entrypoint_types/loader_tests.cpp index 844c791238..75161d0ded 100644 --- a/pp/entrypoint_types/loader_tests.cpp +++ b/pp/entrypoint_types/loader_tests.cpp @@ -38,11 +38,22 @@ class RevertableLoaderFixture : public testing::Test { lss_.build_deferred_indexes(); } - [[nodiscard]] auto open_chunk_stream() const { - return storage_.get_asc_integer_stream(storage_.open_chunks[0].encoder.external_index); + void encode_more(uint32_t ls_id, const LabelViewSet& label_set, const SampleList& samples) { + for (const auto& sample : samples) { + encoder_.encode(ls_id, sample.timestamp, sample.value); + } + + lss_.find_or_emplace(label_set); + lss_.build_deferred_indexes(); + } + + [[nodiscard]] auto open_chunk_stream(uint32_t ls_id) const { + return storage_.get_asc_integer_stream(storage_.open_chunks[ls_id].encoder.external_index); } - [[nodiscard]] SampleList decode_open_chunk() const { return Decoder::decode_chunk(storage_, storage_.open_chunks[0]); } + [[nodiscard]] SampleList decode_open_chunk(uint32_t ls_id) const { + return Decoder::decode_chunk(storage_, storage_.open_chunks[ls_id]); + } }; TEST_F(RevertableLoaderFixture, LoadFinalizeRestoresUnloadedOpenChunk) { @@ -56,7 +67,7 @@ TEST_F(RevertableLoaderFixture, LoadFinalizeRestoresUnloadedOpenChunk) { loader.load_finalize(); // Assert - EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}}), decode_open_chunk()); + EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}}), decode_open_chunk(0)); EXPECT_FALSE(storage_.unloaded_series_bitmap.is_set(0)); } @@ -64,7 +75,7 @@ TEST_F(RevertableLoaderFixture, RevertRestoresUnloadedOpenChunk) { // Arrange unloader_.create_snapshot(stream_); unloader_.unload(); - const auto trimmed_stream = open_chunk_stream(); + const auto trimmed_stream = open_chunk_stream(0); entrypoint_types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 1}; loader.load_next(stream_.span()); @@ -72,11 +83,82 @@ TEST_F(RevertableLoaderFixture, RevertRestoresUnloadedOpenChunk) { // Act loader.revert(); - const auto restored_stream = open_chunk_stream(); + const auto restored_stream = open_chunk_stream(0); // Assert EXPECT_EQ(trimmed_stream, restored_stream); EXPECT_TRUE(storage_.unloaded_series_bitmap.is_set(0)); } +TEST_F(RevertableLoaderFixture, LoadFinalizeLoadsSeriesByBatch) { + // Arrange + encode_more(1, LabelViewSet{{"job", "b"}}, SampleList{{1, 11.0}, {2, 12.0}, {3, 13.0}, {4, 14.0}, {5, 15.0}}); + encode_more(2, LabelViewSet{{"job", "c"}}, SampleList{{1, 21.0}, {2, 22.0}, {3, 23.0}, {4, 24.0}, {5, 25.0}}); + + unloader_.create_snapshot(stream_); + unloader_.unload(); + + entrypoint_types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 2}; + + // Act + loader.load_next(stream_.span()); + loader.load_finalize(); + + const auto has_second_batch = loader.next_batch(); + loader.load_next(stream_.span()); + loader.load_finalize(); + + const auto has_third_batch = loader.next_batch(); + + // Assert + EXPECT_TRUE(has_second_batch); + EXPECT_FALSE(has_third_batch); + + EXPECT_FALSE(storage_.unloaded_series_bitmap.is_set(0)); + EXPECT_FALSE(storage_.unloaded_series_bitmap.is_set(1)); + EXPECT_FALSE(storage_.unloaded_series_bitmap.is_set(2)); + + EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}}), decode_open_chunk(0)); + EXPECT_EQ((SampleList{{1, 11.0}, {2, 12.0}, {3, 13.0}, {4, 14.0}, {5, 15.0}}), decode_open_chunk(1)); + EXPECT_EQ((SampleList{{1, 21.0}, {2, 22.0}, {3, 23.0}, {4, 24.0}, {5, 25.0}}), decode_open_chunk(2)); +} + +TEST_F(RevertableLoaderFixture, RevertRestoresSeriesLoadedAcrossBatches) { + // Arrange + encode_more(1, LabelViewSet{{"job", "b"}}, SampleList{{1, 11.0}, {2, 12.0}, {3, 13.0}, {4, 14.0}, {5, 15.0}}); + encode_more(2, LabelViewSet{{"job", "c"}}, SampleList{{1, 21.0}, {2, 22.0}, {3, 23.0}, {4, 24.0}, {5, 25.0}}); + + unloader_.create_snapshot(stream_); + unloader_.unload(); + const auto trimmed_stream0 = open_chunk_stream(0); + const auto trimmed_stream1 = open_chunk_stream(1); + const auto trimmed_stream2 = open_chunk_stream(2); + + entrypoint_types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 2}; + + loader.load_next(stream_.span()); + loader.load_finalize(); + + const auto has_second_batch = loader.next_batch(); + loader.load_next(stream_.span()); + loader.load_finalize(); + + // Act + loader.revert(); + const auto restored_stream0 = open_chunk_stream(0); + const auto restored_stream1 = open_chunk_stream(1); + const auto restored_stream2 = open_chunk_stream(2); + + // Assert + ASSERT_TRUE(has_second_batch); + + EXPECT_EQ(trimmed_stream0, restored_stream0); + EXPECT_EQ(trimmed_stream1, restored_stream1); + EXPECT_EQ(trimmed_stream2, restored_stream2); + + EXPECT_TRUE(storage_.unloaded_series_bitmap.is_set(0)); + EXPECT_TRUE(storage_.unloaded_series_bitmap.is_set(1)); + EXPECT_TRUE(storage_.unloaded_series_bitmap.is_set(2)); +} + } // namespace diff --git a/pp/entrypoint_types/lss_tests.cpp b/pp/entrypoint_types/lss_tests.cpp index 8ae4c0af09..2a0cb47b98 100644 --- a/pp/entrypoint_types/lss_tests.cpp +++ b/pp/entrypoint_types/lss_tests.cpp @@ -22,10 +22,52 @@ using entrypoint_types::ShrinkAwareSnapshotLSS; using entrypoint_types::SnapshotLSS; using PromPP::Primitives::LabelViewSet; +TEST(LssTest, CreateLssEncodingBimapSelectsExpectedAlternative) { + // Arrange + + // Act + const auto lss = create_lss(LssType::kEncodingBimap); + + // Assert + EXPECT_TRUE(std::holds_alternative(*lss)); +} + +TEST(LssTest, CreateLssQueryableEncodingBimapSelectsExpectedAlternative) { + // Arrange + + // Act + const auto lss = create_lss(LssType::kQueryableEncodingBimap); + + // Assert + EXPECT_TRUE(std::holds_alternative(*lss)); +} + +TEST(LssTest, CreateLssRejectsUnknownType) { + // Arrange + const auto unknown_type = static_cast(-1); + + // Act + + // Assert + EXPECT_THROW((void)create_lss(unknown_type), BareBones::Exception); +} + +TEST(LssTest, CreateSnapshotFromEncodingBimapProducesPlainSnapshot) { + // Arrange + auto lss = create_lss(LssType::kEncodingBimap); + std::get(*lss).find_or_emplace(LabelViewSet{{"job", "a"}}); + + // Act + const auto snapshot = create_snapshot_lss(*lss); + + // Assert + EXPECT_TRUE(std::holds_alternative(*snapshot)); +} + template using QueryableEncodingBimapCopier = series_index::QueryableEncodingBimapCopier; -class SnapshotFixture : public testing::Test { +class SnapshotLssFixture : public testing::Test { protected: static constexpr uint32_t kShrinkBoundary = 3U; @@ -89,49 +131,7 @@ class SnapshotFixture : public testing::Test { } }; -TEST(LssTest, CreateLssEncodingBimapSelectsExpectedAlternative) { - // Arrange - - // Act - const auto lss = create_lss(LssType::kEncodingBimap); - - // Assert - EXPECT_TRUE(std::holds_alternative(*lss)); -} - -TEST(LssTest, CreateLssQueryableEncodingBimapSelectsExpectedAlternative) { - // Arrange - - // Act - const auto lss = create_lss(LssType::kQueryableEncodingBimap); - - // Assert - EXPECT_TRUE(std::holds_alternative(*lss)); -} - -TEST(LssTest, CreateLssRejectsUnknownType) { - // Arrange - const auto unknown_type = static_cast(-1); - - // Act - - // Assert - EXPECT_THROW((void)create_lss(unknown_type), BareBones::Exception); -} - -TEST(LssTest, CreateSnapshotFromEncodingBimapProducesPlainSnapshot) { - // Arrange - auto lss = create_lss(LssType::kEncodingBimap); - std::get(*lss).find_or_emplace(LabelViewSet{{"job", "a"}}); - - // Act - const auto snapshot = create_snapshot_lss(*lss); - - // Assert - EXPECT_TRUE(std::holds_alternative(*snapshot)); -} - -TEST_F(SnapshotFixture, SnapshotResolvesNormalQueryableLss) { +TEST_F(SnapshotLssFixture, ResolvesNormalQueryableLss) { // Arrange auto lss = create_queryable_lss(); @@ -144,7 +144,7 @@ TEST_F(SnapshotFixture, SnapshotResolvesNormalQueryableLss) { EXPECT_EQ(ls4_, std::get(*snapshot)[4]); } -TEST_F(SnapshotFixture, SnapshotFromFixedQueryableLssIsShrinkAware) { +TEST_F(SnapshotLssFixture, FromFixedQueryableLssIsShrinkAware) { // Arrange auto lss = create_fixed_lss(); @@ -155,7 +155,7 @@ TEST_F(SnapshotFixture, SnapshotFromFixedQueryableLssIsShrinkAware) { EXPECT_TRUE(std::holds_alternative(*snapshot)); } -TEST_F(SnapshotFixture, ShrinkAwareSnapshotResolvesSurvivingPreBoundarySeries) { +TEST_F(SnapshotLssFixture, ShrinkAwareResolvesSurvivingPreBoundarySeries) { // Arrange auto lss = create_shrunk_lss(); @@ -167,7 +167,7 @@ TEST_F(SnapshotFixture, ShrinkAwareSnapshotResolvesSurvivingPreBoundarySeries) { EXPECT_EQ(ls1_, std::get(*snapshot)[1]); } -TEST_F(SnapshotFixture, ShrinkAwareSnapshotHidesDroppedPreBoundarySeries) { +TEST_F(SnapshotLssFixture, ShrinkAwareHidesDroppedPreBoundarySeries) { // Arrange auto lss = create_shrunk_lss(); @@ -180,7 +180,7 @@ TEST_F(SnapshotFixture, ShrinkAwareSnapshotHidesDroppedPreBoundarySeries) { EXPECT_EQ(0U, std::get(*snapshot)[2].size()); } -TEST_F(SnapshotFixture, ShrinkAwareSnapshotResolvesPostBoundarySeries) { +TEST_F(SnapshotLssFixture, ShrinkAwareResolvesPostBoundarySeries) { // Arrange auto lss = create_shrunk_lss(); @@ -193,7 +193,7 @@ TEST_F(SnapshotFixture, ShrinkAwareSnapshotResolvesPostBoundarySeries) { EXPECT_EQ(ls4_, std::get(*snapshot)[4]); } -TEST(LssTest, ReallocationsDetectorReportsReallocOnEmplace) { +TEST(ReallocationsDetectorTest, ReportsReallocOnEmplace) { // Arrange QueryableEncodingBimap lss; ReallocationsDetector detector(lss); @@ -205,7 +205,7 @@ TEST(LssTest, ReallocationsDetectorReportsReallocOnEmplace) { EXPECT_TRUE(detector.has_reallocations()); } -TEST(LssTest, ReallocationsDetectorStaysQuietWithoutChanges) { +TEST(ReallocationsDetectorTest, StaysQuietWithoutChanges) { // Arrange QueryableEncodingBimap lss; lss.find_or_emplace(LabelViewSet{{"job", "a"}}); diff --git a/pp/entrypoint_types/querier_tests.cpp b/pp/entrypoint_types/querier_tests.cpp index 1da40fc469..1edf7fff00 100644 --- a/pp/entrypoint_types/querier_tests.cpp +++ b/pp/entrypoint_types/querier_tests.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -29,27 +30,101 @@ using series_data::unloading::Unloader; using InstantQuerierWrapper = entrypoint_types::InstantQuerierWithArgumentsWrapper, std::span>; using RangeQuery = series_data::querier::Query>; -class SerializedDataPtrStorage { +template +class UninitializedMemory { public: - ~SerializedDataPtrStorage() { - if (constructed_) { + UninitializedMemory() { std::ranges::fill(storage_, kDefaultValue); } + + ~UninitializedMemory() { + if (!has_default_value()) { std::destroy_at(ptr()); } } - [[nodiscard]] entrypoint_types::SerializedDataPtr* ptr() noexcept { return reinterpret_cast(storage_); } - [[nodiscard]] const entrypoint_types::SerializedDataPtr* ptr() const noexcept { - return reinterpret_cast(storage_); + [[nodiscard]] T* ptr() noexcept { return reinterpret_cast(storage_); } + [[nodiscard]] const T* ptr() const noexcept { return reinterpret_cast(storage_); } + [[nodiscard]] T& value() noexcept { return *ptr(); } + [[nodiscard]] const T& value() const noexcept { return *ptr(); } + [[nodiscard]] bool has_default_value() const noexcept { + return std::ranges::all_of(storage_, [](std::byte byte) { return byte == kDefaultValue; }); } - [[nodiscard]] const entrypoint_types::SerializedDataGo* get() const noexcept { return ptr()->get(); } - - void mark_constructed() noexcept { constructed_ = true; } private: - alignas(entrypoint_types::SerializedDataPtr) std::byte storage_[sizeof(entrypoint_types::SerializedDataPtr)]; - bool constructed_{false}; + static constexpr auto kDefaultValue = std::byte{0x5a}; + + alignas(T) std::byte storage_[sizeof(T)]; +}; + +class RangeQuerierUninitializedMemoryFixture : public testing::Test { + protected: + DataStorage storage_; + Encoder<> encoder_{storage_}; + BareBones::ShrinkedToFitOStringStream unloaded_chunks_; + UninitializedMemory serialized_data_memory_; + + RangeQuery query_for(LabelSetID label_set_id, int64_t min, int64_t max) { + Slice label_set_ids; + label_set_ids.push_back(label_set_id); + return RangeQuery{.time_interval{.min = min, .max = max}, .label_set_ids = std::move(label_set_ids)}; + } + + [[nodiscard]] entrypoint_types::SerializedDataPtr* serialized_data_ptr() noexcept { return serialized_data_memory_.ptr(); } + + void unload_open_chunks() { + Unloader unloader{storage_}; + unloader.create_snapshot(unloaded_chunks_); + unloader.unload(); + } + + void load_unloaded_chunks(LabelSetID label_set_id) { + std::vector label_set_ids{label_set_id}; + Loader loader{storage_, label_set_ids, static_cast(label_set_ids.size())}; + loader.load_next(unloaded_chunks_.span()); + loader.load_finalize(); + } }; +TEST_F(RangeQuerierUninitializedMemoryFixture, QueryWritesSerializedDataToPreparedMemory) { + // Arrange + encoder_.encode(0, 1, 1.0); + auto query = query_for(0, 1, 1); + const auto was_default_before_prepare = serialized_data_memory_.has_default_value(); + entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; + + // Act + wrapper.query(); + + // Assert + EXPECT_TRUE(was_default_before_prepare); + EXPECT_FALSE(serialized_data_memory_.has_default_value()); + ASSERT_NE(nullptr, serialized_data_memory_.value().get()); +} + +TEST_F(RangeQuerierUninitializedMemoryFixture, QueryFinalizeWritesSerializedDataToPreparedMemory) { + // Arrange + encoder_.encode(0, 1, 1.0); + encoder_.encode(0, 2, 2.0); + encoder_.encode(0, 3, 3.0); + + unload_open_chunks(); + + auto query = query_for(0, 1, 3); + const auto was_default_before_prepare = serialized_data_memory_.has_default_value(); + entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; + + // Act + wrapper.query(); + const auto need_loading = wrapper.need_loading(); + load_unloaded_chunks(0); + wrapper.query_finalize(); + + // Assert + ASSERT_TRUE(need_loading); + EXPECT_TRUE(was_default_before_prepare); + EXPECT_FALSE(serialized_data_memory_.has_default_value()); + ASSERT_NE(nullptr, serialized_data_memory_.value().get()); +} + class InstantQuerierWrapperFixture : public testing::Test { protected: DataStorage storage_; @@ -79,7 +154,7 @@ class InstantQuerierWrapperFixture : public testing::Test { } }; -TEST_F(InstantQuerierWrapperFixture, QueryReturnsSampleBeforeTimestamp) { +TEST_F(InstantQuerierWrapperFixture, QueryReturnsSampleAtTimestamp) { // Arrange encode_open_chunk(); std::span samples_view{samples_}; @@ -132,7 +207,9 @@ class RangeQuerierWrapperFixture : public testing::Test { protected: DataStorage storage_; Encoder<> encoder_{storage_}; - SerializedDataPtrStorage serialized_data_; + BareBones::ShrinkedToFitOStringStream unloaded_chunks_; + UninitializedMemory serialized_data_memory_; + entrypoint_types::SerializedDataPtr serialized_data_; RangeQuery query_for(LabelSetID label_set_id, int64_t min, int64_t max) { Slice label_set_ids; @@ -142,9 +219,26 @@ class RangeQuerierWrapperFixture : public testing::Test { [[nodiscard]] SampleList decode_chunk(uint32_t chunk_id) const { SampleList decoded; - std::ranges::copy((*serialized_data_.ptr())->iterator(chunk_id), DecodeIteratorSentinel{}, std::back_inserter(decoded)); + std::ranges::copy(serialized_data_->iterator(chunk_id), DecodeIteratorSentinel{}, std::back_inserter(decoded)); return decoded; } + + [[nodiscard]] entrypoint_types::SerializedDataPtr* serialized_data_ptr() noexcept { return serialized_data_memory_.ptr(); } + + void take_serialized_data() { serialized_data_ = std::move(serialized_data_memory_.value()); } + + void unload_open_chunks() { + Unloader unloader{storage_}; + unloader.create_snapshot(unloaded_chunks_); + unloader.unload(); + } + + void load_unloaded_chunks(LabelSetID label_set_id) { + std::vector label_set_ids{label_set_id}; + Loader loader{storage_, label_set_ids, static_cast(label_set_ids.size())}; + loader.load_next(unloaded_chunks_.span()); + loader.load_finalize(); + } }; TEST_F(RangeQuerierWrapperFixture, QuerySerializesMatchingOpenChunk) { @@ -154,18 +248,19 @@ TEST_F(RangeQuerierWrapperFixture, QuerySerializesMatchingOpenChunk) { encoder_.encode(0, 3, 3.0); encoder_.encode(0, 4, 4.0); encoder_.encode(0, 5, 5.0); + auto query = query_for(0, 2, 4); - entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_.ptr()}; + entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; // Act wrapper.query(); - serialized_data_.mark_constructed(); + take_serialized_data(); const auto decoded = decode_chunk(0); // Assert ASSERT_FALSE(wrapper.need_loading()); - ASSERT_NE(nullptr, serialized_data_.get()); - ASSERT_EQ(1U, serialized_data_.get()->get_chunks_view().size()); + ASSERT_NE(nullptr, serialized_data_); + ASSERT_EQ(1U, serialized_data_->get_chunks_view().size()); EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}}), decoded); } @@ -173,16 +268,47 @@ TEST_F(RangeQuerierWrapperFixture, QuerySerializesEmptyResultWhenSeriesDoesNotMa // Arrange encoder_.encode(0, 10, 10.0); auto query = query_for(0, 1, 5); - entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_.ptr()}; + entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; // Act wrapper.query(); - serialized_data_.mark_constructed(); + take_serialized_data(); // Assert ASSERT_FALSE(wrapper.need_loading()); - ASSERT_NE(nullptr, serialized_data_.get()); - EXPECT_EQ(0U, serialized_data_.get()->get_chunks_view().size()); + ASSERT_NE(nullptr, serialized_data_); + EXPECT_EQ(0U, serialized_data_->get_chunks_view().size()); +} + +TEST_F(RangeQuerierWrapperFixture, QueryDefersSerializationUntilUnloadedSeriesIsLoaded) { + // Arrange + encoder_.encode(0, 1, 1.0); + encoder_.encode(0, 2, 2.0); + encoder_.encode(0, 3, 3.0); + + unload_open_chunks(); + + auto query = query_for(0, 1, 3); + entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; + + // Act + wrapper.query(); + + const auto need_loading = wrapper.need_loading(); + const auto series_to_load_0 = wrapper.series_to_load().is_set(0); + const auto was_default_before_finalize = serialized_data_memory_.has_default_value(); + + load_unloaded_chunks(0); + wrapper.query_finalize(); + take_serialized_data(); + + // Assert + ASSERT_TRUE(need_loading); + EXPECT_TRUE(series_to_load_0); + EXPECT_TRUE(was_default_before_finalize); + ASSERT_NE(nullptr, serialized_data_); + ASSERT_EQ(1U, serialized_data_->get_chunks_view().size()); + EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}}), decode_chunk(0)); } } // namespace diff --git a/pp/entrypoint_types/serialized_data_tests.cpp b/pp/entrypoint_types/serialized_data_tests.cpp index 5dc2398517..922b07a7f0 100644 --- a/pp/entrypoint_types/serialized_data_tests.cpp +++ b/pp/entrypoint_types/serialized_data_tests.cpp @@ -18,9 +18,10 @@ using series_data::decoder::DecodeIteratorSentinel; using series_data::encoder::Sample; using series_data::encoder::SampleList; using series_data::querier::Querier; +using series_data::serialization::SerializedDataView; using Query = series_data::querier::Query>; -class SerializedDataFixture : public testing::Test { +class SerializedDataGoFixture : public testing::Test { protected: DataStorage storage_; Encoder<> encoder_{storage_}; @@ -33,7 +34,7 @@ class SerializedDataFixture : public testing::Test { } }; -TEST_F(SerializedDataFixture, EmptyQueriedChunkListProducesNoChunks) { +TEST_F(SerializedDataGoFixture, EmptyQueriedChunkListProducesNoChunks) { // Arrange // Act @@ -41,15 +42,17 @@ TEST_F(SerializedDataFixture, EmptyQueriedChunkListProducesNoChunks) { // Assert EXPECT_EQ(0U, data.get_chunks_view().size()); + EXPECT_EQ(SerializedDataView::kNoMoreSeries, data.next().first); } -TEST_F(SerializedDataFixture, RoundTripsQueriedOpenChunk) { +TEST_F(SerializedDataGoFixture, RoundTripsQueriedOpenChunk) { // Arrange encoder_.encode(0, 1, 1.0); encoder_.encode(0, 2, 2.0); encoder_.encode(0, 3, 3.0); encoder_.encode(0, 4, 4.0); encoder_.encode(0, 5, 5.0); + const auto& queried_chunks = querier_.query(Query{.time_interval{.min = 1, .max = 5}, .label_set_ids = {0}}); // Act @@ -63,7 +66,7 @@ TEST_F(SerializedDataFixture, RoundTripsQueriedOpenChunk) { EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}}), decoded); } -TEST_F(SerializedDataFixture, RoundTripsQueriedFinalizedChunk) { +TEST_F(SerializedDataGoFixture, RoundTripsQueriedFinalizedChunk) { // Arrange encoder_.encode(0, 1, 1.0); encoder_.encode(0, 2, 2.0); @@ -71,7 +74,9 @@ TEST_F(SerializedDataFixture, RoundTripsQueriedFinalizedChunk) { encoder_.encode(0, 4, 4.0); encoder_.encode(0, 5, 5.0); ChunkFinalizer::finalize(storage_, 0, storage_.open_chunks[0]); + encoder_.encode(0, 10, 10.0); + const auto& queried_chunks = querier_.query(Query{.time_interval{.min = 1, .max = 5}, .label_set_ids = {0}}); // Act @@ -85,4 +90,31 @@ TEST_F(SerializedDataFixture, RoundTripsQueriedFinalizedChunk) { EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}, {4, 4.0}, {5, 5.0}}), decoded); } +TEST_F(SerializedDataGoFixture, NextReturnsChunkIdsForAllQueriedSeries) { + // Arrange + encoder_.encode(0, 1, 1.0); + encoder_.encode(0, 2, 2.0); + encoder_.encode(0, 3, 3.0); + + encoder_.encode(1, 1, 11.0); + encoder_.encode(1, 2, 12.0); + encoder_.encode(1, 3, 13.0); + + const auto& queried_chunks = querier_.query(Query{.time_interval{.min = 1, .max = 3}, .label_set_ids = {0, 1}}); + + // Act + entrypoint_types::SerializedDataGo data{storage_, queried_chunks}; + const auto series0 = data.next(); + const auto series1 = data.next(); + const auto end = data.next(); + + // Assert + ASSERT_EQ(2U, data.get_chunks_view().size()); + EXPECT_EQ(0U, series0.first); + EXPECT_EQ(1U, series1.first); + EXPECT_EQ(SerializedDataView::kNoMoreSeries, end.first); + EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}}), decode_chunk(data, series0.second)); + EXPECT_EQ((SampleList{{1, 11.0}, {2, 12.0}, {3, 13.0}}), decode_chunk(data, series1.second)); +} + } // namespace From 049edea00ffcb9af33964a2f0544bf0f39a8658a Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Tue, 30 Jun 2026 07:26:40 +0000 Subject: [PATCH 05/31] entrypoint types tests build --- pp/BUILD | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pp/BUILD b/pp/BUILD index 1ae8e34daf..fbe8bba764 100644 --- a/pp/BUILD +++ b/pp/BUILD @@ -202,7 +202,11 @@ cc_test( name = "entrypoint_types_test", srcs = glob(["entrypoint_types/**/*_tests.cpp"]), deps = [ + ":bare_bones", ":entrypoint_types", + ":metrics", + ":prometheus", + ":wal", "@gtest//:gtest_main", ], ) From 2627923167ea51c2a2f4a1dae54c691fda7c2efe Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Tue, 30 Jun 2026 08:08:05 +0000 Subject: [PATCH 06/31] move components to entrypoint --- pp/BUILD | 21 ++++++++----------- pp/entrypoint/Makefile | 6 +++--- pp/entrypoint/{ => bridge}/common.cpp | 0 pp/entrypoint/{ => bridge}/common.h | 0 pp/entrypoint/{ => bridge}/go_constants.cpp | 4 ++-- pp/entrypoint/{ => bridge}/head_status.cpp | 4 ++-- pp/entrypoint/{ => bridge}/head_status.h | 0 pp/entrypoint/{ => bridge}/head_wal.cpp | 8 +++---- pp/entrypoint/{ => bridge}/head_wal.h | 0 pp/entrypoint/{ => bridge}/index_writer.cpp | 2 +- pp/entrypoint/{ => bridge}/index_writer.h | 0 pp/entrypoint/{ => bridge}/label_set.cpp | 2 +- pp/entrypoint/{ => bridge}/label_set.h | 0 pp/entrypoint/{ => bridge}/metrics.cpp | 0 pp/entrypoint/{ => bridge}/metrics.h | 0 pp/entrypoint/{ => bridge}/primitives_lss.cpp | 2 +- pp/entrypoint/{ => bridge}/primitives_lss.h | 0 .../{ => bridge}/prometheus_relabeler.cpp | 6 +++--- .../{ => bridge}/prometheus_relabeler.h | 0 pp/entrypoint/{ => bridge}/remote_write.cpp | 2 +- pp/entrypoint/{ => bridge}/remote_write.h | 0 .../{ => bridge}/series_data_data_storage.cpp | 10 ++++----- .../{ => bridge}/series_data_data_storage.h | 0 .../{ => bridge}/series_data_encoder.cpp | 2 +- .../{ => bridge}/series_data_encoder.h | 0 ...ies_data_serialization_serialized_data.cpp | 2 +- ...eries_data_serialization_serialized_data.h | 0 pp/entrypoint/{ => bridge}/wal_decoder.cpp | 6 +++--- pp/entrypoint/{ => bridge}/wal_decoder.h | 0 pp/entrypoint/{ => bridge}/wal_encoder.cpp | 4 ++-- pp/entrypoint/{ => bridge}/wal_encoder.h | 0 pp/entrypoint/{ => bridge}/wal_hashdex.cpp | 6 +++--- pp/entrypoint/{ => bridge}/wal_hashdex.h | 0 .../types}/data_storage.h | 0 .../types}/encoder.h | 0 .../types}/exception.h | 0 .../types}/go_constants.h | 0 .../types}/hashdex.h | 2 +- .../types}/loader.h | 2 +- .../types}/lss.h | 0 .../types}/querier.h | 4 ++-- .../types}/serialized_data.h | 0 42 files changed, 46 insertions(+), 49 deletions(-) rename pp/entrypoint/{ => bridge}/common.cpp (100%) rename pp/entrypoint/{ => bridge}/common.h (100%) rename pp/entrypoint/{ => bridge}/go_constants.cpp (91%) rename pp/entrypoint/{ => bridge}/head_status.cpp (93%) rename pp/entrypoint/{ => bridge}/head_status.h (100%) rename pp/entrypoint/{ => bridge}/head_wal.cpp (97%) rename pp/entrypoint/{ => bridge}/head_wal.h (100%) rename pp/entrypoint/{ => bridge}/index_writer.cpp (99%) rename pp/entrypoint/{ => bridge}/index_writer.h (100%) rename pp/entrypoint/{ => bridge}/label_set.cpp (99%) rename pp/entrypoint/{ => bridge}/label_set.h (100%) rename pp/entrypoint/{ => bridge}/metrics.cpp (100%) rename pp/entrypoint/{ => bridge}/metrics.h (100%) rename pp/entrypoint/{ => bridge}/primitives_lss.cpp (99%) rename pp/entrypoint/{ => bridge}/primitives_lss.h (100%) rename pp/entrypoint/{ => bridge}/prometheus_relabeler.cpp (99%) rename pp/entrypoint/{ => bridge}/prometheus_relabeler.h (100%) rename pp/entrypoint/{ => bridge}/remote_write.cpp (97%) rename pp/entrypoint/{ => bridge}/remote_write.h (100%) rename pp/entrypoint/{ => bridge}/series_data_data_storage.cpp (98%) rename pp/entrypoint/{ => bridge}/series_data_data_storage.h (100%) rename pp/entrypoint/{ => bridge}/series_data_encoder.cpp (98%) rename pp/entrypoint/{ => bridge}/series_data_encoder.h (100%) rename pp/entrypoint/{ => bridge}/series_data_serialization_serialized_data.cpp (97%) rename pp/entrypoint/{ => bridge}/series_data_serialization_serialized_data.h (100%) rename pp/entrypoint/{ => bridge}/wal_decoder.cpp (99%) rename pp/entrypoint/{ => bridge}/wal_decoder.h (100%) rename pp/entrypoint/{ => bridge}/wal_encoder.cpp (99%) rename pp/entrypoint/{ => bridge}/wal_encoder.h (100%) rename pp/entrypoint/{ => bridge}/wal_hashdex.cpp (98%) rename pp/entrypoint/{ => bridge}/wal_hashdex.h (100%) rename pp/{entrypoint_types => entrypoint/types}/data_storage.h (100%) rename pp/{entrypoint_types => entrypoint/types}/encoder.h (100%) rename pp/{entrypoint_types => entrypoint/types}/exception.h (100%) rename pp/{entrypoint_types => entrypoint/types}/go_constants.h (100%) rename pp/{entrypoint_types => entrypoint/types}/hashdex.h (96%) rename pp/{entrypoint_types => entrypoint/types}/loader.h (98%) rename pp/{entrypoint_types => entrypoint/types}/lss.h (100%) rename pp/{entrypoint_types => entrypoint/types}/querier.h (97%) rename pp/{entrypoint_types => entrypoint/types}/serialized_data.h (100%) diff --git a/pp/BUILD b/pp/BUILD index d675826452..7db1eac543 100644 --- a/pp/BUILD +++ b/pp/BUILD @@ -185,7 +185,7 @@ cc_test( cc_library( name = "entrypoint_types", hdrs = glob([ - "entrypoint_types/**/*.h", + "entrypoint/types/**/*.h", ]), linkstatic = True, deps = [ @@ -199,17 +199,14 @@ cc_library( ) cc_library( - name = "entrypoint", + name = "entrypoint_bridge", srcs = glob( - include = ["entrypoint/**/*.cpp"], - exclude = [ - "entrypoint/init/**", - "entrypoint/**/*_tests.cpp", - ], + include = ["entrypoint/bridge/**/*.cpp"], + exclude = ["entrypoint/bridge/**/*_tests.cpp"], ), hdrs = glob([ - "entrypoint/**/*.h", - "entrypoint/**/*.hpp", + "entrypoint/bridge/**/*.h", + "entrypoint/bridge/**/*.hpp", ]), linkstatic = True, deps = [ @@ -228,7 +225,7 @@ cc_library( cc_static_library( name = "entrypoint_aio", deps = [ - ":entrypoint", + ":entrypoint_bridge", ], ) @@ -236,8 +233,8 @@ cc_library( name = "entrypoint_init", srcs = glob(["entrypoint/init/*.cpp"]), hdrs = glob([ - "entrypoint/init/*.h", - "entrypoint/init/*.hpp", + "entrypoint/init/**/*.h", + "entrypoint/init/**/*.hpp", ]), linkstatic = True, ) diff --git a/pp/entrypoint/Makefile b/pp/entrypoint/Makefile index 45493b45aa..a9fe7745af 100644 --- a/pp/entrypoint/Makefile +++ b/pp/entrypoint/Makefile @@ -8,7 +8,7 @@ else sed := sed endif -# All headers in entrypoint +# All C API headers in entrypoint/bridge # # This headers combined to result header-file. # Symbols from this files used to build runtime entrypoint. @@ -16,7 +16,7 @@ endif # # We separate .h and .hpp files here, because we need _helper.hpp file with no exported symbols, # but with internal re-used code. -headers := $(wildcard *.h) +headers := $(wildcard bridge/*.h) # Platform detected from host # @@ -76,7 +76,7 @@ install: $(prefixed_archives) @install $^ $(install_dir) # Shared type/layout contracts that the Go side reads via cgo (e.g. Sizeof_* macros). -type_headers := ../entrypoint_types/go_constants.h +type_headers := ../entrypoint/types/go_constants.h $(result_dir)/entrypoint.h: $(type_headers) $(headers) entrypoint.h.template ## Concat all headers in one @mkdir -p ${@D} diff --git a/pp/entrypoint/common.cpp b/pp/entrypoint/bridge/common.cpp similarity index 100% rename from pp/entrypoint/common.cpp rename to pp/entrypoint/bridge/common.cpp diff --git a/pp/entrypoint/common.h b/pp/entrypoint/bridge/common.h similarity index 100% rename from pp/entrypoint/common.h rename to pp/entrypoint/bridge/common.h diff --git a/pp/entrypoint/go_constants.cpp b/pp/entrypoint/bridge/go_constants.cpp similarity index 91% rename from pp/entrypoint/go_constants.cpp rename to pp/entrypoint/bridge/go_constants.cpp index a9328ab5b7..fd2ca8da9f 100644 --- a/pp/entrypoint/go_constants.cpp +++ b/pp/entrypoint/bridge/go_constants.cpp @@ -1,5 +1,5 @@ -#include "entrypoint_types/go_constants.h" -#include "entrypoint_types/serialized_data.h" +#include "entrypoint/types/go_constants.h" +#include "entrypoint/types/serialized_data.h" #include "metrics/storage.h" #include "prometheus/relabeler.h" #include "wal/output_decoder.h" diff --git a/pp/entrypoint/head_status.cpp b/pp/entrypoint/bridge/head_status.cpp similarity index 93% rename from pp/entrypoint/head_status.cpp rename to pp/entrypoint/bridge/head_status.cpp index 223f902ba7..4d466397b9 100644 --- a/pp/entrypoint/head_status.cpp +++ b/pp/entrypoint/bridge/head_status.cpp @@ -1,7 +1,7 @@ #include "head_status.h" -#include "entrypoint_types/data_storage.h" -#include "entrypoint_types/lss.h" +#include "entrypoint/types/data_storage.h" +#include "entrypoint/types/lss.h" #include "head/status.h" #include "primitives/go_slice.h" diff --git a/pp/entrypoint/head_status.h b/pp/entrypoint/bridge/head_status.h similarity index 100% rename from pp/entrypoint/head_status.h rename to pp/entrypoint/bridge/head_status.h diff --git a/pp/entrypoint/head_wal.cpp b/pp/entrypoint/bridge/head_wal.cpp similarity index 97% rename from pp/entrypoint/head_wal.cpp rename to pp/entrypoint/bridge/head_wal.cpp index 9a0fbb4746..295030ac6d 100644 --- a/pp/entrypoint/head_wal.cpp +++ b/pp/entrypoint/bridge/head_wal.cpp @@ -2,10 +2,10 @@ #include -#include "entrypoint_types/encoder.h" -#include "entrypoint_types/exception.h" -#include "entrypoint_types/hashdex.h" -#include "entrypoint_types/lss.h" +#include "entrypoint/types/encoder.h" +#include "entrypoint/types/exception.h" +#include "entrypoint/types/hashdex.h" +#include "entrypoint/types/lss.h" #include "primitives/go_slice.h" #include "wal/decoder.h" #include "wal/encoder.h" diff --git a/pp/entrypoint/head_wal.h b/pp/entrypoint/bridge/head_wal.h similarity index 100% rename from pp/entrypoint/head_wal.h rename to pp/entrypoint/bridge/head_wal.h diff --git a/pp/entrypoint/index_writer.cpp b/pp/entrypoint/bridge/index_writer.cpp similarity index 99% rename from pp/entrypoint/index_writer.cpp rename to pp/entrypoint/bridge/index_writer.cpp index 1b8ef633f3..296f7b9dc9 100644 --- a/pp/entrypoint/index_writer.cpp +++ b/pp/entrypoint/bridge/index_writer.cpp @@ -2,7 +2,7 @@ #include -#include "entrypoint_types/lss.h" +#include "entrypoint/types/lss.h" #include "primitives/go_slice.h" #include "series_index/prometheus/tsdb/index/index_writer.h" diff --git a/pp/entrypoint/index_writer.h b/pp/entrypoint/bridge/index_writer.h similarity index 100% rename from pp/entrypoint/index_writer.h rename to pp/entrypoint/bridge/index_writer.h diff --git a/pp/entrypoint/label_set.cpp b/pp/entrypoint/bridge/label_set.cpp similarity index 99% rename from pp/entrypoint/label_set.cpp rename to pp/entrypoint/bridge/label_set.cpp index 140b271477..e62c84e900 100644 --- a/pp/entrypoint/label_set.cpp +++ b/pp/entrypoint/bridge/label_set.cpp @@ -2,7 +2,7 @@ #include "bare_bones/algorithm.h" #include "bare_bones/iterator.h" -#include "entrypoint_types/lss.h" +#include "entrypoint/types/lss.h" #include "primitives/go_model.h" #include "primitives/go_slice.h" diff --git a/pp/entrypoint/label_set.h b/pp/entrypoint/bridge/label_set.h similarity index 100% rename from pp/entrypoint/label_set.h rename to pp/entrypoint/bridge/label_set.h diff --git a/pp/entrypoint/metrics.cpp b/pp/entrypoint/bridge/metrics.cpp similarity index 100% rename from pp/entrypoint/metrics.cpp rename to pp/entrypoint/bridge/metrics.cpp diff --git a/pp/entrypoint/metrics.h b/pp/entrypoint/bridge/metrics.h similarity index 100% rename from pp/entrypoint/metrics.h rename to pp/entrypoint/bridge/metrics.h diff --git a/pp/entrypoint/primitives_lss.cpp b/pp/entrypoint/bridge/primitives_lss.cpp similarity index 99% rename from pp/entrypoint/primitives_lss.cpp rename to pp/entrypoint/bridge/primitives_lss.cpp index 7c4443764e..5205e690d0 100644 --- a/pp/entrypoint/primitives_lss.cpp +++ b/pp/entrypoint/bridge/primitives_lss.cpp @@ -4,7 +4,7 @@ #include "bare_bones/bitset.h" #include "bare_bones/vector.h" -#include "entrypoint_types/lss.h" +#include "entrypoint/types/lss.h" #include "primitives/go_model.h" #include "primitives/go_slice.h" #include "series_index/querier/label_names_querier.h" diff --git a/pp/entrypoint/primitives_lss.h b/pp/entrypoint/bridge/primitives_lss.h similarity index 100% rename from pp/entrypoint/primitives_lss.h rename to pp/entrypoint/bridge/primitives_lss.h diff --git a/pp/entrypoint/prometheus_relabeler.cpp b/pp/entrypoint/bridge/prometheus_relabeler.cpp similarity index 99% rename from pp/entrypoint/prometheus_relabeler.cpp rename to pp/entrypoint/bridge/prometheus_relabeler.cpp index 41865c2db5..668e08d675 100644 --- a/pp/entrypoint/prometheus_relabeler.cpp +++ b/pp/entrypoint/bridge/prometheus_relabeler.cpp @@ -1,8 +1,8 @@ #include "prometheus_relabeler.h" -#include "entrypoint_types/exception.h" -#include "entrypoint_types/hashdex.h" -#include "entrypoint_types/lss.h" +#include "entrypoint/types/exception.h" +#include "entrypoint/types/hashdex.h" +#include "entrypoint/types/lss.h" #include "primitives/go_slice.h" #include "prometheus/relabeler.h" diff --git a/pp/entrypoint/prometheus_relabeler.h b/pp/entrypoint/bridge/prometheus_relabeler.h similarity index 100% rename from pp/entrypoint/prometheus_relabeler.h rename to pp/entrypoint/bridge/prometheus_relabeler.h diff --git a/pp/entrypoint/remote_write.cpp b/pp/entrypoint/bridge/remote_write.cpp similarity index 97% rename from pp/entrypoint/remote_write.cpp rename to pp/entrypoint/bridge/remote_write.cpp index 2f58ebdcb4..c774aaf07d 100644 --- a/pp/entrypoint/remote_write.cpp +++ b/pp/entrypoint/bridge/remote_write.cpp @@ -1,4 +1,4 @@ -#include "entrypoint_types/lss.h" +#include "entrypoint/types/lss.h" #include "wal/output_decoder.h" extern "C" void prompp_remote_write_message_list_dtor(void* args) { diff --git a/pp/entrypoint/remote_write.h b/pp/entrypoint/bridge/remote_write.h similarity index 100% rename from pp/entrypoint/remote_write.h rename to pp/entrypoint/bridge/remote_write.h diff --git a/pp/entrypoint/series_data_data_storage.cpp b/pp/entrypoint/bridge/series_data_data_storage.cpp similarity index 98% rename from pp/entrypoint/series_data_data_storage.cpp rename to pp/entrypoint/bridge/series_data_data_storage.cpp index 2e6cd088b0..d8d88fe49e 100644 --- a/pp/entrypoint/series_data_data_storage.cpp +++ b/pp/entrypoint/bridge/series_data_data_storage.cpp @@ -3,11 +3,11 @@ #include #include -#include "entrypoint_types/data_storage.h" -#include "entrypoint_types/loader.h" -#include "entrypoint_types/lss.h" -#include "entrypoint_types/querier.h" -#include "entrypoint_types/serialized_data.h" +#include "entrypoint/types/data_storage.h" +#include "entrypoint/types/loader.h" +#include "entrypoint/types/lss.h" +#include "entrypoint/types/querier.h" +#include "entrypoint/types/serialized_data.h" #include "head/chunk_recoder.h" #include "primitives/go_slice.h" #include "series_data/data_storage.h" diff --git a/pp/entrypoint/series_data_data_storage.h b/pp/entrypoint/bridge/series_data_data_storage.h similarity index 100% rename from pp/entrypoint/series_data_data_storage.h rename to pp/entrypoint/bridge/series_data_data_storage.h diff --git a/pp/entrypoint/series_data_encoder.cpp b/pp/entrypoint/bridge/series_data_encoder.cpp similarity index 98% rename from pp/entrypoint/series_data_encoder.cpp rename to pp/entrypoint/bridge/series_data_encoder.cpp index 730f6e9214..e7a0c66bad 100644 --- a/pp/entrypoint/series_data_encoder.cpp +++ b/pp/entrypoint/bridge/series_data_encoder.cpp @@ -1,6 +1,6 @@ #include "series_data_encoder.h" -#include "entrypoint_types/encoder.h" +#include "entrypoint/types/encoder.h" #include "primitives/primitives.h" #include "prometheus/relabeler.h" #include "series_data/data_storage.h" diff --git a/pp/entrypoint/series_data_encoder.h b/pp/entrypoint/bridge/series_data_encoder.h similarity index 100% rename from pp/entrypoint/series_data_encoder.h rename to pp/entrypoint/bridge/series_data_encoder.h diff --git a/pp/entrypoint/series_data_serialization_serialized_data.cpp b/pp/entrypoint/bridge/series_data_serialization_serialized_data.cpp similarity index 97% rename from pp/entrypoint/series_data_serialization_serialized_data.cpp rename to pp/entrypoint/bridge/series_data_serialization_serialized_data.cpp index 41aeb11778..81a488ee9b 100644 --- a/pp/entrypoint/series_data_serialization_serialized_data.cpp +++ b/pp/entrypoint/bridge/series_data_serialization_serialized_data.cpp @@ -1,6 +1,6 @@ #include "series_data_serialization_serialized_data.h" -#include "entrypoint_types/serialized_data.h" +#include "entrypoint/types/serialized_data.h" extern "C" void prompp_series_data_serialization_serialized_data_next(void* args, void* res) { struct Arguments { diff --git a/pp/entrypoint/series_data_serialization_serialized_data.h b/pp/entrypoint/bridge/series_data_serialization_serialized_data.h similarity index 100% rename from pp/entrypoint/series_data_serialization_serialized_data.h rename to pp/entrypoint/bridge/series_data_serialization_serialized_data.h diff --git a/pp/entrypoint/wal_decoder.cpp b/pp/entrypoint/bridge/wal_decoder.cpp similarity index 99% rename from pp/entrypoint/wal_decoder.cpp rename to pp/entrypoint/bridge/wal_decoder.cpp index 8227fb72ef..453db90116 100644 --- a/pp/entrypoint/wal_decoder.cpp +++ b/pp/entrypoint/bridge/wal_decoder.cpp @@ -1,8 +1,8 @@ #include "wal_decoder.h" -#include "entrypoint_types/exception.h" -#include "entrypoint_types/hashdex.h" -#include "entrypoint_types/lss.h" +#include "entrypoint/types/exception.h" +#include "entrypoint/types/hashdex.h" +#include "entrypoint/types/lss.h" #include "primitives/go_slice.h" #include "primitives/go_slice_protozero.h" #include "wal/decoder.h" diff --git a/pp/entrypoint/wal_decoder.h b/pp/entrypoint/bridge/wal_decoder.h similarity index 100% rename from pp/entrypoint/wal_decoder.h rename to pp/entrypoint/bridge/wal_decoder.h diff --git a/pp/entrypoint/wal_encoder.cpp b/pp/entrypoint/bridge/wal_encoder.cpp similarity index 99% rename from pp/entrypoint/wal_encoder.cpp rename to pp/entrypoint/bridge/wal_encoder.cpp index 7d17a76e6a..ec46ca9807 100644 --- a/pp/entrypoint/wal_encoder.cpp +++ b/pp/entrypoint/bridge/wal_encoder.cpp @@ -2,8 +2,8 @@ #include -#include "entrypoint_types/exception.h" -#include "entrypoint_types/hashdex.h" +#include "entrypoint/types/exception.h" +#include "entrypoint/types/hashdex.h" #include "primitives/go_slice.h" #include "wal/encoder.h" #include "wal/wal.h" diff --git a/pp/entrypoint/wal_encoder.h b/pp/entrypoint/bridge/wal_encoder.h similarity index 100% rename from pp/entrypoint/wal_encoder.h rename to pp/entrypoint/bridge/wal_encoder.h diff --git a/pp/entrypoint/wal_hashdex.cpp b/pp/entrypoint/bridge/wal_hashdex.cpp similarity index 98% rename from pp/entrypoint/wal_hashdex.cpp rename to pp/entrypoint/bridge/wal_hashdex.cpp index 8eb3500e6a..701eda664d 100644 --- a/pp/entrypoint/wal_hashdex.cpp +++ b/pp/entrypoint/bridge/wal_hashdex.cpp @@ -1,8 +1,8 @@ #include "wal_hashdex.h" -#include "entrypoint_types/data_storage.h" -#include "entrypoint_types/exception.h" -#include "entrypoint_types/hashdex.h" +#include "entrypoint/types/data_storage.h" +#include "entrypoint/types/exception.h" +#include "entrypoint/types/hashdex.h" #include "primitives/go_slice.h" #include "wal/decoder.h" diff --git a/pp/entrypoint/wal_hashdex.h b/pp/entrypoint/bridge/wal_hashdex.h similarity index 100% rename from pp/entrypoint/wal_hashdex.h rename to pp/entrypoint/bridge/wal_hashdex.h diff --git a/pp/entrypoint_types/data_storage.h b/pp/entrypoint/types/data_storage.h similarity index 100% rename from pp/entrypoint_types/data_storage.h rename to pp/entrypoint/types/data_storage.h diff --git a/pp/entrypoint_types/encoder.h b/pp/entrypoint/types/encoder.h similarity index 100% rename from pp/entrypoint_types/encoder.h rename to pp/entrypoint/types/encoder.h diff --git a/pp/entrypoint_types/exception.h b/pp/entrypoint/types/exception.h similarity index 100% rename from pp/entrypoint_types/exception.h rename to pp/entrypoint/types/exception.h diff --git a/pp/entrypoint_types/go_constants.h b/pp/entrypoint/types/go_constants.h similarity index 100% rename from pp/entrypoint_types/go_constants.h rename to pp/entrypoint/types/go_constants.h diff --git a/pp/entrypoint_types/hashdex.h b/pp/entrypoint/types/hashdex.h similarity index 96% rename from pp/entrypoint_types/hashdex.h rename to pp/entrypoint/types/hashdex.h index 30cc641b9d..03ea96cd29 100644 --- a/pp/entrypoint_types/hashdex.h +++ b/pp/entrypoint/types/hashdex.h @@ -3,7 +3,7 @@ #include #include -#include "entrypoint_types/lss.h" +#include "entrypoint/types/lss.h" #include "wal/hashdex/basic_decoder.h" #include "wal/hashdex/go_head.h" #include "wal/hashdex/go_model.h" diff --git a/pp/entrypoint_types/loader.h b/pp/entrypoint/types/loader.h similarity index 98% rename from pp/entrypoint_types/loader.h rename to pp/entrypoint/types/loader.h index d785211e24..340ed68d2d 100644 --- a/pp/entrypoint_types/loader.h +++ b/pp/entrypoint/types/loader.h @@ -5,7 +5,7 @@ #include "bare_bones/iterator.h" #include "bare_bones/preprocess.h" -#include "entrypoint_types/lss.h" +#include "entrypoint/types/lss.h" #include "series_data/data_storage.h" #include "series_data/unloading/loader.h" #include "series_data/unloading/reverter.h" diff --git a/pp/entrypoint_types/lss.h b/pp/entrypoint/types/lss.h similarity index 100% rename from pp/entrypoint_types/lss.h rename to pp/entrypoint/types/lss.h diff --git a/pp/entrypoint_types/querier.h b/pp/entrypoint/types/querier.h similarity index 97% rename from pp/entrypoint_types/querier.h rename to pp/entrypoint/types/querier.h index 075157eb79..f5d72a3295 100644 --- a/pp/entrypoint_types/querier.h +++ b/pp/entrypoint/types/querier.h @@ -8,8 +8,8 @@ #include "bare_bones/bitset.h" #include "bare_bones/preprocess.h" -#include "entrypoint_types/go_constants.h" -#include "entrypoint_types/serialized_data.h" +#include "entrypoint/types/go_constants.h" +#include "entrypoint/types/serialized_data.h" #include "primitives/go_slice.h" #include "primitives/primitives.h" #include "series_data/data_storage.h" diff --git a/pp/entrypoint_types/serialized_data.h b/pp/entrypoint/types/serialized_data.h similarity index 100% rename from pp/entrypoint_types/serialized_data.h rename to pp/entrypoint/types/serialized_data.h From 314171383cb40cde6a0a47aa7ff841904887e00f Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Tue, 30 Jun 2026 11:04:39 +0000 Subject: [PATCH 07/31] build fixes --- pp/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pp/BUILD b/pp/BUILD index df792a66b6..a7695adcf7 100644 --- a/pp/BUILD +++ b/pp/BUILD @@ -200,7 +200,7 @@ cc_library( cc_test( name = "entrypoint_types_test", - srcs = glob(["entrypoint_types/**/*_tests.cpp"]), + srcs = glob(["entrypoint/types/**/*_tests.cpp"]), deps = [ ":bare_bones", ":entrypoint_types", From db4d0a4b5712c5658258086f30d4a88cd50ed14f Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Tue, 30 Jun 2026 11:13:30 +0000 Subject: [PATCH 08/31] consistent namespace naming --- pp/entrypoint/bridge/go_constants.cpp | 2 +- pp/entrypoint/bridge/head_status.cpp | 8 +-- pp/entrypoint/bridge/head_wal.cpp | 22 +++---- pp/entrypoint/bridge/index_writer.cpp | 8 +-- pp/entrypoint/bridge/label_set.cpp | 4 +- pp/entrypoint/bridge/primitives_lss.cpp | 32 +++++----- pp/entrypoint/bridge/prometheus_relabeler.cpp | 62 +++++++++---------- pp/entrypoint/bridge/remote_write.cpp | 6 +- .../bridge/series_data_data_storage.cpp | 30 ++++----- pp/entrypoint/bridge/series_data_encoder.cpp | 14 ++--- ...ies_data_serialization_serialized_data.cpp | 18 +++--- pp/entrypoint/bridge/wal_decoder.cpp | 22 +++---- pp/entrypoint/bridge/wal_encoder.cpp | 20 +++--- pp/entrypoint/bridge/wal_hashdex.cpp | 10 +-- pp/entrypoint/types/data_storage.h | 4 +- pp/entrypoint/types/encoder.h | 4 +- pp/entrypoint/types/exception.h | 4 +- pp/entrypoint/types/hashdex.h | 2 +- pp/entrypoint/types/loader.h | 4 +- pp/entrypoint/types/lss.h | 4 +- pp/entrypoint/types/querier.h | 8 +-- pp/entrypoint/types/serialized_data.h | 4 +- pp/go/cppbridge/entrypoint.h | 28 ++++----- 23 files changed, 160 insertions(+), 160 deletions(-) diff --git a/pp/entrypoint/bridge/go_constants.cpp b/pp/entrypoint/bridge/go_constants.cpp index fd2ca8da9f..d6b50d2235 100644 --- a/pp/entrypoint/bridge/go_constants.cpp +++ b/pp/entrypoint/bridge/go_constants.cpp @@ -13,7 +13,7 @@ static_assert(sizeof(roaring::Roaring) == Sizeof_RoaringBitset); static_assert(sizeof(PromPP::Prometheus::Relabel::InnerSeries) == Sizeof_InnerSeries); -static_assert(sizeof(entrypoint_types::SerializedDataIterator) == Sizeof_SerializedDataIterator); +static_assert(sizeof(entrypoint::types::SerializedDataIterator) == Sizeof_SerializedDataIterator); static_assert(sizeof(metrics::Storage::Iterator) == Sizeof_MetricsIterator); diff --git a/pp/entrypoint/bridge/head_status.cpp b/pp/entrypoint/bridge/head_status.cpp index 4d466397b9..e672911845 100644 --- a/pp/entrypoint/bridge/head_status.cpp +++ b/pp/entrypoint/bridge/head_status.cpp @@ -5,8 +5,8 @@ #include "head/status.h" #include "primitives/go_slice.h" -using entrypoint_types::DataStoragePtr; -using entrypoint_types::LssVariantPtr; +using entrypoint::types::DataStoragePtr; +using entrypoint::types::LssVariantPtr; using Status = head::Status; @@ -17,9 +17,9 @@ extern "C" void prompp_get_head_status_lss(void* args, void* res) { }; const auto in = static_cast(args); - const auto& lss = std::get(*in->lss); + const auto& lss = std::get(*in->lss); - head::StatusGetterLSS{lss, in->limit}.get(*static_cast(res)); + head::StatusGetterLSS{lss, in->limit}.get(*static_cast(res)); } extern "C" void prompp_get_head_status_data_storage(void* args, void* res) { diff --git a/pp/entrypoint/bridge/head_wal.cpp b/pp/entrypoint/bridge/head_wal.cpp index 295030ac6d..6e40be0877 100644 --- a/pp/entrypoint/bridge/head_wal.cpp +++ b/pp/entrypoint/bridge/head_wal.cpp @@ -11,15 +11,15 @@ #include "wal/encoder.h" #include "wal/wal.h" -using Encoder = PromPP::WAL::GenericEncoder>; +using Encoder = PromPP::WAL::GenericEncoder>; using EncoderPtr = std::unique_ptr; -using Decoder = PromPP::WAL::GenericDecoder; +using Decoder = PromPP::WAL::GenericDecoder; using DecoderPtr = std::unique_ptr; static_assert(sizeof(EncoderPtr) == sizeof(void*)); static_assert(sizeof(DecoderPtr) == sizeof(void*)); extern "C" void prompp_head_wal_encoder_ctor(void* args, void* res) { - using entrypoint_types::LssVariantPtr; + using entrypoint::types::LssVariantPtr; struct Arguments { uint16_t shard_id; @@ -32,7 +32,7 @@ extern "C" void prompp_head_wal_encoder_ctor(void* args, void* res) { }; const auto in = static_cast(args); - auto& lss = std::get(*in->lss); + auto& lss = std::get(*in->lss); new (res) Result{.encoder = std::make_unique(lss, in->shard_id, in->log_shards)}; } @@ -86,7 +86,7 @@ extern "C" void prompp_head_wal_encoder_add_inner_series(void* args, void* res) in->encoder->add_inner_series(in->incoming_inner_series, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -110,7 +110,7 @@ extern "C" void prompp_head_wal_encoder_finalize(void* args, void* res) { in->encoder->finalize(out, out_stream); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -129,7 +129,7 @@ extern "C" void prompp_head_wal_encoder_max_written_item_index(void* args, void* } extern "C" void prompp_head_wal_decoder_ctor(void* args, void* res) { - using entrypoint_types::LssVariantPtr; + using entrypoint::types::LssVariantPtr; struct Arguments { LssVariantPtr lss; @@ -141,7 +141,7 @@ extern "C" void prompp_head_wal_decoder_ctor(void* args, void* res) { }; const auto in = static_cast(args); - auto& lss = std::get(*in->lss); + auto& lss = std::get(*in->lss); new (res) Result{.decoder = std::make_unique(lss, in->encoder_version)}; } @@ -182,7 +182,7 @@ extern "C" void prompp_head_wal_decoder_decode(void* args, void* res) { } } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -190,7 +190,7 @@ extern "C" void prompp_head_wal_decoder_decode_to_data_storage(void* args, void* struct Arguments { DecoderPtr decoder; PromPP::Primitives::Go::SliceView segment; - entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint::types::SeriesDataEncoderWrapperPtr encoder_wrapper; }; struct Result { @@ -213,6 +213,6 @@ extern "C" void prompp_head_wal_decoder_decode_to_data_storage(void* args, void* out->encode_timestamp = in->decoder->decoder().encoded_at_tsns(); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } diff --git a/pp/entrypoint/bridge/index_writer.cpp b/pp/entrypoint/bridge/index_writer.cpp index 296f7b9dc9..115ad1ab78 100644 --- a/pp/entrypoint/bridge/index_writer.cpp +++ b/pp/entrypoint/bridge/index_writer.cpp @@ -8,7 +8,7 @@ using PromPP::Primitives::Go::SliceView; using series_index::prometheus::tsdb::index::ChunkMetadata; -using IndexWriter = series_index::prometheus::tsdb::index::IndexWriter; +using IndexWriter = series_index::prometheus::tsdb::index::IndexWriter; namespace { @@ -22,7 +22,7 @@ struct IndexWriterHandle { // stable pointer from the constructor (like the buffer), so reading it needs no extra cgo call. uint8_t has_more_postings{0}; - explicit IndexWriterHandle(entrypoint_types::QueryableEncodingBimap& lss) : writer(lss) {} + explicit IndexWriterHandle(entrypoint::types::QueryableEncodingBimap& lss) : writer(lss) {} PromPP::Primitives::Go::BytesStream reset_buffer() noexcept { buffer.resize(0); @@ -36,7 +36,7 @@ using IndexWriterHandlePtr = std::unique_ptr; extern "C" void prompp_index_writer_ctor(void* args, void* res) { struct Arguments { - entrypoint_types::LssVariantPtr lss; + entrypoint::types::LssVariantPtr lss; }; struct Result { IndexWriterHandlePtr writer; @@ -45,7 +45,7 @@ extern "C" void prompp_index_writer_ctor(void* args, void* res) { }; const auto in = static_cast(args); - auto handle = std::make_unique(std::get(*in->lss)); + auto handle = std::make_unique(std::get(*in->lss)); // Capture the interior pointers before moving the handle into the result (the move nulls it). auto* buffer = &handle->buffer; auto* has_more_postings = &handle->has_more_postings; diff --git a/pp/entrypoint/bridge/label_set.cpp b/pp/entrypoint/bridge/label_set.cpp index e62c84e900..7e917a5d8c 100644 --- a/pp/entrypoint/bridge/label_set.cpp +++ b/pp/entrypoint/bridge/label_set.cpp @@ -6,8 +6,8 @@ #include "primitives/go_model.h" #include "primitives/go_slice.h" -using entrypoint_types::LssVariantPtr; -using entrypoint_types::SnapshotLSSVariantPtr; +using entrypoint::types::LssVariantPtr; +using entrypoint::types::SnapshotLSSVariantPtr; using PromPP::Primitives::Go::Slice; using PromPP::Primitives::Go::SliceView; diff --git a/pp/entrypoint/bridge/primitives_lss.cpp b/pp/entrypoint/bridge/primitives_lss.cpp index 5205e690d0..e6ec75b2f3 100644 --- a/pp/entrypoint/bridge/primitives_lss.cpp +++ b/pp/entrypoint/bridge/primitives_lss.cpp @@ -15,12 +15,12 @@ using GoLabelMatchers = PromPP::Primitives::Go::SliceView>; using GoSliceOfString = PromPP::Primitives::Go::Slice; using GoSliceViewString = PromPP::Primitives::Go::SliceView; -using entrypoint_types::LsIdsSlice; -using entrypoint_types::LsIdsSlicePtr; -using entrypoint_types::LssType; -using entrypoint_types::LssVariantPtr; -using entrypoint_types::QueryableEncodingBimap; -using entrypoint_types::SnapshotLSSVariantPtr; +using entrypoint::types::LsIdsSlice; +using entrypoint::types::LsIdsSlicePtr; +using entrypoint::types::LssType; +using entrypoint::types::LssVariantPtr; +using entrypoint::types::QueryableEncodingBimap; +using entrypoint::types::SnapshotLSSVariantPtr; extern "C" void prompp_primitives_lss_ctor(void* args, void* res) { struct Arguments { @@ -62,7 +62,7 @@ PROMPP_ALWAYS_INLINE FindOrEmplaceResult find_or_emplace(auto& lss, const auto& if constexpr (Lss::kIsReadOnly) { throw BareBones::Exception(0x1b877a0ab46a69a6, "lss is readonly"); } else { - const entrypoint_types::ReallocationsDetector reallocation_detector(lss); + const entrypoint::types::ReallocationsDetector reallocation_detector(lss); const auto ls_id = lss.find_or_emplace(label_set); return {.ls_id = ls_id, .lss_has_reallocations = reallocation_detector.has_reallocations()}; } @@ -95,8 +95,8 @@ extern "C" void prompp_primitives_lss_find_or_emplace_builder(void* args, void* const auto in = static_cast(args); new (res) FindOrEmplaceResult(std::visit( [&builder = in->builder](Lss& lss) { - static const entrypoint_types::SnapshotLSS::value_type empty_label_set; - const auto& label_set = builder.snapshot ? std::get(*builder.snapshot)[builder.ls_id] : empty_label_set; + static const entrypoint::types::SnapshotLSS::value_type empty_label_set; + const auto& label_set = builder.snapshot ? std::get(*builder.snapshot)[builder.ls_id] : empty_label_set; return find_or_emplace(lss, LabelSetBuilder{label_set, builder.sorted_add, builder.sorted_del}); }, @@ -180,8 +180,8 @@ extern "C" void prompp_primitives_group_series_by_label_names(void* args, void* const auto in = static_cast(args); const auto out = new (res) GroupSeriesByLabelNamesResult(); - series_index::querier::group_series_by_label_names( - std::get(*in->snapshot), in->series_ids.span(), in->label_name_ids.span(), out->groups); + series_index::querier::group_series_by_label_names( + std::get(*in->snapshot), in->series_ids.span(), in->label_name_ids.span(), out->groups); } extern "C" void prompp_primitives_group_series_by_label_names_result_free(void* args) { @@ -306,7 +306,7 @@ extern "C" void prompp_create_snapshot_lss(void* args, void* res) { SnapshotLSSVariantPtr snapshot; }; - new (res) Result{.snapshot = entrypoint_types::create_snapshot_lss(*static_cast(args)->lss)}; + new (res) Result{.snapshot = entrypoint::types::create_snapshot_lss(*static_cast(args)->lss)}; } extern "C" void prompp_primitives_snapshot_dtor(void* args) { @@ -343,10 +343,10 @@ extern "C" void prompp_primitives_snapshot_lss_copy_added_series(uint64_t source uint64_t source_bitset, uint64_t destination_lss, uint64_t ids_mapping) { - const auto& src_snapshot_variant = *std::bit_cast(source_snapshot); - const auto& src = std::get(src_snapshot_variant); + const auto& src_snapshot_variant = *std::bit_cast(source_snapshot); + const auto& src = std::get(src_snapshot_variant); const auto& src_bitset = *std::bit_cast(source_bitset); - auto& dst = std::get(*std::bit_cast(destination_lss)); + auto& dst = std::get(*std::bit_cast(destination_lss)); const auto dst_src_ids_mapping = std::bit_cast(ids_mapping); *dst_src_ids_mapping = std::make_unique(); @@ -372,7 +372,7 @@ extern "C" void prompp_primitives_lss_finalize_copy_and_shrink(void* args) { }; const auto* in = static_cast(args); auto& lss = std::get(*in->lss); - auto& resolve_snapshot = std::get(*in->resolve_snapshot); + auto& resolve_snapshot = std::get(*in->resolve_snapshot); lss.finalize_copy_and_shrink(resolve_snapshot, *in->new_to_old_mapping); } diff --git a/pp/entrypoint/bridge/prometheus_relabeler.cpp b/pp/entrypoint/bridge/prometheus_relabeler.cpp index 668e08d675..51f4ae6c0d 100644 --- a/pp/entrypoint/bridge/prometheus_relabeler.cpp +++ b/pp/entrypoint/bridge/prometheus_relabeler.cpp @@ -7,7 +7,7 @@ #include "primitives/go_slice.h" #include "prometheus/relabeler.h" -using entrypoint_types::LssVariantPtr; +using entrypoint::types::LssVariantPtr; using PromPP::Primitives::Go::SliceView; using PromPP::Prometheus::Relabel::InnerSeries; using PromPP::Prometheus::Relabel::RelabeledSeries; @@ -39,7 +39,7 @@ extern "C" void prompp_prometheus_stateless_relabeler_ctor(void* args, void* res out->stateless_relabeler = std::make_unique(in->go_rcfgs); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -67,7 +67,7 @@ extern "C" void prompp_prometheus_stateless_relabeler_reset_to(void* args, void* } catch (...) { auto* out = new (res) Result(); auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -196,7 +196,7 @@ extern "C" void prompp_prometheus_per_shard_relabeler_ctor(void* args, void* res out->per_shard_relabeler = std::make_unique(in->external_labels, in->stateless_relabeler, in->number_of_shards, in->shard_id); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -245,7 +245,7 @@ extern "C" void prompp_prometheus_per_shard_single_relabeler_update_relabeler_st } catch (...) { auto* out = new (res) Result(); auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -265,12 +265,12 @@ extern "C" void prompp_prometheus_per_shard_relabeler_output_relabeling(void* ar const auto in = static_cast(args); try { - const auto& lss = std::get(*in->lss); + const auto& lss = std::get(*in->lss); in->per_shard_relabeler->output_relabeling(lss, *in->cache, in->relabeled_series, in->incoming_inner_series, in->encoders_inner_series); } catch (...) { const auto out = new (res) Result(); auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -341,7 +341,7 @@ extern "C" void prompp_prometheus_cache_update(void* args, void* res) { } catch (...) { auto* out = new (res) Result(); auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -401,10 +401,10 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling(void* try { std::visit( [in, out](auto& hashdex) { - auto& input_lss = std::get(*in->input_lss); - auto& target_lss = std::get(*in->target_lss); + auto& input_lss = std::get(*in->input_lss); + auto& target_lss = std::get(*in->target_lss); - const entrypoint_types::ReallocationsDetector reallocation_detector(target_lss); + const entrypoint::types::ReallocationsDetector reallocation_detector(target_lss); in->per_goroutine_relabeler->input_relabeling(input_lss, target_lss, *in->cache, hashdex, in->options, *in->stateless_relabeler, *out, in->shards_inner_series, in->shards_relabeled_series); target_lss.build_deferred_indexes(); @@ -413,7 +413,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling(void* *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -441,8 +441,8 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_from_ try { std::visit( [in, out](auto& hashdex) { - auto& input_lss = std::get(*in->input_lss); - auto& target_lss = std::get(*in->target_lss); + auto& input_lss = std::get(*in->input_lss); + auto& target_lss = std::get(*in->target_lss); out->ok = in->per_goroutine_relabeler->input_relabeling_from_cache(input_lss, target_lss, *in->cache, hashdex, in->options, *out, in->shards_inner_series); @@ -450,7 +450,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_from_ *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -481,10 +481,10 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_with_ try { std::visit( [in, out](auto& hashdex) { - auto& input_lss = std::get(*in->input_lss); - auto& target_lss = std::get(*in->target_lss); + auto& input_lss = std::get(*in->input_lss); + auto& target_lss = std::get(*in->target_lss); - const entrypoint_types::ReallocationsDetector reallocation_detector(target_lss); + const entrypoint::types::ReallocationsDetector reallocation_detector(target_lss); in->per_goroutine_relabeler->input_relabeling_with_stalenans(input_lss, target_lss, *in->cache, hashdex, in->options, *in->stateless_relabeler, *out, in->shards_inner_series, in->shards_relabeled_series, in->def_timestamp); target_lss.build_deferred_indexes(); @@ -493,7 +493,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_with_ *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -522,8 +522,8 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_with_ try { std::visit( [in, out](auto& hashdex) { - auto& input_lss = std::get(*in->input_lss); - auto& target_lss = std::get(*in->target_lss); + auto& input_lss = std::get(*in->input_lss); + auto& target_lss = std::get(*in->target_lss); out->ok = in->per_goroutine_relabeler->input_relabeling_with_stalenans_from_cache(input_lss, target_lss, *in->cache, hashdex, in->options, *out, in->shards_inner_series, in->def_timestamp); @@ -531,7 +531,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_relabeling_with_ *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -556,9 +556,9 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_transition_relab try { std::visit( [in, out](auto& hashdex) { - auto& target_lss = std::get(*in->target_lss); + auto& target_lss = std::get(*in->target_lss); - const entrypoint_types::ReallocationsDetector reallocation_detector(target_lss); + const entrypoint::types::ReallocationsDetector reallocation_detector(target_lss); in->per_goroutine_relabeler->input_transition_relabeling(target_lss, hashdex, *out, in->shards_inner_series); target_lss.build_deferred_indexes(); out->target_lss_has_reallocations = reallocation_detector.has_reallocations(); @@ -566,7 +566,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_transition_relab *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -591,14 +591,14 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_input_transition_relab try { std::visit( [in, out](auto& hashdex) { - auto& target_lss = std::get(*in->target_lss); + auto& target_lss = std::get(*in->target_lss); out->ok = in->per_goroutine_relabeler->input_transition_relabeling_only_read(target_lss, hashdex, *out, in->shards_inner_series); }, *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -619,8 +619,8 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_append_relabeler_serie const auto out = new (res) Result(); try { - auto& lss = std::get(*in->target_lss); - const entrypoint_types::ReallocationsDetector reallocation_detector(lss); + auto& lss = std::get(*in->target_lss); + const entrypoint::types::ReallocationsDetector reallocation_detector(lss); for (size_t id = 0; id != in->shards_relabeled_series.size(); ++id) { if (in->shards_relabeled_series[id].size() == 0) { @@ -634,7 +634,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_append_relabeler_serie out->target_lss_has_reallocations = reallocation_detector.has_reallocations(); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -652,7 +652,7 @@ extern "C" void prompp_prometheus_per_goroutine_relabeler_track_stale_nans(void* extern "C" void prompp_remap_stale_nans_state(void* args) { struct Arguments { StaleNaNsStatePtr stale_nans_state; - entrypoint_types::LsIdsSlicePtr dst_src_ls_ids_mapping; + entrypoint::types::LsIdsSlicePtr dst_src_ls_ids_mapping; }; const auto in = static_cast(args); diff --git a/pp/entrypoint/bridge/remote_write.cpp b/pp/entrypoint/bridge/remote_write.cpp index c774aaf07d..a00af63325 100644 --- a/pp/entrypoint/bridge/remote_write.cpp +++ b/pp/entrypoint/bridge/remote_write.cpp @@ -35,7 +35,7 @@ extern "C" void prompp_remote_write_message_encoders_dtor(void* args) { extern "C" void prompp_remote_write_encode_message(void* args) { struct Arguments { MessageEncoder* encoder; - PromPP::Primitives::Go::SliceView snapshot_list; + PromPP::Primitives::Go::SliceView snapshot_list; uint64_t message_index; uint64_t messages_count; PromPP::Primitives::Go::SliceView messages; @@ -43,8 +43,8 @@ extern "C" void prompp_remote_write_encode_message(void* args) { const auto in = static_cast(args); - const auto snapshot_getter = [in](uint32_t shard_id) -> const entrypoint_types::SnapshotLSS& { - return std::get(*in->snapshot_list[shard_id]); + const auto snapshot_getter = [in](uint32_t shard_id) -> const entrypoint::types::SnapshotLSS& { + return std::get(*in->snapshot_list[shard_id]); }; in->encoder->encode(snapshot_getter, in->message_index, in->messages_count, in->messages); diff --git a/pp/entrypoint/bridge/series_data_data_storage.cpp b/pp/entrypoint/bridge/series_data_data_storage.cpp index d8d88fe49e..b550c3a4b7 100644 --- a/pp/entrypoint/bridge/series_data_data_storage.cpp +++ b/pp/entrypoint/bridge/series_data_data_storage.cpp @@ -18,9 +18,9 @@ #include "series_data/unloading/unloader.h" #include "series_index/querier/selector_querier.h" -using entrypoint_types::DataStoragePtr; -using entrypoint_types::QueryableEncodingBimap; -using entrypoint_types::QueryStatus; +using entrypoint::types::DataStoragePtr; +using entrypoint::types::QueryableEncodingBimap; +using entrypoint::types::QueryStatus; using PromPP::Primitives::LabelSetID; using PromPP::Primitives::Go::BytesStream; using PromPP::Primitives::Go::Slice; @@ -34,15 +34,15 @@ using SerializedChunkRecoder = head::ChunkRecoder; using ChunkRecoderVariantPtr = std::unique_ptr; -using entrypoint_types::RevertableLoader; +using entrypoint::types::RevertableLoader; using LoaderVariant = std::variant; using LoaderVariantPtr = std::unique_ptr; static_assert(sizeof(LoaderVariantPtr) == sizeof(void*)); -using entrypoint_types::QuerierType; -using entrypoint_types::QuerierVariant; -using entrypoint_types::QuerierVariantPtr; +using entrypoint::types::QuerierType; +using entrypoint::types::QuerierVariant; +using entrypoint::types::QuerierVariantPtr; extern "C" void prompp_series_data_data_storage_ctor(void* res) { using Result = struct { @@ -117,7 +117,7 @@ extern "C" void prompp_series_data_data_storage_queried_series_set_bitset(void* extern "C" void prompp_series_data_data_storage_query_v2(void* args, void* res) { using Query = series_data::querier::Query>; - using entrypoint_types::RangeQuerierWithArgumentsWrapperV2; + using entrypoint::types::RangeQuerierWithArgumentsWrapperV2; using series_data::querier::Querier; struct Arguments { @@ -128,7 +128,7 @@ extern "C" void prompp_series_data_data_storage_query_v2(void* args, void* res) struct Result { QuerierVariantPtr querier{}; QueryStatus status{}; - entrypoint_types::SerializedDataPtr* serialized_data{}; + entrypoint::types::SerializedDataPtr* serialized_data{}; }; const auto in = static_cast(args); @@ -146,7 +146,7 @@ extern "C" void prompp_series_data_data_storage_query_v2(void* args, void* res) } extern "C" void prompp_series_data_data_storage_instant_query(void* args, void* res) { - using entrypoint_types::InstantQuerierWithArgumentsWrapperEntrypoint; + using entrypoint::types::InstantQuerierWithArgumentsWrapperEntrypoint; using PromPP::Primitives::Timestamp; using series_data::InstantQuerier; @@ -154,7 +154,7 @@ extern "C" void prompp_series_data_data_storage_instant_query(void* args, void* DataStoragePtr data_storage; SliceView label_set_ids; Timestamp timestamp; - entrypoint_types::SampleWithGoLabels* samples; + entrypoint::types::SampleWithGoLabels* samples; }; using Result = struct { @@ -179,7 +179,7 @@ extern "C" void prompp_series_data_data_storage_instant_query(void* args, void* } extern "C" void prompp_series_data_data_storage_query_final(void* args) { - using entrypoint_types::QuerierVariantPtr; + using entrypoint::types::QuerierVariantPtr; struct Arguments { Slice queriers; @@ -240,7 +240,7 @@ extern "C" void prompp_series_data_data_storage_dtor(void* args) { extern "C" void prompp_series_data_chunk_recoder_ctor(void* args, void* res) { struct Arguments { - entrypoint_types::LssVariantPtr lss; + entrypoint::types::LssVariantPtr lss; uint32_t ls_id_batch_size; DataStoragePtr data_storage; PromPP::Primitives::TimeInterval time_interval; @@ -261,7 +261,7 @@ extern "C" void prompp_series_data_chunk_recoder_ctor(void* args, void* res) { extern "C" void prompp_series_data_serialized_chunk_recoder_ctor(void* args, void* res) { struct Arguments { - entrypoint_types::SerializedDataPtr* serialized_data; + entrypoint::types::SerializedDataPtr* serialized_data; PromPP::Primitives::TimeInterval time_interval; }; struct Result { @@ -408,7 +408,7 @@ extern "C" void prompp_series_data_data_storage_loader_ctor(void* args, void* re extern "C" void prompp_series_data_data_storage_revertable_loader_ctor(void* args, void* res) { struct Arguments { - entrypoint_types::LssVariantPtr lss; + entrypoint::types::LssVariantPtr lss; uint32_t ls_id_batch_size; DataStoragePtr data_storage; }; diff --git a/pp/entrypoint/bridge/series_data_encoder.cpp b/pp/entrypoint/bridge/series_data_encoder.cpp index e7a0c66bad..a06644b476 100644 --- a/pp/entrypoint/bridge/series_data_encoder.cpp +++ b/pp/entrypoint/bridge/series_data_encoder.cpp @@ -10,15 +10,15 @@ extern "C" void prompp_series_data_encoder_ctor(void* args, void* res) { series_data::DataStorage* data_storage; }; using Result = struct { - entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint::types::SeriesDataEncoderWrapperPtr encoder_wrapper; }; - new (res) Result{.encoder_wrapper = std::make_unique(*static_cast(args)->data_storage)}; + new (res) Result{.encoder_wrapper = std::make_unique(*static_cast(args)->data_storage)}; } extern "C" void prompp_series_data_encoder_encode(void* args) { struct Arguments { - entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint::types::SeriesDataEncoderWrapperPtr encoder_wrapper; uint32_t series_id; int64_t timestamp; double value; @@ -32,7 +32,7 @@ extern "C" void prompp_series_data_encoder_encode(void* args) { extern "C" void prompp_series_data_encoder_encode_inner_series_slice(void* args) { struct Arguments { - entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint::types::SeriesDataEncoderWrapperPtr encoder_wrapper; PromPP::Primitives::Go::SliceView inner_series_slice; }; @@ -52,18 +52,18 @@ extern "C" void prompp_series_data_encoder_encode_inner_series_slice(void* args) extern "C" void prompp_series_data_encoder_merge_out_of_order_chunks(void* args) { struct Arguments { - entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint::types::SeriesDataEncoderWrapperPtr encoder_wrapper; }; auto& encoder = static_cast(args)->encoder_wrapper->encoder; const auto arena_guard = encoder.storage().thread_arena_guard(); - entrypoint_types::OutdatedChunkMerger{encoder}.merge(); + entrypoint::types::OutdatedChunkMerger{encoder}.merge(); } extern "C" void prompp_series_data_encoder_dtor(void* args) { struct Arguments { - entrypoint_types::SeriesDataEncoderWrapperPtr encoder_wrapper; + entrypoint::types::SeriesDataEncoderWrapperPtr encoder_wrapper; }; static_cast(args)->~Arguments(); diff --git a/pp/entrypoint/bridge/series_data_serialization_serialized_data.cpp b/pp/entrypoint/bridge/series_data_serialization_serialized_data.cpp index 81a488ee9b..43b5af3c78 100644 --- a/pp/entrypoint/bridge/series_data_serialization_serialized_data.cpp +++ b/pp/entrypoint/bridge/series_data_serialization_serialized_data.cpp @@ -4,7 +4,7 @@ extern "C" void prompp_series_data_serialization_serialized_data_next(void* args, void* res) { struct Arguments { - entrypoint_types::SerializedDataPtr serialized_data; + entrypoint::types::SerializedDataPtr serialized_data; }; using Result = struct { @@ -17,26 +17,26 @@ extern "C" void prompp_series_data_serialization_serialized_data_next(void* args extern "C" void prompp_series_data_serialization_serialized_data_iterator_ctor(void* args) { struct Arguments { - entrypoint_types::SerializedDataIterator* iterator; - entrypoint_types::SerializedDataPtr serialized_data; + entrypoint::types::SerializedDataIterator* iterator; + entrypoint::types::SerializedDataPtr serialized_data; uint32_t chunk_ref; }; const auto in = static_cast(args); - new (in->iterator) entrypoint_types::SerializedDataIterator(in->serialized_data->iterator(in->chunk_ref)); + new (in->iterator) entrypoint::types::SerializedDataIterator(in->serialized_data->iterator(in->chunk_ref)); } extern "C" void prompp_series_data_serialization_serialized_data_iterator_next(void* iterator) { using series_data::decoder::DecodeIteratorSentinel; - ++(*static_cast(iterator)); + ++(*static_cast(iterator)); } extern "C" void prompp_series_data_serialization_serialized_data_iterator_seek(void* args) { using series_data::decoder::DecodeIteratorSentinel; struct Arguments { - entrypoint_types::SerializedDataIterator* iterator; + entrypoint::types::SerializedDataIterator* iterator; int64_t target_timestamp; }; @@ -46,8 +46,8 @@ extern "C" void prompp_series_data_serialization_serialized_data_iterator_seek(v extern "C" void prompp_series_data_serialization_serialized_data_iterator_reset(void* args) { struct Arguments { - entrypoint_types::SerializedDataIterator* iterator; - entrypoint_types::SerializedDataPtr serialized_data; + entrypoint::types::SerializedDataIterator* iterator; + entrypoint::types::SerializedDataPtr serialized_data; uint32_t chunk_ref; }; @@ -57,7 +57,7 @@ extern "C" void prompp_series_data_serialization_serialized_data_iterator_reset( extern "C" void prompp_series_data_serialization_serialized_data_dtor(void* args) { struct Arguments { - entrypoint_types::SerializedDataPtr serialized_data; + entrypoint::types::SerializedDataPtr serialized_data; }; static_cast(args)->~Arguments(); diff --git a/pp/entrypoint/bridge/wal_decoder.cpp b/pp/entrypoint/bridge/wal_decoder.cpp index 453db90116..6137f5bf13 100644 --- a/pp/entrypoint/bridge/wal_decoder.cpp +++ b/pp/entrypoint/bridge/wal_decoder.cpp @@ -54,7 +54,7 @@ extern "C" void prompp_wal_decoder_decode(void* args, void* res) { in->decoder->decode(in->segment, out->protobuf, *out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -90,7 +90,7 @@ extern "C" void prompp_wal_decoder_decode_to_hashdex(void* args, void* res) { out->replica.reset_to(replica.data(), replica.size()); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -143,7 +143,7 @@ extern "C" void prompp_wal_decoder_decode_to_hashdex_with_metric_injection(void* out->replica.reset_to(replica.data(), replica.size()); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -164,7 +164,7 @@ extern "C" void prompp_wal_decoder_decode_dry(void* args, void* res) { in->decoder->decode_dry(in->segment, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -187,7 +187,7 @@ extern "C" void prompp_wal_decoder_restore_from_stream(void* args, void* res) { in->decoder->restore_from_stream(in->stream, in->segment_id, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -195,9 +195,9 @@ extern "C" void prompp_wal_decoder_restore_from_stream(void* args, void* res) { // OutputDecoder // -using entrypoint_types::LssVariantPtr; +using entrypoint::types::LssVariantPtr; -using OutputDecoder = PromPP::WAL::OutputDecoder; +using OutputDecoder = PromPP::WAL::OutputDecoder; using OutputDecoderPtr = std::unique_ptr; static_assert(sizeof(OutputDecoderPtr) == sizeof(void*)); @@ -263,7 +263,7 @@ extern "C" void prompp_wal_output_decoder_ctor(void* args, void* res) { }; auto* in = static_cast(args); - auto& output_lss = std::get(*in->output_lss); + auto& output_lss = std::get(*in->output_lss); new (res) Result{.decoder = std::make_unique(*in->stateless_relabeler, output_lss, in->external_labels, static_cast(in->encoder_version))}; } @@ -294,7 +294,7 @@ extern "C" void prompp_wal_output_decoder_dump_to(void* args, void* res) { in->decoder->dump_to(bytes_stream); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -316,7 +316,7 @@ extern "C" void prompp_wal_output_decoder_load_from(void* args, void* res) { in->decoder->load_from(bytes_stream); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -366,6 +366,6 @@ extern "C" void prompp_wal_output_decoder_decode(void* args, void* res) { out->sample_count = in->samples_storage->samples_count() - prev_sample_count; } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } diff --git a/pp/entrypoint/bridge/wal_encoder.cpp b/pp/entrypoint/bridge/wal_encoder.cpp index ec46ca9807..8bac6d725b 100644 --- a/pp/entrypoint/bridge/wal_encoder.cpp +++ b/pp/entrypoint/bridge/wal_encoder.cpp @@ -63,7 +63,7 @@ extern "C" void prompp_wal_encoder_add(void* args, void* res) { std::visit(lmb, *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -89,7 +89,7 @@ extern "C" void prompp_wal_encoder_add_inner_series(void* args, void* res) { in->encoder->add_inner_series(in->incoming_inner_series, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -116,7 +116,7 @@ extern "C" void prompp_wal_encoder_add_relabeled_series(void* args, void* res) { in->encoder->add_relabeled_series(in->incoming_relabeled_series, in->relabeler_state_update, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -146,7 +146,7 @@ extern "C" void prompp_wal_encoder_add_with_stale_nans(void* args, void* res) { std::visit(lmb, *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -173,7 +173,7 @@ extern "C" void prompp_wal_encoder_collect_source(void* args, void* res) { in->encoder->collect_source(out, in->stale_ts, in->source_state); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -201,7 +201,7 @@ extern "C" void prompp_wal_encoder_finalize(void* args, void* res) { in->encoder->finalize(out, out_stream); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -273,7 +273,7 @@ extern "C" void prompp_wal_encoder_lightweight_add(void* args, void* res) { std::visit(lmb, *in->hashdex); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -316,7 +316,7 @@ extern "C" void prompp_wal_encoder_lightweight_add_inner_series(void* args, void in->encoder->add_inner_series(in->incoming_inner_series, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -361,7 +361,7 @@ extern "C" void prompp_wal_encoder_lightweight_add_relabeled_series(void* args, in->encoder->add_relabeled_series(in->incoming_relabeled_series, in->relabeler_state_update, out); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -405,6 +405,6 @@ extern "C" void prompp_wal_encoder_lightweight_finalize(void* args, void* res) { in->encoder->finalize(out, out_stream); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } diff --git a/pp/entrypoint/bridge/wal_hashdex.cpp b/pp/entrypoint/bridge/wal_hashdex.cpp index 701eda664d..4e652ef8d2 100644 --- a/pp/entrypoint/bridge/wal_hashdex.cpp +++ b/pp/entrypoint/bridge/wal_hashdex.cpp @@ -89,7 +89,7 @@ extern "C" void prompp_wal_protobuf_hashdex_snappy_presharding(void* args, void* out->replica.reset_to(replica.data(), replica.size()); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -136,7 +136,7 @@ extern "C" void prompp_wal_go_model_hashdex_presharding(void* args, void* res) { out->replica.reset_to(replica.data(), replica.size()); } catch (...) { auto err_stream = PromPP::Primitives::Go::BytesStream(&out->error); - entrypoint_types::handle_current_exception(err_stream); + entrypoint::types::handle_current_exception(err_stream); } } @@ -206,10 +206,10 @@ extern "C" void prompp_wal_go_head_hashdex_ctor(void* res) { extern "C" void prompp_wal_go_head_hashdex_presharding(void* args) { struct Arguments { HashdexVariant* hashdex; - entrypoint_types::LssVariantPtr lss; - entrypoint_types::DataStoragePtr data_storage; + entrypoint::types::LssVariantPtr lss; + entrypoint::types::DataStoragePtr data_storage; }; const auto in = static_cast(args); - std::get(*in->hashdex).presharding(&std::get(*in->lss), in->data_storage.get()); + std::get(*in->hashdex).presharding(&std::get(*in->lss), in->data_storage.get()); } diff --git a/pp/entrypoint/types/data_storage.h b/pp/entrypoint/types/data_storage.h index d04af5f878..73b617dfbf 100644 --- a/pp/entrypoint/types/data_storage.h +++ b/pp/entrypoint/types/data_storage.h @@ -4,10 +4,10 @@ #include "series_data/data_storage.h" -namespace entrypoint_types { +namespace entrypoint::types { using DataStoragePtr = std::unique_ptr; static_assert(sizeof(DataStoragePtr) == sizeof(void*)); -} // namespace entrypoint_types +} // namespace entrypoint::types diff --git a/pp/entrypoint/types/encoder.h b/pp/entrypoint/types/encoder.h index b135f4df98..adba4bbdfc 100644 --- a/pp/entrypoint/types/encoder.h +++ b/pp/entrypoint/types/encoder.h @@ -6,7 +6,7 @@ #include "series_data/encoder.h" #include "series_data/outdated_chunk_merger.h" -namespace entrypoint_types { +namespace entrypoint::types { using Encoder = series_data::Encoder<>; using OutdatedChunkMerger = series_data::OutdatedChunkMerger; @@ -21,4 +21,4 @@ using SeriesDataEncoderWrapperPtr = std::unique_ptr; static_assert(sizeof(SeriesDataEncoderWrapperPtr) == sizeof(void*)); -} // namespace entrypoint_types +} // namespace entrypoint::types diff --git a/pp/entrypoint/types/exception.h b/pp/entrypoint/types/exception.h index 33085643c3..dd19f3b8f0 100644 --- a/pp/entrypoint/types/exception.h +++ b/pp/entrypoint/types/exception.h @@ -7,7 +7,7 @@ #include "bare_bones/exception.h" #include "bare_bones/preprocess.h" -namespace entrypoint_types { +namespace entrypoint::types { PROMPP_ALWAYS_INLINE void handle_current_exception(std::ostream& out, const std::source_location location = std::source_location::current()) { out << location.function_name() << ": "; @@ -23,4 +23,4 @@ PROMPP_ALWAYS_INLINE void handle_current_exception(std::ostream& out, const std: } } -} // namespace entrypoint_types +} // namespace entrypoint::types diff --git a/pp/entrypoint/types/hashdex.h b/pp/entrypoint/types/hashdex.h index 03ea96cd29..1fcc46835e 100644 --- a/pp/entrypoint/types/hashdex.h +++ b/pp/entrypoint/types/hashdex.h @@ -22,7 +22,7 @@ enum HashdexType : uint8_t { kGoHead, }; -using GoHeadHashdex = PromPP::WAL::hashdex::GoHead; +using GoHeadHashdex = PromPP::WAL::hashdex::GoHead; using HashdexVariant = std::variant; using LsIdsSlicePtr = std::unique_ptr; @@ -192,4 +192,4 @@ inline SnapshotLSSVariantPtr create_snapshot_lss(LssVariant& lss_variant) { } } -} // namespace entrypoint_types +} // namespace entrypoint::types diff --git a/pp/entrypoint/types/querier.h b/pp/entrypoint/types/querier.h index f5d72a3295..3a045f2217 100644 --- a/pp/entrypoint/types/querier.h +++ b/pp/entrypoint/types/querier.h @@ -18,7 +18,7 @@ #include "series_data/querier/querier.h" #include "series_data/querier/query.h" -namespace entrypoint_types { +namespace entrypoint::types { template concept QuerierInterface = requires(Querier querier) { @@ -105,7 +105,7 @@ enum class QuerierType : uint8_t { kInstantQuerier = 0, kRangeQuerier, kRangeQue using QuerierVariant = std::variant; using QuerierVariantPtr = std::unique_ptr; -} // namespace entrypoint_types +} // namespace entrypoint::types -static_assert(entrypoint_types::QuerierInterface); -static_assert(entrypoint_types::QuerierInterface); +static_assert(entrypoint::types::QuerierInterface); +static_assert(entrypoint::types::QuerierInterface); diff --git a/pp/entrypoint/types/serialized_data.h b/pp/entrypoint/types/serialized_data.h index 576c146a2d..bec8ec06ff 100644 --- a/pp/entrypoint/types/serialized_data.h +++ b/pp/entrypoint/types/serialized_data.h @@ -7,7 +7,7 @@ #include "series_data/querier/querier.h" #include "series_data/serialization/serialized_data.h" -namespace entrypoint_types { +namespace entrypoint::types { class SerializedDataGo { public: @@ -30,4 +30,4 @@ using SerializedDataIterator = series_data::serialization::SerializedDataView::S static_assert(sizeof(SerializedDataPtr) == sizeof(void*)); -} // namespace entrypoint_types +} // namespace entrypoint::types diff --git a/pp/go/cppbridge/entrypoint.h b/pp/go/cppbridge/entrypoint.h index 67d9cf0cd5..800ed6d1b7 100755 --- a/pp/go/cppbridge/entrypoint.h +++ b/pp/go/cppbridge/entrypoint.h @@ -1,3 +1,17 @@ +#define Sizeof_SizeT sizeof(size_t) +#define Sizeof_StdVector 24 +#define Sizeof_BareBonesVector 16 +#define Sizeof_RoaringBitset 40 +#define Sizeof_InnerSeries (Sizeof_SizeT + Sizeof_BareBonesVector + Sizeof_RoaringBitset) +#define Sizeof_GoLabels 16 + +#define Sizeof_SerializedDataIterator 152 + +#define Sizeof_MetricsIterator 24 + +#define Sizeof_SegmentSamplesStorage 80 +#define Sizeof_RemoteWriteMessageEncoder 32 +#define Sizeof_SegmentSamplesStorageListIterator 56 #ifdef __cplusplus extern "C" { #endif @@ -33,20 +47,6 @@ void prompp_dump_memory_profile(void* args, void* res); #ifdef __cplusplus } #endif -#define Sizeof_SizeT sizeof(size_t) -#define Sizeof_StdVector 24 -#define Sizeof_BareBonesVector 16 -#define Sizeof_RoaringBitset 40 -#define Sizeof_InnerSeries (Sizeof_SizeT + Sizeof_BareBonesVector + Sizeof_RoaringBitset) -#define Sizeof_GoLabels 16 - -#define Sizeof_SerializedDataIterator 152 - -#define Sizeof_MetricsIterator 24 - -#define Sizeof_SegmentSamplesStorage 80 -#define Sizeof_RemoteWriteMessageEncoder 32 -#define Sizeof_SegmentSamplesStorageListIterator 56 #ifdef __cplusplus extern "C" { #endif From caa02a2cbc94efc20fae6e0485a879a5eb615b68 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Tue, 30 Jun 2026 11:19:30 +0000 Subject: [PATCH 09/31] update namespaces in tests --- pp/entrypoint/bridge/go_constants.cpp | 24 ------------------- pp/entrypoint/types/go_constants_tests.cpp | 2 +- pp/entrypoint/types/loader_tests.cpp | 10 ++++---- pp/entrypoint/types/lss_tests.cpp | 24 +++++++++---------- pp/entrypoint/types/querier_tests.cpp | 22 ++++++++--------- pp/entrypoint/types/serialized_data_tests.cpp | 10 ++++---- 6 files changed, 34 insertions(+), 58 deletions(-) delete mode 100644 pp/entrypoint/bridge/go_constants.cpp diff --git a/pp/entrypoint/bridge/go_constants.cpp b/pp/entrypoint/bridge/go_constants.cpp deleted file mode 100644 index d6b50d2235..0000000000 --- a/pp/entrypoint/bridge/go_constants.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "entrypoint/types/go_constants.h" -#include "entrypoint/types/serialized_data.h" -#include "metrics/storage.h" -#include "prometheus/relabeler.h" -#include "wal/output_decoder.h" -#include "wal/segment_samples_storage.h" - -namespace { - -static_assert(sizeof(std::vector) == Sizeof_StdVector); -static_assert(sizeof(BareBones::Vector) == Sizeof_BareBonesVector); -static_assert(sizeof(roaring::Roaring) == Sizeof_RoaringBitset); - -static_assert(sizeof(PromPP::Prometheus::Relabel::InnerSeries) == Sizeof_InnerSeries); - -static_assert(sizeof(entrypoint::types::SerializedDataIterator) == Sizeof_SerializedDataIterator); - -static_assert(sizeof(metrics::Storage::Iterator) == Sizeof_MetricsIterator); - -static_assert(sizeof(PromPP::WAL::SegmentSamplesStorage) == Sizeof_SegmentSamplesStorage); -static_assert(sizeof(PromPP::WAL::ProtobufEncoder) == Sizeof_RemoteWriteMessageEncoder); -static_assert(sizeof(PromPP::WAL::SegmentSamplesStorageList::Iterator) == Sizeof_SegmentSamplesStorageListIterator); - -} // namespace \ No newline at end of file diff --git a/pp/entrypoint/types/go_constants_tests.cpp b/pp/entrypoint/types/go_constants_tests.cpp index 4bd0d9bd63..73d81dc88b 100644 --- a/pp/entrypoint/types/go_constants_tests.cpp +++ b/pp/entrypoint/types/go_constants_tests.cpp @@ -19,7 +19,7 @@ TEST(GoConstantsTest, CompileTimeSizesMatchConstants) { static_assert(sizeof(PromPP::Prometheus::Relabel::InnerSeries) == Sizeof_InnerSeries); - static_assert(sizeof(entrypoint_types::SerializedDataIterator) == Sizeof_SerializedDataIterator); + static_assert(sizeof(entrypoint::types::SerializedDataIterator) == Sizeof_SerializedDataIterator); static_assert(sizeof(metrics::Storage::Iterator) == Sizeof_MetricsIterator); diff --git a/pp/entrypoint/types/loader_tests.cpp b/pp/entrypoint/types/loader_tests.cpp index 65f8c0d18b..32c4cce5c9 100644 --- a/pp/entrypoint/types/loader_tests.cpp +++ b/pp/entrypoint/types/loader_tests.cpp @@ -10,7 +10,7 @@ namespace { -using entrypoint_types::QueryableEncodingBimap; +using entrypoint::types::QueryableEncodingBimap; using PromPP::Primitives::LabelViewSet; using series_data::DataStorage; using series_data::Decoder; @@ -62,7 +62,7 @@ TEST_F(RevertableLoaderFixture, LoadFinalizeRestoresUnloadedOpenChunk) { unloader_.unload(); // Act - entrypoint_types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 1}; + entrypoint::types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 1}; loader.load_next(stream_.span()); loader.load_finalize(); @@ -77,7 +77,7 @@ TEST_F(RevertableLoaderFixture, RevertRestoresUnloadedOpenChunk) { unloader_.unload(); const auto trimmed_stream = open_chunk_stream(0); - entrypoint_types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 1}; + entrypoint::types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 1}; loader.load_next(stream_.span()); loader.load_finalize(); @@ -98,7 +98,7 @@ TEST_F(RevertableLoaderFixture, LoadFinalizeLoadsSeriesByBatch) { unloader_.create_snapshot(stream_); unloader_.unload(); - entrypoint_types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 2}; + entrypoint::types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 2}; // Act loader.load_next(stream_.span()); @@ -134,7 +134,7 @@ TEST_F(RevertableLoaderFixture, RevertRestoresSeriesLoadedAcrossBatches) { const auto trimmed_stream1 = open_chunk_stream(1); const auto trimmed_stream2 = open_chunk_stream(2); - entrypoint_types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 2}; + entrypoint::types::RevertableLoader loader{storage_, lss_.ls_id_set().begin(), lss_.ls_id_set().end(), 2}; loader.load_next(stream_.span()); loader.load_finalize(); diff --git a/pp/entrypoint/types/lss_tests.cpp b/pp/entrypoint/types/lss_tests.cpp index 4f33e3a640..fde5bd6893 100644 --- a/pp/entrypoint/types/lss_tests.cpp +++ b/pp/entrypoint/types/lss_tests.cpp @@ -12,14 +12,14 @@ namespace { -using entrypoint_types::create_lss; -using entrypoint_types::create_snapshot_lss; -using entrypoint_types::EncodingBimap; -using entrypoint_types::LssType; -using entrypoint_types::QueryableEncodingBimap; -using entrypoint_types::ReallocationsDetector; -using entrypoint_types::ShrinkAwareSnapshotLSS; -using entrypoint_types::SnapshotLSS; +using entrypoint::types::create_lss; +using entrypoint::types::create_snapshot_lss; +using entrypoint::types::EncodingBimap; +using entrypoint::types::LssType; +using entrypoint::types::QueryableEncodingBimap; +using entrypoint::types::ReallocationsDetector; +using entrypoint::types::ShrinkAwareSnapshotLSS; +using entrypoint::types::SnapshotLSS; using PromPP::Primitives::LabelViewSet; TEST(LssTest, CreateLssEncodingBimapSelectsExpectedAlternative) { @@ -77,7 +77,7 @@ class SnapshotLssFixture : public testing::Test { const LabelViewSet ls3_{{"job", "d"}}; const LabelViewSet ls4_{{"job", "e"}}; - entrypoint_types::LssVariantPtr create_queryable_lss() const { + entrypoint::types::LssVariantPtr create_queryable_lss() const { auto lss = create_lss(LssType::kQueryableEncodingBimap); auto& bimap = std::get(*lss); @@ -92,14 +92,14 @@ class SnapshotLssFixture : public testing::Test { return lss; } - entrypoint_types::LssVariantPtr create_fixed_lss() const { + entrypoint::types::LssVariantPtr create_fixed_lss() const { auto lss = create_queryable_lss(); std::get(*lss).set_pending_shrink_boundary(kShrinkBoundary); return lss; } - entrypoint_types::LssVariantPtr create_shrunk_lss() const { + entrypoint::types::LssVariantPtr create_shrunk_lss() const { QueryableEncodingBimap seeded_lss; seeded_lss.find_or_emplace(ls0_); seeded_lss.find_or_emplace(ls1_); @@ -152,7 +152,7 @@ TEST_F(SnapshotLssFixture, FromFixedQueryableLssIsShrinkAware) { const auto snapshot = create_snapshot_lss(*lss); // Assert - EXPECT_TRUE(std::holds_alternative(*snapshot)); + EXPECT_TRUE(std::holds_alternative(*snapshot)); } TEST_F(SnapshotLssFixture, ShrinkAwareResolvesSurvivingPreBoundarySeries) { diff --git a/pp/entrypoint/types/querier_tests.cpp b/pp/entrypoint/types/querier_tests.cpp index 898ead327e..8ee230f346 100644 --- a/pp/entrypoint/types/querier_tests.cpp +++ b/pp/entrypoint/types/querier_tests.cpp @@ -27,7 +27,7 @@ using series_data::encoder::Sample; using series_data::encoder::SampleList; using series_data::unloading::Loader; using series_data::unloading::Unloader; -using InstantQuerierWrapper = entrypoint_types::InstantQuerierWithArgumentsWrapper, std::span>; +using InstantQuerierWrapper = entrypoint::types::InstantQuerierWithArgumentsWrapper, std::span>; using RangeQuery = series_data::querier::Query>; template @@ -60,7 +60,7 @@ class RangeQuerierUninitializedMemoryFixture : public testing::Test { DataStorage storage_; Encoder<> encoder_{storage_}; BareBones::ShrinkedToFitOStringStream unloaded_chunks_; - UninitializedMemory serialized_data_memory_; + UninitializedMemory serialized_data_memory_; RangeQuery query_for(LabelSetID label_set_id, int64_t min, int64_t max) { Slice label_set_ids; @@ -68,7 +68,7 @@ class RangeQuerierUninitializedMemoryFixture : public testing::Test { return RangeQuery{.time_interval{.min = min, .max = max}, .label_set_ids = std::move(label_set_ids)}; } - [[nodiscard]] entrypoint_types::SerializedDataPtr* serialized_data_ptr() noexcept { return serialized_data_memory_.ptr(); } + [[nodiscard]] entrypoint::types::SerializedDataPtr* serialized_data_ptr() noexcept { return serialized_data_memory_.ptr(); } void unload_open_chunks() { Unloader unloader{storage_}; @@ -89,7 +89,7 @@ TEST_F(RangeQuerierUninitializedMemoryFixture, QueryWritesSerializedDataToPrepar encoder_.encode(0, 1, 1.0); auto query = query_for(0, 1, 1); const auto was_default_before_prepare = serialized_data_memory_.has_default_value(); - entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; + entrypoint::types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; // Act wrapper.query(); @@ -110,7 +110,7 @@ TEST_F(RangeQuerierUninitializedMemoryFixture, QueryFinalizeWritesSerializedData auto query = query_for(0, 1, 3); const auto was_default_before_prepare = serialized_data_memory_.has_default_value(); - entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; + entrypoint::types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; // Act wrapper.query(); @@ -208,8 +208,8 @@ class RangeQuerierWrapperFixture : public testing::Test { DataStorage storage_; Encoder<> encoder_{storage_}; BareBones::ShrinkedToFitOStringStream unloaded_chunks_; - UninitializedMemory serialized_data_memory_; - entrypoint_types::SerializedDataPtr serialized_data_; + UninitializedMemory serialized_data_memory_; + entrypoint::types::SerializedDataPtr serialized_data_; RangeQuery query_for(LabelSetID label_set_id, int64_t min, int64_t max) { Slice label_set_ids; @@ -223,7 +223,7 @@ class RangeQuerierWrapperFixture : public testing::Test { return decoded; } - [[nodiscard]] entrypoint_types::SerializedDataPtr* serialized_data_ptr() noexcept { return serialized_data_memory_.ptr(); } + [[nodiscard]] entrypoint::types::SerializedDataPtr* serialized_data_ptr() noexcept { return serialized_data_memory_.ptr(); } void take_serialized_data() { serialized_data_ = std::move(serialized_data_memory_.value()); } @@ -250,7 +250,7 @@ TEST_F(RangeQuerierWrapperFixture, QuerySerializesMatchingOpenChunk) { encoder_.encode(0, 5, 5.0); auto query = query_for(0, 2, 4); - entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; + entrypoint::types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; // Act wrapper.query(); @@ -268,7 +268,7 @@ TEST_F(RangeQuerierWrapperFixture, QuerySerializesEmptyResultWhenSeriesDoesNotMa // Arrange encoder_.encode(0, 10, 10.0); auto query = query_for(0, 1, 5); - entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; + entrypoint::types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; // Act wrapper.query(); @@ -289,7 +289,7 @@ TEST_F(RangeQuerierWrapperFixture, QueryDefersSerializationUntilUnloadedSeriesIs unload_open_chunks(); auto query = query_for(0, 1, 3); - entrypoint_types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; + entrypoint::types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; // Act wrapper.query(); diff --git a/pp/entrypoint/types/serialized_data_tests.cpp b/pp/entrypoint/types/serialized_data_tests.cpp index 442eaaf920..5ace443252 100644 --- a/pp/entrypoint/types/serialized_data_tests.cpp +++ b/pp/entrypoint/types/serialized_data_tests.cpp @@ -27,7 +27,7 @@ class SerializedDataGoFixture : public testing::Test { Encoder<> encoder_{storage_}; Querier querier_{storage_}; - [[nodiscard]] static SampleList decode_chunk(const entrypoint_types::SerializedDataGo& data, uint32_t chunk_id) { + [[nodiscard]] static SampleList decode_chunk(const entrypoint::types::SerializedDataGo& data, uint32_t chunk_id) { SampleList decoded; std::ranges::copy(data.iterator(chunk_id), DecodeIteratorSentinel{}, std::back_inserter(decoded)); return decoded; @@ -38,7 +38,7 @@ TEST_F(SerializedDataGoFixture, EmptyQueriedChunkListProducesNoChunks) { // Arrange // Act - entrypoint_types::SerializedDataGo data{storage_, {}}; + entrypoint::types::SerializedDataGo data{storage_, {}}; // Assert EXPECT_EQ(0U, data.get_chunks_view().size()); @@ -56,7 +56,7 @@ TEST_F(SerializedDataGoFixture, RoundTripsQueriedOpenChunk) { const auto& queried_chunks = querier_.query(Query{.time_interval{.min = 1, .max = 5}, .label_set_ids = {0}}); // Act - entrypoint_types::SerializedDataGo data{storage_, queried_chunks}; + entrypoint::types::SerializedDataGo data{storage_, queried_chunks}; const auto next_series = data.next(); const auto decoded = decode_chunk(data, 0); @@ -80,7 +80,7 @@ TEST_F(SerializedDataGoFixture, RoundTripsQueriedFinalizedChunk) { const auto& queried_chunks = querier_.query(Query{.time_interval{.min = 1, .max = 5}, .label_set_ids = {0}}); // Act - entrypoint_types::SerializedDataGo data{storage_, queried_chunks}; + entrypoint::types::SerializedDataGo data{storage_, queried_chunks}; const auto next_series = data.next(); const auto decoded = decode_chunk(data, 0); @@ -103,7 +103,7 @@ TEST_F(SerializedDataGoFixture, NextReturnsChunkIdsForAllQueriedSeries) { const auto& queried_chunks = querier_.query(Query{.time_interval{.min = 1, .max = 3}, .label_set_ids = {0, 1}}); // Act - entrypoint_types::SerializedDataGo data{storage_, queried_chunks}; + entrypoint::types::SerializedDataGo data{storage_, queried_chunks}; const auto series0 = data.next(); const auto series1 = data.next(); const auto end = data.next(); From 12afebea40c746e3f16803c5d6cf4dcb10df8426 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Wed, 1 Jul 2026 08:36:45 +0000 Subject: [PATCH 10/31] add libclang to docker container --- Dockerfile.ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index 286597085a..5ad8aa88c6 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -44,7 +44,7 @@ RUN set -eux; \ rm -f /tmp/llvm-snapshot.gpg.key; \ echo "deb [signed-by=/usr/share/keyrings/apt-llvm-org.gpg] http://apt.llvm.org/trixie/ llvm-toolchain-trixie-21 main" > /etc/apt/sources.list.d/llvm.list; \ apt-get update; \ - apt-get install -y --no-install-recommends clang-format-21 clang-tidy-21; \ + apt-get install -y --no-install-recommends clang-format-21 clang-tidy-21 libclang-21-dev; \ ln -sf /usr/lib/llvm-21/bin/clang-tidy /usr/bin/clang-tidy; \ ln -sf /usr/lib/llvm-21/bin/clang-format /usr/bin/clang-format; \ update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100; \ From d1b9e6ae522595cbb0cee50cc25588c1abd6327f Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Sun, 5 Jul 2026 17:57:12 +0000 Subject: [PATCH 11/31] init tooling --- pp/tools/entrypoint_codegen/.bazelrc | 1 + pp/tools/entrypoint_codegen/.clangd | 5 + pp/tools/entrypoint_codegen/BUILD | 162 +++++++++ pp/tools/entrypoint_codegen/MODULE.bazel | 10 + pp/tools/entrypoint_codegen/MODULE.bazel.lock | 127 +++++++ pp/tools/entrypoint_codegen/app/argparse.cpp | 157 +++++++++ pp/tools/entrypoint_codegen/app/argparse.h | 17 + pp/tools/entrypoint_codegen/app/main.cpp | 51 +++ pp/tools/entrypoint_codegen/app/run.cpp | 149 ++++++++ pp/tools/entrypoint_codegen/app/run.h | 45 +++ .../entrypoint_codegen/app/runtime_debug.cpp | 26 ++ .../entrypoint_codegen/app/runtime_debug.h | 17 + pp/tools/entrypoint_codegen/build_defs.bzl | 8 + .../clang_adapter/cursor_reader.hpp | 57 ++++ .../clang_adapter/function_extractor.hpp | 276 +++++++++++++++ .../clang_adapter/parse.cpp | 50 +++ .../entrypoint_codegen/clang_adapter/parse.h | 20 ++ .../clang_adapter/session.hpp | 321 ++++++++++++++++++ .../emit/diagnostic_text.cpp | 110 ++++++ .../entrypoint_codegen/emit/diagnostic_text.h | 14 + .../emit/diagnostic_text_tests.cpp | 61 ++++ .../entrypoint_codegen/emit/diagnostics.cpp | 38 +++ .../entrypoint_codegen/emit/diagnostics.h | 11 + .../emit/diagnostics_tests.cpp | 40 +++ pp/tools/entrypoint_codegen/emit/json.cpp | 257 ++++++++++++++ pp/tools/entrypoint_codegen/emit/json.h | 11 + .../entrypoint_codegen/emit/json_tests.cpp | 43 +++ .../facts/entrypoint_facts.cpp | 240 +++++++++++++ .../facts/entrypoint_facts.h | 57 ++++ .../facts/entrypoint_facts_tests.cpp | 65 ++++ pp/tools/entrypoint_codegen/facts/facts.h | 121 +++++++ .../entrypoint_codegen/facts/string_table.cpp | 111 ++++++ .../entrypoint_codegen/facts/string_table.h | 34 ++ .../entrypoint_codegen/facts/tagged_index.h | 27 ++ .../libclang_repository.bzl | 133 ++++++++ .../entrypoint_codegen/validate/validate.cpp | 108 ++++++ .../entrypoint_codegen/validate/validate.h | 9 + .../validate/validate_tests.cpp | 94 +++++ 38 files changed, 3083 insertions(+) create mode 100644 pp/tools/entrypoint_codegen/.bazelrc create mode 100644 pp/tools/entrypoint_codegen/.clangd create mode 100644 pp/tools/entrypoint_codegen/BUILD create mode 100644 pp/tools/entrypoint_codegen/MODULE.bazel create mode 100644 pp/tools/entrypoint_codegen/MODULE.bazel.lock create mode 100644 pp/tools/entrypoint_codegen/app/argparse.cpp create mode 100644 pp/tools/entrypoint_codegen/app/argparse.h create mode 100644 pp/tools/entrypoint_codegen/app/main.cpp create mode 100644 pp/tools/entrypoint_codegen/app/run.cpp create mode 100644 pp/tools/entrypoint_codegen/app/run.h create mode 100644 pp/tools/entrypoint_codegen/app/runtime_debug.cpp create mode 100644 pp/tools/entrypoint_codegen/app/runtime_debug.h create mode 100644 pp/tools/entrypoint_codegen/build_defs.bzl create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/cursor_reader.hpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/function_extractor.hpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/parse.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/parse.h create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/session.hpp create mode 100644 pp/tools/entrypoint_codegen/emit/diagnostic_text.cpp create mode 100644 pp/tools/entrypoint_codegen/emit/diagnostic_text.h create mode 100644 pp/tools/entrypoint_codegen/emit/diagnostic_text_tests.cpp create mode 100644 pp/tools/entrypoint_codegen/emit/diagnostics.cpp create mode 100644 pp/tools/entrypoint_codegen/emit/diagnostics.h create mode 100644 pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp create mode 100644 pp/tools/entrypoint_codegen/emit/json.cpp create mode 100644 pp/tools/entrypoint_codegen/emit/json.h create mode 100644 pp/tools/entrypoint_codegen/emit/json_tests.cpp create mode 100644 pp/tools/entrypoint_codegen/facts/entrypoint_facts.cpp create mode 100644 pp/tools/entrypoint_codegen/facts/entrypoint_facts.h create mode 100644 pp/tools/entrypoint_codegen/facts/entrypoint_facts_tests.cpp create mode 100644 pp/tools/entrypoint_codegen/facts/facts.h create mode 100644 pp/tools/entrypoint_codegen/facts/string_table.cpp create mode 100644 pp/tools/entrypoint_codegen/facts/string_table.h create mode 100644 pp/tools/entrypoint_codegen/facts/tagged_index.h create mode 100644 pp/tools/entrypoint_codegen/libclang_repository.bzl create mode 100644 pp/tools/entrypoint_codegen/validate/validate.cpp create mode 100644 pp/tools/entrypoint_codegen/validate/validate.h create mode 100644 pp/tools/entrypoint_codegen/validate/validate_tests.cpp diff --git a/pp/tools/entrypoint_codegen/.bazelrc b/pp/tools/entrypoint_codegen/.bazelrc new file mode 100644 index 0000000000..c1abb89e92 --- /dev/null +++ b/pp/tools/entrypoint_codegen/.bazelrc @@ -0,0 +1 @@ +common --noenable_workspace diff --git a/pp/tools/entrypoint_codegen/.clangd b/pp/tools/entrypoint_codegen/.clangd new file mode 100644 index 0000000000..6b8a7e66b9 --- /dev/null +++ b/pp/tools/entrypoint_codegen/.clangd @@ -0,0 +1,5 @@ +CompileFlags: + Add: + - "-std=c++2b" + - "-I/workspaces/prompp/pp/tools/entrypoint_codegen" + - "-isystem/usr/lib/llvm-21/include" diff --git a/pp/tools/entrypoint_codegen/BUILD b/pp/tools/entrypoint_codegen/BUILD new file mode 100644 index 0000000000..98118eff73 --- /dev/null +++ b/pp/tools/entrypoint_codegen/BUILD @@ -0,0 +1,162 @@ +load(":build_defs.bzl", "entrypoint_codegen_copts") + +package(default_visibility = ["//visibility:private"]) + +ENTRYPOINT_CODEGEN_COPTS = entrypoint_codegen_copts() + +cc_library( + name = "facts", + srcs = [ + "facts/entrypoint_facts.cpp", + "facts/string_table.cpp", + ], + hdrs = [ + "facts/entrypoint_facts.h", + "facts/facts.h", + "facts/string_table.h", + "facts/tagged_index.h", + ], + copts = ENTRYPOINT_CODEGEN_COPTS, +) + +cc_test( + name = "entrypoint_facts_test", + srcs = ["facts/entrypoint_facts_tests.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":facts", + "@gtest//:gtest_main", + ], +) + +cc_library( + name = "clang_adapter", + srcs = [ + "clang_adapter/cursor_reader.hpp", + "clang_adapter/function_extractor.hpp", + "clang_adapter/parse.cpp", + "clang_adapter/session.hpp", + ], + hdrs = [ + "clang_adapter/parse.h", + ], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":facts", + "@system_libclang//:libclang", + ], +) + +cc_library( + name = "validate", + srcs = ["validate/validate.cpp"], + hdrs = ["validate/validate.h"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [":facts"], +) + +cc_test( + name = "validate_test", + srcs = ["validate/validate_tests.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":facts", + ":validate", + "@gtest//:gtest_main", + ], +) + +cc_library( + name = "diagnostic_text", + srcs = ["emit/diagnostic_text.cpp"], + hdrs = ["emit/diagnostic_text.h"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":facts", + ], +) + +cc_test( + name = "diagnostic_text_test", + srcs = ["emit/diagnostic_text_tests.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":diagnostic_text", + ":facts", + "@gtest//:gtest_main", + ], +) + +cc_library( + name = "emit_json", + srcs = ["emit/json.cpp"], + hdrs = ["emit/json.h"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":diagnostic_text", + ":facts", + ], +) + +cc_test( + name = "emit_json_test", + srcs = ["emit/json_tests.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":emit_json", + ":facts", + "@gtest//:gtest_main", + ], +) + +cc_library( + name = "emit_diagnostics", + srcs = ["emit/diagnostics.cpp"], + hdrs = ["emit/diagnostics.h"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":diagnostic_text", + ":facts", + ], +) + +cc_test( + name = "emit_diagnostics_test", + srcs = ["emit/diagnostics_tests.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":emit_diagnostics", + ":facts", + "@gtest//:gtest_main", + ], +) + +cc_library( + name = "app", + srcs = [ + "app/argparse.cpp", + "app/run.cpp", + "app/runtime_debug.cpp", + ], + hdrs = [ + "app/argparse.h", + "app/run.h", + "app/runtime_debug.h", + ], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":clang_adapter", + ":emit_diagnostics", + ":emit_json", + ":facts", + ":validate", + ], +) + +cc_binary( + name = "entrypoint_codegen", + srcs = ["app/main.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + visibility = ["//visibility:public"], + deps = [":app"], +) diff --git a/pp/tools/entrypoint_codegen/MODULE.bazel b/pp/tools/entrypoint_codegen/MODULE.bazel new file mode 100644 index 0000000000..b9a92ba979 --- /dev/null +++ b/pp/tools/entrypoint_codegen/MODULE.bazel @@ -0,0 +1,10 @@ +module(name = "entrypoint_codegen") + +bazel_dep( + name = "googletest", + repo_name = "gtest", + version = "1.11.0", +) + +libclang = use_extension("//:libclang_repository.bzl", "libclang_extension") +use_repo(libclang, "system_libclang") diff --git a/pp/tools/entrypoint_codegen/MODULE.bazel.lock b/pp/tools/entrypoint_codegen/MODULE.bazel.lock new file mode 100644 index 0000000000..8f577d057c --- /dev/null +++ b/pp/tools/entrypoint_codegen/MODULE.bazel.lock @@ -0,0 +1,127 @@ +{ + "lockFileVersion": 11, + "registryFileHashes": { + "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", + "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", + "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/source.json": "7e3a9adf473e9af076ae485ed649d5641ad50ec5c11718103f34de03170d94ad", + "https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel": "50341a62efbc483e8a2a6aec30994a58749bd7b885e18dd96aa8c33031e558ef", + "https://bcr.bazel.build/modules/apple_support/1.5.0/source.json": "eb98a7627c0bc486b57f598ad8da50f6625d974c8f723e9ea71bd39f709c9862", + "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", + "https://bcr.bazel.build/modules/bazel_features/1.11.0/source.json": "c9320aa53cd1c441d24bd6b716da087ad7e4ff0d9742a9884587596edfe53015", + "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", + "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", + "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", + "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/source.json": "082ed5f9837901fada8c68c2f3ddc958bb22b6d654f71dd73f3df30d45d4b749", + "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", + "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", + "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", + "https://bcr.bazel.build/modules/googletest/1.11.0/source.json": "c73d9ef4268c91bd0c1cd88f1f9dfa08e814b1dbe89b5f594a9f08ba0244d206", + "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", + "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", + "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", + "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", + "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", + "https://bcr.bazel.build/modules/platforms/0.0.9/source.json": "cd74d854bf16a9e002fb2ca7b1a421f4403cda29f824a765acd3a8c56f8d43e6", + "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", + "https://bcr.bazel.build/modules/protobuf/21.7/source.json": "bbe500720421e582ff2d18b0802464205138c06056f443184de39fbb8187b09b", + "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", + "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", + "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", + "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", + "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", + "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", + "https://bcr.bazel.build/modules/rules_cc/0.0.9/source.json": "1f1ba6fea244b616de4a554a0f4983c91a9301640c8fe0dd1d410254115c8430", + "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", + "https://bcr.bazel.build/modules/rules_java/7.6.5/MODULE.bazel": "481164be5e02e4cab6e77a36927683263be56b7e36fef918b458d7a8a1ebadb1", + "https://bcr.bazel.build/modules/rules_java/7.6.5/source.json": "a805b889531d1690e3c72a7a7e47a870d00323186a9904b36af83aa3d053ee8d", + "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", + "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/source.json": "a075731e1b46bc8425098512d038d416e966ab19684a10a34f4741295642fc35", + "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", + "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", + "https://bcr.bazel.build/modules/rules_license/0.0.7/source.json": "355cc5737a0f294e560d52b1b7a6492d4fff2caf0bef1a315df5a298fca2d34a", + "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", + "https://bcr.bazel.build/modules/rules_pkg/0.7.0/source.json": "c2557066e0c0342223ba592510ad3d812d4963b9024831f7f66fd0584dd8c66c", + "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", + "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", + "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/source.json": "d57902c052424dfda0e71646cb12668d39c4620ee0544294d9d941e7d12bc3a9", + "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", + "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel": "26114f0c0b5e93018c0c066d6673f1a2c3737c7e90af95eff30cfee38d0bbac7", + "https://bcr.bazel.build/modules/rules_python/0.22.1/source.json": "57226905e783bae7c37c2dd662be078728e48fa28ee4324a7eabcafb5a43d014", + "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", + "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", + "https://bcr.bazel.build/modules/stardoc/0.5.1/source.json": "a96f95e02123320aa015b956f29c00cb818fa891ef823d55148e1a362caacf29", + "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", + "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/source.json": "f1ef7d3f9e0e26d4b23d1c39b5f5de71f584dd7d1b4ef83d9bbba6ec7a6a6459", + "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", + "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d" + }, + "selectedYankedVersions": {}, + "moduleExtensions": { + "//:libclang_repository.bzl%libclang_extension": { + "general": { + "bzlTransitiveDigest": "QhHM+hE3cYM+CNga0q6YabsxuePowVG61SE5NQ+/3Xk=", + "usagesDigest": "9+KLxb6yQ4k6Qd6N9mM+l30Tc/TYMc0kY0YZDcH4CEo=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "system_libclang": { + "bzlFile": "@@//:libclang_repository.bzl", + "ruleClassName": "libclang_repository", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [] + } + }, + "@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": { + "general": { + "bzlTransitiveDigest": "PjIds3feoYE8SGbbIq2SFTZy3zmxeO2tQevJZNDo7iY=", + "usagesDigest": "+hz7IHWN6A1oVJJWNDB6yZRG+RYhF76wAYItpAeIUIg=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_apple_cc_toolchains": { + "bzlFile": "@@apple_support~//crosstool:setup.bzl", + "ruleClassName": "_apple_cc_autoconf_toolchains", + "attributes": {} + }, + "local_config_apple_cc": { + "bzlFile": "@@apple_support~//crosstool:setup.bzl", + "ruleClassName": "_apple_cc_autoconf", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "apple_support~", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@platforms//host:extension.bzl%host_platform": { + "general": { + "bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=", + "usagesDigest": "pCYpDQmqMbmiiPI1p2Kd3VLm5T48rRAht5WdW0X2GlA=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "host_platform": { + "bzlFile": "@@platforms//host:extension.bzl", + "ruleClassName": "host_platform_repo", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [] + } + } + } +} diff --git a/pp/tools/entrypoint_codegen/app/argparse.cpp b/pp/tools/entrypoint_codegen/app/argparse.cpp new file mode 100644 index 0000000000..89cd631ea7 --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/argparse.cpp @@ -0,0 +1,157 @@ +#include "app/argparse.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace entrypoint_codegen::app { + +namespace { + +bool has_cpp_extension(const std::filesystem::path& path) { + const std::string extension = path.extension().string(); + return extension == ".cpp" || extension == ".cc" || extension == ".cxx"; +} + +std::vector collect_input_files(const std::vector& inputs) { + std::vector files; + for (const std::filesystem::path& input : inputs) { + if (!std::filesystem::exists(input)) { + continue; + } + if (std::filesystem::is_regular_file(input)) { + if (has_cpp_extension(input)) { + files.push_back(std::filesystem::absolute(input).lexically_normal()); + } + continue; + } + if (std::filesystem::is_directory(input)) { + for (const auto& entry : std::filesystem::directory_iterator(input)) { + if (entry.is_regular_file() && has_cpp_extension(entry.path())) { + files.push_back(std::filesystem::absolute(entry.path()).lexically_normal()); + } + } + } + } + + std::sort(files.begin(), files.end()); + files.erase(std::unique(files.begin(), files.end()), files.end()); + return files; +} + +void append_clang_args_file(const std::filesystem::path& path, std::vector& clang_args) { + std::ifstream file(path); + if (!file) { + throw std::runtime_error("unable to open clang args file: " + path.string()); + } + for (std::string line; std::getline(file, line);) { + if (!line.empty()) { + clang_args.push_back(std::move(line)); + } + } +} + +OutputMode parse_output_mode(std::string_view value) { + if (value == "json") { + return OutputMode::kJson; + } + if (value == "lint") { + return OutputMode::kLint; + } + if (value == "check" || value == "none") { + return OutputMode::kCheck; + } + throw std::runtime_error("unknown output mode: " + std::string(value)); +} + +} // namespace + +void write_help(std::ostream& out) { + out << "entrypoint_codegen [options] [...] -- [...]\n"; + out << " --source=PATH Additional source path, file or directory.\n"; + out << " --input=PATH Alias for --source=PATH.\n"; + out << " --output=PATH JSON output path. Defaults to ./entrypoint_facts.json.\n"; + out << " --output-dir=PATH Directory for entrypoint_facts.json.\n"; + out << " --mode=json|lint|check Output mode. Defaults to json.\n"; + out << " --format=json|lint|none Alias for --mode; none means check.\n"; + out << " --no-output Alias for --mode=check.\n"; + out << " --runtime-debug Append runtime debug diagnostics.\n"; + out << " --clang-arg=ARG Additional clang parser argument, repeatable.\n"; + out << " --extra-arg=ARG Alias for --clang-arg=ARG.\n"; + out << " --clang-args-file=PATH File with one clang parser argument per line.\n"; + out << " -- Treat remaining arguments as clang parser arguments.\n"; +} + +CliOptions parse_arguments(int argc, char** argv) { + std::vector inputs; + CliOptions options; + options.run_options.output_path = std::filesystem::current_path() / "entrypoint_facts.json"; + + for (int i = 1; i < argc; ++i) { + const std::string arg = argv[i]; + if (arg == "--help" || arg == "-h") { + options.help = true; + return options; + } + if (arg.rfind("--output=", 0) == 0) { + options.run_options.output_path = arg.substr(std::string("--output=").size()); + continue; + } + if (arg.rfind("--output-dir=", 0) == 0) { + options.run_options.output_path = std::filesystem::path(arg.substr(std::string("--output-dir=").size())) / "entrypoint_facts.json"; + continue; + } + if (arg.rfind("--mode=", 0) == 0) { + options.run_options.output_mode = parse_output_mode(arg.substr(std::string("--mode=").size())); + continue; + } + if (arg.rfind("--format=", 0) == 0) { + options.run_options.output_mode = parse_output_mode(arg.substr(std::string("--format=").size())); + continue; + } + if (arg == "--no-output") { + options.run_options.output_mode = OutputMode::kCheck; + continue; + } + if (arg == "--runtime-debug") { + options.run_options.runtime_debug = true; + continue; + } + if (arg.rfind("--clang-arg=", 0) == 0) { + options.run_options.clang_args.push_back(arg.substr(std::string("--clang-arg=").size())); + continue; + } + if (arg.rfind("--extra-arg=", 0) == 0) { + options.run_options.clang_args.push_back(arg.substr(std::string("--extra-arg=").size())); + continue; + } + if (arg.rfind("--clang-args-file=", 0) == 0) { + append_clang_args_file(arg.substr(std::string("--clang-args-file=").size()), options.run_options.clang_args); + continue; + } + if (arg.rfind("--source=", 0) == 0) { + inputs.emplace_back(arg.substr(std::string("--source=").size())); + continue; + } + if (arg.rfind("--input=", 0) == 0) { + inputs.emplace_back(arg.substr(std::string("--input=").size())); + continue; + } + if (arg == "--") { + for (++i; i < argc; ++i) { + options.run_options.clang_args.emplace_back(argv[i]); + } + break; + } + inputs.emplace_back(arg); + } + + options.run_options.source_files = collect_input_files(inputs); + return options; +} + +} // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/argparse.h b/pp/tools/entrypoint_codegen/app/argparse.h new file mode 100644 index 0000000000..cc11f4318d --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/argparse.h @@ -0,0 +1,17 @@ +#pragma once + +#include "app/run.h" + +#include + +namespace entrypoint_codegen::app { + +struct CliOptions { + RunOptions run_options; + bool help = false; +}; + +CliOptions parse_arguments(int argc, char** argv); +void write_help(std::ostream& out); + +} // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/main.cpp b/pp/tools/entrypoint_codegen/app/main.cpp new file mode 100644 index 0000000000..c8248700ec --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/main.cpp @@ -0,0 +1,51 @@ +#include "app/argparse.h" +#include "app/run.h" + +#include +#include + +int main(int argc, char** argv) { + namespace app = entrypoint_codegen::app; + + app::CliOptions cli_options; + try { + cli_options = app::parse_arguments(argc, argv); + } catch (const std::exception& error) { + std::cerr << "entrypoint_codegen failed: " << error.what() << "\n"; + return 1; + } + + if (cli_options.help) { + app::write_help(std::cout); + return 0; + } + + if (cli_options.run_options.source_files.empty()) { + app::write_help(std::cout); + return 1; + } + + cli_options.run_options.diagnostics_output = &std::cout; + + app::RunReport report; + try { + report = app::run(cli_options.run_options); + } catch (const std::exception& error) { + std::cerr << "entrypoint_codegen failed: " << error.what() << "\n"; + return 1; + } + + if (cli_options.run_options.output_mode == app::OutputMode::kJson) { + std::cout << "Wrote " << cli_options.run_options.output_path << "\n"; + } + + if (report.decision == app::ExitDecision::kAnalysisFailed) { + std::cerr << "entrypoint_codegen found " << report.error_count << " error diagnostics"; + if (report.warning_count != 0 || report.info_count != 0) { + std::cerr << " (" << report.warning_count << " warnings, " << report.info_count << " info)"; + } + std::cerr << "\n"; + return 2; + } + return 0; +} \ No newline at end of file diff --git a/pp/tools/entrypoint_codegen/app/run.cpp b/pp/tools/entrypoint_codegen/app/run.cpp new file mode 100644 index 0000000000..743e1684b9 --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/run.cpp @@ -0,0 +1,149 @@ +#include "app/run.h" + +#include "app/runtime_debug.h" +#include "clang_adapter/parse.h" +#include "emit/diagnostics.h" +#include "emit/json.h" +#include "validate/validate.h" + +#include +#include +#include +#include +#include + +namespace entrypoint_codegen::app { + +namespace { + +class tracking_memory_resource : public std::pmr::memory_resource { + public: + explicit tracking_memory_resource(std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) + : upstream_(upstream) {} + + [[nodiscard]] size_t allocated_bytes() const noexcept { return allocated_bytes_; } + [[nodiscard]] size_t deallocated_bytes() const noexcept { return deallocated_bytes_; } + [[nodiscard]] size_t peak_live_bytes() const noexcept { return peak_live_bytes_; } + + private: + void* do_allocate(size_t bytes, size_t alignment) override { + void* result = upstream_->allocate(bytes, alignment); + allocated_bytes_ += bytes; + live_bytes_ += bytes; + if (live_bytes_ > peak_live_bytes_) { + peak_live_bytes_ = live_bytes_; + } + return result; + } + + void do_deallocate(void* pointer, size_t bytes, size_t alignment) override { + upstream_->deallocate(pointer, bytes, alignment); + deallocated_bytes_ += bytes; + live_bytes_ -= bytes; + } + + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { + return this == &other; + } + + std::pmr::memory_resource* upstream_; + size_t allocated_bytes_ = 0; + size_t deallocated_bytes_ = 0; + size_t live_bytes_ = 0; + size_t peak_live_bytes_ = 0; +}; + +struct DiagnosticCounts { + uint32_t total = 0; + uint32_t errors = 0; + uint32_t warnings = 0; + uint32_t infos = 0; +}; + +DiagnosticCounts count_diagnostics(const facts::EntrypointFacts& facts) { + DiagnosticCounts counts; + for (const facts::Diagnostic& diagnostic : facts.diagnostics()) { + ++counts.total; + switch (diagnostic.severity) { + case facts::Severity::kInfo: { + ++counts.infos; + break; + } + case facts::Severity::kWarning: { + ++counts.warnings; + break; + } + case facts::Severity::kError: { + ++counts.errors; + break; + } + } + } + return counts; +} + +void write_json_output(const RunOptions& options, const facts::EntrypointFacts& facts) { + if (options.output_path.has_parent_path()) { + std::filesystem::create_directories(options.output_path.parent_path()); + } + + std::ofstream output(options.output_path, std::ios::trunc); + if (!output) { + throw std::runtime_error("failed to open output file: " + options.output_path.string()); + } + emit::write_json(output, facts); +} + +void write_lint_output(const RunOptions& options, const facts::EntrypointFacts& facts) { + std::ostream& output = options.diagnostics_output == nullptr ? std::cout : *options.diagnostics_output; + emit::write_diagnostics(output, facts); +} + +} // namespace + +RunReport run(const RunOptions& options) { + tracking_memory_resource memory_resource; + facts::EntrypointFacts facts = clang_adapter::parse_files(clang_adapter::ParseOptions{ + .source_files = options.source_files, + .clang_args = options.clang_args, + .memory_resource = &memory_resource, + }); + + validate::validate_entrypoints(facts); + + if (options.runtime_debug) { + append_runtime_debug_diagnostics(facts, RuntimeDebugSnapshot{ + .allocated_bytes = memory_resource.allocated_bytes(), + .deallocated_bytes = memory_resource.deallocated_bytes(), + .peak_live_bytes = memory_resource.peak_live_bytes(), + }); + } + + switch (options.output_mode) { + case OutputMode::kJson: { + write_json_output(options, facts); + break; + } + case OutputMode::kLint: { + write_lint_output(options, facts); + break; + } + case OutputMode::kCheck: { + break; + } + } + + const DiagnosticCounts diagnostic_counts = count_diagnostics(facts); + return RunReport{ + .decision = diagnostic_counts.errors == 0 ? ExitDecision::kSuccess : ExitDecision::kAnalysisFailed, + .diagnostic_count = diagnostic_counts.total, + .error_count = diagnostic_counts.errors, + .warning_count = diagnostic_counts.warnings, + .info_count = diagnostic_counts.infos, + .app_allocated_bytes = memory_resource.allocated_bytes(), + .app_deallocated_bytes = memory_resource.deallocated_bytes(), + .app_peak_live_bytes = memory_resource.peak_live_bytes(), + }; +} + +} // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/run.h b/pp/tools/entrypoint_codegen/app/run.h new file mode 100644 index 0000000000..470f585c2d --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/run.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace entrypoint_codegen::app { + +enum class OutputMode : uint8_t { + kJson, + kLint, + kCheck, +}; + +struct RunOptions { + std::vector source_files; + std::vector clang_args; + std::filesystem::path output_path; + OutputMode output_mode = OutputMode::kJson; + bool runtime_debug = false; + std::ostream* diagnostics_output = nullptr; +}; + +enum class ExitDecision : uint8_t { + kSuccess, + kAnalysisFailed, +}; + +struct RunReport { + ExitDecision decision; + uint32_t diagnostic_count; + uint32_t error_count; + uint32_t warning_count; + uint32_t info_count; + size_t app_allocated_bytes; + size_t app_deallocated_bytes; + size_t app_peak_live_bytes; +}; + +RunReport run(const RunOptions& options); + +} // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug.cpp new file mode 100644 index 0000000000..31497d4f7b --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/runtime_debug.cpp @@ -0,0 +1,26 @@ +#include "app/runtime_debug.h" + +#include +#include + +namespace entrypoint_codegen::app { + +void append_runtime_debug_diagnostics(facts::EntrypointFacts& facts, RuntimeDebugSnapshot snapshot) { + const facts::SourceFileId source_file = facts.add_source_file(""); + const std::string message = "App PMR allocations: allocated=" + std::to_string(snapshot.allocated_bytes) + + " deallocated=" + std::to_string(snapshot.deallocated_bytes) + + " peak_live=" + std::to_string(snapshot.peak_live_bytes) + " bytes"; + facts.add_diagnostic(facts::Diagnostic{ + .code = facts::DiagnosticCode::kRuntimeMemoryUsage, + .message = facts.add_string(message), + .severity = facts::Severity::kInfo, + .function = std::nullopt, + .location = facts::SourceLocation{ + .file = source_file, + .line = 0, + .column = 0, + }, + }); +} + +} // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.h b/pp/tools/entrypoint_codegen/app/runtime_debug.h new file mode 100644 index 0000000000..601366c76e --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/runtime_debug.h @@ -0,0 +1,17 @@ +#pragma once + +#include "facts/entrypoint_facts.h" + +#include + +namespace entrypoint_codegen::app { + +struct RuntimeDebugSnapshot { + size_t allocated_bytes; + size_t deallocated_bytes; + size_t peak_live_bytes; +}; + +void append_runtime_debug_diagnostics(facts::EntrypointFacts& facts, RuntimeDebugSnapshot snapshot); + +} // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/build_defs.bzl b/pp/tools/entrypoint_codegen/build_defs.bzl new file mode 100644 index 0000000000..f9ff3a8584 --- /dev/null +++ b/pp/tools/entrypoint_codegen/build_defs.bzl @@ -0,0 +1,8 @@ +def entrypoint_codegen_copts(): + include_root = native.package_name() + if include_root == "": + include_root = "." + return [ + "-std=c++2b", + "-I" + include_root, + ] diff --git a/pp/tools/entrypoint_codegen/clang_adapter/cursor_reader.hpp b/pp/tools/entrypoint_codegen/clang_adapter/cursor_reader.hpp new file mode 100644 index 0000000000..e7cd33e719 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/cursor_reader.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "facts/entrypoint_facts.h" +#include "facts/facts.h" + +#include + +#include + +namespace entrypoint_codegen::clang_adapter { + +inline facts::StringId add_cursor_spelling(facts::EntrypointFacts& facts, CXCursor cursor) { + CXString spelling = clang_getCursorSpelling(cursor); + const char* raw = clang_getCString(spelling); + const facts::StringId id = facts.add_string(raw == nullptr ? std::string_view() : std::string_view(raw)); + clang_disposeString(spelling); + return id; +} + +inline facts::StringId add_type_spelling(facts::EntrypointFacts& facts, CXType type) { + CXString spelling = clang_getTypeSpelling(type); + const char* raw = clang_getCString(spelling); + const facts::StringId id = facts.add_string(raw == nullptr ? std::string_view() : std::string_view(raw)); + clang_disposeString(spelling); + return id; +} + +inline facts::StringId add_cursor_raw_comment(facts::EntrypointFacts& facts, CXCursor cursor) { + CXString comment = clang_Cursor_getRawCommentText(cursor); + const char* raw = clang_getCString(comment); + const facts::StringId id = facts.add_string(raw == nullptr ? std::string_view() : std::string_view(raw)); + clang_disposeString(comment); + return id; +} + +inline bool cursor_spelling_equals(CXCursor cursor, std::string_view expected) { + CXString spelling = clang_getCursorSpelling(cursor); + const char* raw = clang_getCString(spelling); + const bool result = (raw == nullptr ? std::string_view() : std::string_view(raw)) == expected; + clang_disposeString(spelling); + return result; +} + +inline bool cursor_spelling_starts_with(CXCursor cursor, std::string_view prefix) { + CXString spelling = clang_getCursorSpelling(cursor); + const char* raw = clang_getCString(spelling); + const std::string_view view = raw == nullptr ? std::string_view() : std::string_view(raw); + const bool result = view.substr(0, prefix.size()) == prefix; + clang_disposeString(spelling); + return result; +} + +inline bool has_c_linkage(CXCursor function_cursor) { + return clang_getCursorLanguage(function_cursor) == CXLanguage_C; +} + +} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/function_extractor.hpp b/pp/tools/entrypoint_codegen/clang_adapter/function_extractor.hpp new file mode 100644 index 0000000000..06f71cc909 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/function_extractor.hpp @@ -0,0 +1,276 @@ +#pragma once + +#include "clang_adapter/cursor_reader.hpp" +#include "clang_adapter/session.hpp" +#include "facts/facts.h" + +#include + +#include +#include + +namespace entrypoint_codegen::clang_adapter { + +struct ParamNameAndRole { + facts::StringId name; + facts::ParamRole role; +}; + +inline facts::ParamRole param_role_for(std::string_view name) { + if (name == "args") { + return facts::ParamRole::kArgs; + } + if (name == "res") { + return facts::ParamRole::kRes; + } + return facts::ParamRole::kOther; +} + +inline ParamNameAndRole add_param_name_and_role(facts::EntrypointFacts& facts, CXCursor cursor) { + CXString spelling = clang_getCursorSpelling(cursor); + const char* raw = clang_getCString(spelling); + const std::string_view name = raw == nullptr ? std::string_view() : std::string_view(raw); + const ParamNameAndRole result{ + .name = facts.add_string(name), + .role = param_role_for(name), + }; + clang_disposeString(spelling); + return result; +} + +inline facts::BridgeKind bridge_kind_from_annotation(std::string_view annotation) { + if (annotation == "prompp.entrypoint.cgo") { + return facts::BridgeKind::kCGo; + } + if (annotation == "prompp.entrypoint.fastcgo") { + return facts::BridgeKind::kFastCGo; + } + return facts::BridgeKind::kUnknown; +} + +inline facts::BridgeKind bridge_kind_from_annotation_cursor(CXCursor cursor) { + CXString spelling = clang_getCursorSpelling(cursor); + const char* raw = clang_getCString(spelling); + const facts::BridgeKind bridge_kind = bridge_kind_from_annotation(raw == nullptr ? std::string_view() : std::string_view(raw)); + clang_disposeString(spelling); + return bridge_kind; +} + +class FunctionExtractor { + public: + explicit FunctionExtractor(ParseSession& session) : session_(session) {} + + [[nodiscard]] bool is_candidate_function(CXCursor function_cursor) const { + return has_c_linkage(function_cursor) || cursor_spelling_starts_with(function_cursor, "prompp_"); + } + + void add_function(CXCursor function_cursor) { + std::pmr::vector params = extract_params(function_cursor); + + const facts::BridgeKind bridge_kind = extract_bridge_kind(function_cursor); + std::pmr::vector layouts(session_.memory_resource()); + if (bridge_kind == facts::BridgeKind::kFastCGo) { + layouts = extract_layouts(function_cursor); + } + + const CXType function_type = clang_getCursorType(function_cursor); + session_.facts().add_function(facts::FunctionDecl{ + .name = add_cursor_spelling(session_.facts(), function_cursor), + .return_type_spelling = add_type_spelling(session_.facts(), clang_getResultType(function_type)), + .documentation = add_cursor_raw_comment(session_.facts(), function_cursor), + .bridge_kind = bridge_kind, + .params = session_.facts().add_params(params), + .layouts = session_.facts().add_layouts(layouts), + .location = session_.source_location_for(clang_getCursorLocation(function_cursor)), + .has_c_linkage = has_c_linkage(function_cursor), + }); + } + + private: + [[nodiscard]] facts::BridgeKind extract_bridge_kind(CXCursor function_cursor) { + struct BridgeVisitorState { + facts::BridgeKind bridge_kind = facts::BridgeKind::kUnknown; + } visitor_state; + + clang_visitChildren( + function_cursor, + [](CXCursor cursor, CXCursor /*parent*/, CXClientData data) { + if (clang_getCursorKind(cursor) != CXCursor_AnnotateAttr) { + return CXChildVisit_Continue; + } + + auto& state = *static_cast(data); + const facts::BridgeKind next = bridge_kind_from_annotation_cursor(cursor); + if (next != facts::BridgeKind::kUnknown) { + state.bridge_kind = next; + return CXChildVisit_Break; + } + return CXChildVisit_Continue; + }, + &visitor_state); + + return visitor_state.bridge_kind; + } + + [[nodiscard]] std::pmr::vector extract_params(CXCursor function_cursor) { + std::pmr::vector params(session_.memory_resource()); + const int count = clang_Cursor_getNumArguments(function_cursor); + if (count <= 0) { + return params; + } + + params.reserve(static_cast(count)); + for (int i = 0; i < count; ++i) { + const CXCursor arg = clang_Cursor_getArgument(function_cursor, i); + const ParamNameAndRole name_and_role = add_param_name_and_role(session_.facts(), arg); + params.push_back(facts::ParamDecl{ + .name = name_and_role.name, + .type_spelling = add_type_spelling(session_.facts(), clang_getCursorType(arg)), + .role = name_and_role.role, + .location = session_.source_location_for(clang_getCursorLocation(arg)), + }); + } + return params; + } + + [[nodiscard]] std::pmr::vector extract_layouts(CXCursor function_cursor) { + std::pmr::vector layouts(session_.memory_resource()); + struct LayoutVisitorState { + FunctionExtractor& extractor; + std::pmr::vector& layouts; + } visitor_state{ + .extractor = *this, + .layouts = layouts, + }; + + clang_visitChildren( + function_cursor, + [](CXCursor cursor, CXCursor /*parent*/, CXClientData data) { + auto& state = *static_cast(data); + if (state.extractor.try_append_layout(cursor, state.layouts)) { + return CXChildVisit_Continue; + } + return CXChildVisit_Recurse; + }, + &visitor_state); + return layouts; + } + + [[nodiscard]] std::pmr::vector extract_fields(CXCursor struct_cursor) { + std::pmr::vector fields(session_.memory_resource()); + struct FieldVisitorState { + FunctionExtractor& extractor; + std::pmr::vector& fields; + } visitor_state{ + .extractor = *this, + .fields = fields, + }; + + clang_visitChildren( + struct_cursor, + [](CXCursor cursor, CXCursor /*parent*/, CXClientData data) { + if (clang_getCursorKind(cursor) != CXCursor_FieldDecl) { + return CXChildVisit_Continue; + } + + auto& state = *static_cast(data); + state.fields.push_back(facts::FieldDecl{ + .name = add_cursor_spelling(state.extractor.session_.facts(), cursor), + .type_spelling = add_type_spelling(state.extractor.session_.facts(), clang_getCursorType(cursor)), + .location = state.extractor.session_.source_location_for(clang_getCursorLocation(cursor)), + }); + return CXChildVisit_Continue; + }, + &visitor_state); + return fields; + } + + [[nodiscard]] std::pmr::vector extract_fields_from_alias(CXCursor alias_cursor) { + std::pmr::vector fields(session_.memory_resource()); + struct AliasVisitorState { + FunctionExtractor& extractor; + std::pmr::vector& fields; + bool found = false; + } visitor_state{ + .extractor = *this, + .fields = fields, + }; + + clang_visitChildren( + alias_cursor, + [](CXCursor cursor, CXCursor /*parent*/, CXClientData data) { + auto& state = *static_cast(data); + if (clang_getCursorKind(cursor) == CXCursor_StructDecl) { + state.fields = state.extractor.extract_fields(cursor); + state.found = true; + return CXChildVisit_Break; + } + return CXChildVisit_Recurse; + }, + &visitor_state); + + if (visitor_state.found) { + return fields; + } + + const CXType canonical_type = clang_getCanonicalType(clang_getCursorType(alias_cursor)); + const CXCursor declaration = clang_getTypeDeclaration(canonical_type); + if (!clang_Cursor_isNull(declaration) && clang_getCursorKind(declaration) == CXCursor_StructDecl) { + return extract_fields(declaration); + } + return fields; + } + + void append_struct_layout(CXCursor struct_cursor, facts::LayoutKind kind, std::pmr::vector& layouts) { + std::pmr::vector fields = extract_fields(struct_cursor); + layouts.push_back(facts::LayoutDecl{ + .kind = kind, + .fields = session_.facts().add_fields(fields), + .location = session_.source_location_for(clang_getCursorLocation(struct_cursor)), + }); + } + + void append_alias_layout(CXCursor alias_cursor, facts::LayoutKind kind, std::pmr::vector& layouts) { + std::pmr::vector fields = extract_fields_from_alias(alias_cursor); + if (fields.empty()) { + return; + } + + layouts.push_back(facts::LayoutDecl{ + .kind = kind, + .fields = session_.facts().add_fields(fields), + .location = session_.source_location_for(clang_getCursorLocation(alias_cursor)), + }); + } + + [[nodiscard]] bool try_append_layout(CXCursor cursor, std::pmr::vector& layouts) { + const CXCursorKind kind = clang_getCursorKind(cursor); + if (kind == CXCursor_StructDecl) { + if (cursor_spelling_equals(cursor, "Arguments")) { + append_struct_layout(cursor, facts::LayoutKind::kArguments, layouts); + return true; + } + if (cursor_spelling_equals(cursor, "Result")) { + append_struct_layout(cursor, facts::LayoutKind::kResult, layouts); + return true; + } + return false; + } + + if (kind == CXCursor_TypeAliasDecl) { + if (cursor_spelling_equals(cursor, "Arguments")) { + append_alias_layout(cursor, facts::LayoutKind::kArguments, layouts); + return true; + } + if (cursor_spelling_equals(cursor, "Result")) { + append_alias_layout(cursor, facts::LayoutKind::kResult, layouts); + return true; + } + } + return false; + } + + ParseSession& session_; +}; + +} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp b/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp new file mode 100644 index 0000000000..c5b49590a7 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp @@ -0,0 +1,50 @@ +#include "clang_adapter/parse.h" + +#include "clang_adapter/function_extractor.hpp" +#include "clang_adapter/session.hpp" + +#include + +#include +namespace entrypoint_codegen::clang_adapter { + +facts::EntrypointFacts parse_files(const ParseOptions& options) { + if (options.source_files.empty()) { + throw std::invalid_argument("no source files provided"); + } + + ParseSession session(options); + session.add_clang_diagnostics(); + + FunctionExtractor extractor(session); + + struct TranslationUnitScanState { + ParseSession& session; + FunctionExtractor& extractor; + }; + + TranslationUnitScanState scan_state{ + .session = session, + .extractor = extractor, + }; + + CXCursor root = clang_getTranslationUnitCursor(session.translation_unit()); + clang_visitChildren( + root, + [](CXCursor cursor, CXCursor /*parent*/, CXClientData data) { + auto& state = *static_cast(data); + if (clang_getCursorKind(cursor) != CXCursor_FunctionDecl || !clang_isCursorDefinition(cursor)) { + return CXChildVisit_Recurse; + } + if (!state.session.is_input_source_file(cursor) || !state.extractor.is_candidate_function(cursor)) { + return CXChildVisit_Continue; + } + state.extractor.add_function(cursor); + return CXChildVisit_Continue; + }, + &scan_state); + + return session.take_facts(); +} + +} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse.h b/pp/tools/entrypoint_codegen/clang_adapter/parse.h new file mode 100644 index 0000000000..bc2f257bed --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse.h @@ -0,0 +1,20 @@ +#pragma once + +#include "facts/entrypoint_facts.h" + +#include +#include +#include +#include + +namespace entrypoint_codegen::clang_adapter { + +struct ParseOptions { + std::vector source_files; + std::vector clang_args; + std::pmr::memory_resource* memory_resource = std::pmr::get_default_resource(); +}; + +facts::EntrypointFacts parse_files(const ParseOptions& options); + +} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/session.hpp b/pp/tools/entrypoint_codegen/clang_adapter/session.hpp new file mode 100644 index 0000000000..147a3be57f --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/session.hpp @@ -0,0 +1,321 @@ +#pragma once + +#include "clang_adapter/parse.h" +#include "facts/entrypoint_facts.h" +#include "facts/facts.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace entrypoint_codegen::clang_adapter { + +inline std::string clang_string_to_std_string(CXString value) { + const char* raw = clang_getCString(value); + std::string out = raw == nullptr ? std::string() : std::string(raw); + clang_disposeString(value); + return out; +} + +inline std::string normalize_path(std::string path) { + if (path.empty()) { + return path; + } + + constexpr std::string_view execroot_marker = "/execroot/_main/"; + if (const size_t marker = path.find(execroot_marker); marker != std::string::npos) { + return path.substr(marker + execroot_marker.size()); + } + + const std::filesystem::path absolute_path(path); + if (!absolute_path.is_absolute()) { + return path; + } + + std::error_code error; + const std::filesystem::path relative_path = std::filesystem::relative(absolute_path, std::filesystem::current_path(), error); + if (error || relative_path.empty()) { + return path; + } + + std::string relative = relative_path.generic_string(); + if (relative == "." || relative.starts_with("../")) { + return path; + } + return relative; +} + +inline std::string path_for_file(CXFile file) { + if (file == nullptr) { + return {}; + } + return normalize_path(clang_string_to_std_string(clang_getFileName(file))); +} + +inline std::pmr::string build_synthetic_source(std::span source_files, std::pmr::memory_resource* memory_resource) { + std::pmr::string source(memory_resource); + for (const std::filesystem::path& file : source_files) { + source += "#include \""; + source += file.string(); + source += "\"\n"; + } + return source; +} + +inline facts::StringId add_diagnostic_message(facts::EntrypointFacts& facts, CXDiagnostic diagnostic) { + CXString message = clang_formatDiagnostic(diagnostic, clang_defaultDiagnosticDisplayOptions()); + const char* raw = clang_getCString(message); + const facts::StringId id = facts.add_string(raw == nullptr ? std::string_view() : std::string_view(raw)); + clang_disposeString(message); + return id; +} + +inline facts::Severity diagnostic_severity_for(CXDiagnosticSeverity severity) { + if (severity == CXDiagnostic_Warning) { + return facts::Severity::kWarning; + } + if (severity == CXDiagnostic_Error || severity == CXDiagnostic_Fatal) { + return facts::Severity::kError; + } + return facts::Severity::kInfo; +} + +class ClangIndexWrapper { + public: + ClangIndexWrapper() : index_(clang_createIndex(0, 0)) {} + ~ClangIndexWrapper() { + if (index_ != nullptr) { + clang_disposeIndex(index_); + } + } + + ClangIndexWrapper(const ClangIndexWrapper&) = delete; + ClangIndexWrapper& operator=(const ClangIndexWrapper&) = delete; + + [[nodiscard]] CXIndex get() const { return index_; } + + private: + CXIndex index_ = nullptr; +}; + +class ClangTranslationUnitWrapper { + public: + ClangTranslationUnitWrapper() = default; + explicit ClangTranslationUnitWrapper(CXTranslationUnit translation_unit) : translation_unit_(translation_unit) {} + ~ClangTranslationUnitWrapper() { + if (translation_unit_ != nullptr) { + clang_disposeTranslationUnit(translation_unit_); + } + } + + ClangTranslationUnitWrapper(const ClangTranslationUnitWrapper&) = delete; + ClangTranslationUnitWrapper& operator=(const ClangTranslationUnitWrapper&) = delete; + + void reset(CXTranslationUnit translation_unit) { + if (translation_unit_ != nullptr) { + clang_disposeTranslationUnit(translation_unit_); + } + translation_unit_ = translation_unit; + } + + [[nodiscard]] CXTranslationUnit get() const { return translation_unit_; } + + private: + CXTranslationUnit translation_unit_ = nullptr; +}; + +class ParseSession { + public: + explicit ParseSession(const ParseOptions& options) + : memory_resource_(options.memory_resource), + facts_(options.memory_resource), + source_files_(options.memory_resource), + synthetic_source_(options.memory_resource), + args_(options.memory_resource) { + if (index_.get() == nullptr) { + throw std::runtime_error("failed to create libclang index"); + } + + register_input_files(options.source_files); + parse_translation_unit(options); + } + + [[nodiscard]] std::pmr::memory_resource* memory_resource() const { return memory_resource_; } + [[nodiscard]] CXTranslationUnit translation_unit() const { return translation_unit_.get(); } + + facts::EntrypointFacts& facts() { return facts_; } + [[nodiscard]] const facts::EntrypointFacts& facts() const { return facts_; } + + [[nodiscard]] facts::EntrypointFacts take_facts() { return std::move(facts_); } + + void add_clang_diagnostics() { + const unsigned count = clang_getNumDiagnostics(translation_unit_.get()); + for (unsigned i = 0; i < count; ++i) { + CXDiagnostic diagnostic = clang_getDiagnostic(translation_unit_.get(), i); + + facts_.add_diagnostic(facts::Diagnostic{ + .code = facts::DiagnosticCode::kClangDiagnostic, + .message = add_diagnostic_message(facts_, diagnostic), + .severity = diagnostic_severity_for(clang_getDiagnosticSeverity(diagnostic)), + .function = std::nullopt, + .location = source_location_for(clang_getDiagnosticLocation(diagnostic)), + }); + + clang_disposeDiagnostic(diagnostic); + } + } + + [[nodiscard]] facts::SourceLocation source_location_for(CXSourceLocation location) { + CXFile file = nullptr; + unsigned line = 0; + unsigned column = 0; + unsigned offset = 0; + + clang_getSpellingLocation(location, &file, &line, &column, &offset); + + return facts::SourceLocation{ + .file = get_source_file(file), + .line = line, + .column = column, + }; + } + + [[nodiscard]] bool is_input_source_file(CXCursor cursor) { + CXFile file = nullptr; + unsigned line = 0; + unsigned column = 0; + unsigned offset = 0; + + clang_getSpellingLocation(clang_getCursorLocation(cursor), &file, &line, &column, &offset); + + if (file == nullptr) { + return false; + } + if (const SourceFile* source_file = find_source_file_by_handle(file); source_file != nullptr) { + return source_file->origin == SourceFileOrigin::kInput; + } + + const std::string path = path_for_file(file); + const SourceFile* source_file = find_source_file_by_path(file, path); + return source_file != nullptr && source_file->origin == SourceFileOrigin::kInput; + } + + private: + enum class SourceFileOrigin : uint8_t { + kInput, + kDiscovered, + }; + + struct SourceFile { + CXFile file = nullptr; + facts::SourceFileId id; + SourceFileOrigin origin = SourceFileOrigin::kDiscovered; + }; + + void register_input_files(const std::vector& source_files) { + source_files_.reserve(source_files.size()); + + for (const std::filesystem::path& file : source_files) { + const std::string path = normalize_path(std::filesystem::absolute(file).lexically_normal().string()); + const facts::SourceFileId id = facts_.add_source_file(path); + source_files_.push_back(SourceFile{ + .file = nullptr, + .id = id, + .origin = SourceFileOrigin::kInput, + }); + } + } + + void parse_translation_unit(const ParseOptions& options) { + const std::string synthetic_path = "/tmp/entrypoint_codegen_batch.cpp"; + synthetic_source_ = build_synthetic_source(options.source_files, memory_resource_); + + CXUnsavedFile unsaved_file{ + .Filename = synthetic_path.c_str(), + .Contents = synthetic_source_.c_str(), + .Length = static_cast(synthetic_source_.size()), + }; + + args_.reserve(options.clang_args.size()); + for (const std::string& arg : options.clang_args) { + args_.push_back(arg.c_str()); + } + + CXTranslationUnit raw_tu = nullptr; + const CXErrorCode parse_result = clang_parseTranslationUnit2(index_.get(), synthetic_path.c_str(), args_.data(), static_cast(args_.size()), + &unsaved_file, 1, CXTranslationUnit_KeepGoing, &raw_tu); + if (parse_result != CXError_Success || raw_tu == nullptr) { + throw std::runtime_error("failed to parse libclang translation unit"); + } + translation_unit_.reset(raw_tu); + } + + [[nodiscard]] facts::SourceFileId get_source_file(CXFile file) { + if (SourceFile* source_file = find_source_file_by_handle(file); source_file != nullptr) { + return source_file->id; + } + + std::string path = path_for_file(file); + if (path.empty()) { + path = ""; + } + if (SourceFile* source_file = find_source_file_by_path(file, path); source_file != nullptr) { + return source_file->id; + } + + const facts::SourceFileId id = facts_.add_source_file(path); + source_files_.push_back(SourceFile{ + .file = file, + .id = id, + .origin = SourceFileOrigin::kDiscovered, + }); + return id; + } + + [[nodiscard]] SourceFile* find_source_file_by_handle(CXFile file) { + if (file == nullptr) { + return nullptr; + } + for (SourceFile& source_file : source_files_) { + if (source_file.file == file) { + return &source_file; + } + } + return nullptr; + } + + [[nodiscard]] SourceFile* find_source_file_by_path(CXFile file, std::string_view path) { + if (path.empty()) { + return nullptr; + } + + for (SourceFile& source_file : source_files_) { + if (facts_.string(facts_.source_file(source_file.id).path) == path) { + if (source_file.file == nullptr) { + source_file.file = file; + } + return &source_file; + } + } + return nullptr; + } + + std::pmr::memory_resource* memory_resource_; + facts::EntrypointFacts facts_; + std::pmr::vector source_files_; + std::pmr::string synthetic_source_; + std::pmr::vector args_; + ClangIndexWrapper index_; + ClangTranslationUnitWrapper translation_unit_; +}; + +} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/emit/diagnostic_text.cpp b/pp/tools/entrypoint_codegen/emit/diagnostic_text.cpp new file mode 100644 index 0000000000..dd4a3ed57e --- /dev/null +++ b/pp/tools/entrypoint_codegen/emit/diagnostic_text.cpp @@ -0,0 +1,110 @@ +#include "emit/diagnostic_text.h" + +namespace entrypoint_codegen::emit { + +std::string_view diagnostic_code_name(facts::DiagnosticCode code) { + switch (code) { + case facts::DiagnosticCode::kClangDiagnostic: { + return "clang_diagnostic"; + } + case facts::DiagnosticCode::kUnsupportedReturnType: { + return "unsupported_return_type"; + } + case facts::DiagnosticCode::kUnsupportedParamCount: { + return "unsupported_param_count"; + } + case facts::DiagnosticCode::kUnsupportedParamType: { + return "unsupported_param_type"; + } + case facts::DiagnosticCode::kUnknownParamRole: + case facts::DiagnosticCode::kInvalidTwoParamOrder: + case facts::DiagnosticCode::kInvalidSecondParamRole: { + return "unknown_param_role"; + } + case facts::DiagnosticCode::kMissingArgumentsLayout: { + return "missing_arguments_layout"; + } + case facts::DiagnosticCode::kMissingResultLayout: { + return "missing_result_layout"; + } + case facts::DiagnosticCode::kUnexpectedArgumentsLayout: { + return "unexpected_arguments_layout"; + } + case facts::DiagnosticCode::kUnexpectedResultLayout: { + return "unexpected_result_layout"; + } + case facts::DiagnosticCode::kMissingNamePrefix: { + return "missing_name_prefix"; + } + case facts::DiagnosticCode::kMissingCLinkage: { + return "missing_c_linkage"; + } + case facts::DiagnosticCode::kMissingEntrypointAttribute: { + return "missing_entrypoint_attribute"; + } + case facts::DiagnosticCode::kRuntimeMemoryUsage: { + return "runtime_memory_usage"; + } + } + return "unknown_diagnostic"; +} + +std::string_view diagnostic_default_message(facts::DiagnosticCode code) { + switch (code) { + case facts::DiagnosticCode::kClangDiagnostic: { + return "Clang diagnostic"; + } + case facts::DiagnosticCode::kUnsupportedReturnType: { + return "FastCGo entrypoint must return void"; + } + case facts::DiagnosticCode::kUnsupportedParamCount: { + return "FastCGo entrypoint supports 0, 1, or 2 parameters"; + } + case facts::DiagnosticCode::kUnsupportedParamType: { + return "FastCGo entrypoint parameters must be void*"; + } + case facts::DiagnosticCode::kUnknownParamRole: { + return "FastCGo parameter must be named args or res"; + } + case facts::DiagnosticCode::kInvalidTwoParamOrder: { + return "FastCGo two-parameter form must be args, res"; + } + case facts::DiagnosticCode::kInvalidSecondParamRole: { + return "FastCGo second parameter must be res"; + } + case facts::DiagnosticCode::kMissingArgumentsLayout: { + return "args parameter requires local Arguments layout"; + } + case facts::DiagnosticCode::kMissingResultLayout: { + return "res parameter requires local Result layout"; + } + case facts::DiagnosticCode::kUnexpectedArgumentsLayout: { + return "Arguments layout exists but args parameter is absent"; + } + case facts::DiagnosticCode::kUnexpectedResultLayout: { + return "Result layout exists but res parameter is absent"; + } + case facts::DiagnosticCode::kMissingNamePrefix: { + return "entrypoint function must use prompp_ prefix"; + } + case facts::DiagnosticCode::kMissingCLinkage: { + return "entrypoint function must be declared extern \"C\""; + } + case facts::DiagnosticCode::kMissingEntrypointAttribute: { + return "entrypoint function requires CGo or FastCGo annotation"; + } + case facts::DiagnosticCode::kRuntimeMemoryUsage: { + return "runtime memory usage"; + } + } + return "unknown diagnostic"; +} + +std::string_view diagnostic_message(const facts::EntrypointFacts& facts, const facts::Diagnostic& diagnostic) { + if (diagnostic.message.has_value()) { + return facts.string(*diagnostic.message); + } + return diagnostic_default_message(diagnostic.code); +} + +} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/diagnostic_text.h b/pp/tools/entrypoint_codegen/emit/diagnostic_text.h new file mode 100644 index 0000000000..26f1ad10ea --- /dev/null +++ b/pp/tools/entrypoint_codegen/emit/diagnostic_text.h @@ -0,0 +1,14 @@ +#pragma once + +#include "facts/entrypoint_facts.h" +#include "facts/facts.h" + +#include + +namespace entrypoint_codegen::emit { + +std::string_view diagnostic_code_name(facts::DiagnosticCode code); +std::string_view diagnostic_default_message(facts::DiagnosticCode code); +std::string_view diagnostic_message(const facts::EntrypointFacts& facts, const facts::Diagnostic& diagnostic); + +} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/diagnostic_text_tests.cpp b/pp/tools/entrypoint_codegen/emit/diagnostic_text_tests.cpp new file mode 100644 index 0000000000..98186aa99d --- /dev/null +++ b/pp/tools/entrypoint_codegen/emit/diagnostic_text_tests.cpp @@ -0,0 +1,61 @@ +#include "emit/diagnostic_text.h" + +#include + +#include + +namespace { + +using entrypoint_codegen::facts::Diagnostic; +using entrypoint_codegen::facts::DiagnosticCode; +using entrypoint_codegen::facts::EntrypointFacts; +using entrypoint_codegen::facts::Severity; +using entrypoint_codegen::facts::SourceLocation; + +SourceLocation add_source_file(EntrypointFacts& facts) { + return SourceLocation{ + .file = facts.add_source_file("entrypoint.cpp"), + .line = 1, + .column = 1, + }; +} + +TEST(DiagnosticTextTest, ReturnsStaticMessageForValidationCode) { + // Arrange + EntrypointFacts facts; + const Diagnostic diagnostic{ + .code = DiagnosticCode::kMissingNamePrefix, + .message = std::nullopt, + .severity = Severity::kError, + .function = std::nullopt, + .location = add_source_file(facts), + }; + + // Act + const std::string_view code = entrypoint_codegen::emit::diagnostic_code_name(diagnostic.code); + const std::string_view message = entrypoint_codegen::emit::diagnostic_message(facts, diagnostic); + + // Assert + EXPECT_EQ(code, "missing_name_prefix"); + EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); +} + +TEST(DiagnosticTextTest, ReturnsDynamicMessageWhenPresent) { + // Arrange + EntrypointFacts facts; + const Diagnostic diagnostic{ + .code = DiagnosticCode::kClangDiagnostic, + .message = facts.add_string("clang says no"), + .severity = Severity::kError, + .function = std::nullopt, + .location = add_source_file(facts), + }; + + // Act + const std::string_view message = entrypoint_codegen::emit::diagnostic_message(facts, diagnostic); + + // Assert + EXPECT_EQ(message, "clang says no"); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/emit/diagnostics.cpp b/pp/tools/entrypoint_codegen/emit/diagnostics.cpp new file mode 100644 index 0000000000..b20990e2bc --- /dev/null +++ b/pp/tools/entrypoint_codegen/emit/diagnostics.cpp @@ -0,0 +1,38 @@ +#include "emit/diagnostics.h" + +#include "emit/diagnostic_text.h" + +#include +#include + +namespace entrypoint_codegen::emit { + +namespace { + +std::string_view severity_name(facts::Severity severity) { + switch (severity) { + case facts::Severity::kInfo: { + return "info"; + } + case facts::Severity::kWarning: { + return "warning"; + } + case facts::Severity::kError: { + return "error"; + } + } + return "error"; +} + +} // namespace + +void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts) { + for (const facts::Diagnostic& diagnostic : facts.diagnostics()) { + const facts::SourceLocation location = diagnostic.location; + out << facts.string(facts.source_file(location.file).path) << ":" << location.line << ":" << location.column + << ": " << severity_name(diagnostic.severity) << ": " << diagnostic_message(facts, diagnostic) + << " [" << diagnostic_code_name(diagnostic.code) << "]\n"; + } +} + +} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/diagnostics.h b/pp/tools/entrypoint_codegen/emit/diagnostics.h new file mode 100644 index 0000000000..288c3820b7 --- /dev/null +++ b/pp/tools/entrypoint_codegen/emit/diagnostics.h @@ -0,0 +1,11 @@ +#pragma once + +#include "facts/entrypoint_facts.h" + +#include + +namespace entrypoint_codegen::emit { + +void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts); + +} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp b/pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp new file mode 100644 index 0000000000..9e94580454 --- /dev/null +++ b/pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp @@ -0,0 +1,40 @@ +#include "emit/diagnostics.h" + +#include + +#include +#include + +namespace { + +using entrypoint_codegen::facts::Diagnostic; +using entrypoint_codegen::facts::DiagnosticCode; +using entrypoint_codegen::facts::EntrypointFacts; +using entrypoint_codegen::facts::Severity; +using entrypoint_codegen::facts::SourceLocation; + +TEST(EmitDiagnosticsTest, WritesCompilerStyleDiagnosticLine) { + // Arrange + EntrypointFacts facts; + const SourceLocation location{ + .file = facts.add_source_file("entrypoint.cpp"), + .line = 7, + .column = 9, + }; + facts.add_diagnostic(Diagnostic{ + .code = DiagnosticCode::kMissingNamePrefix, + .message = std::nullopt, + .severity = Severity::kError, + .function = std::nullopt, + .location = location, + }); + std::ostringstream output; + + // Act + entrypoint_codegen::emit::write_diagnostics(output, facts); + + // Assert + EXPECT_EQ(output.str(), "entrypoint.cpp:7:9: error: entrypoint function must use prompp_ prefix [missing_name_prefix]\n"); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/emit/json.cpp b/pp/tools/entrypoint_codegen/emit/json.cpp new file mode 100644 index 0000000000..50e4aec299 --- /dev/null +++ b/pp/tools/entrypoint_codegen/emit/json.cpp @@ -0,0 +1,257 @@ +#include "emit/json.h" + +#include "emit/diagnostic_text.h" + +#include +#include +#include +#include + +namespace entrypoint_codegen::emit { + +namespace { + +std::string json_escape(std::string_view input) { + std::string out; + out.reserve(input.size() + 8); + for (const char ch : input) { + switch (ch) { + case '\\': { + out += "\\\\"; + break; + } + case '"': { + out += "\\\""; + break; + } + case '\n': { + out += "\\n"; + break; + } + case '\r': { + out += "\\r"; + break; + } + case '\t': { + out += "\\t"; + break; + } + default: { + out.push_back(ch); + } + } + } + return out; +} + +std::string_view bridge_kind_name(facts::BridgeKind kind) { + switch (kind) { + case facts::BridgeKind::kUnknown: { + return "unknown"; + } + case facts::BridgeKind::kCGo: { + return "cgo"; + } + case facts::BridgeKind::kFastCGo: { + return "fastcgo"; + } + } + return "unknown"; +} + +std::string_view param_role_name(facts::ParamRole role) { + switch (role) { + case facts::ParamRole::kArgs: { + return "args"; + } + case facts::ParamRole::kRes: { + return "res"; + } + case facts::ParamRole::kOther: { + return "other"; + } + } + return "other"; +} + +std::string_view layout_kind_name(facts::LayoutKind kind) { + switch (kind) { + case facts::LayoutKind::kArguments: { + return "arguments"; + } + case facts::LayoutKind::kResult: { + return "result"; + } + } + return "unknown"; +} + +std::string_view severity_name(facts::Severity severity) { + switch (severity) { + case facts::Severity::kInfo: { + return "info"; + } + case facts::Severity::kWarning: { + return "warning"; + } + case facts::Severity::kError: { + return "error"; + } + } + return "error"; +} + +void write_location(std::ostream& out, const facts::EntrypointFacts& facts, facts::SourceLocation location) { + out << "{" + << "\"file\": \"" << json_escape(facts.string(facts.source_file(location.file).path)) << "\", " + << "\"line\": " << location.line << ", " + << "\"column\": " << location.column << "}"; +} + +void write_params(std::ostream& out, const facts::EntrypointFacts& facts, facts::ParamRange range) { + out << "["; + const auto params = facts.params(range); + for (size_t i = 0; i < params.size(); ++i) { + if (i != 0) { + out << ", "; + } + const facts::ParamDecl& param = params[i]; + out << "{" + << "\"name\": \"" << json_escape(facts.string(param.name)) << "\", " + << "\"type\": \"" << json_escape(facts.string(param.type_spelling)) << "\", " + << "\"role\": \"" << param_role_name(param.role) << "\", " + << "\"location\": "; + write_location(out, facts, param.location); + out << "}"; + } + out << "]"; +} + +void write_layouts(std::ostream& out, const facts::EntrypointFacts& facts, facts::LayoutRange range) { + out << "["; + const auto layouts = facts.layouts(range); + for (size_t i = 0; i < layouts.size(); ++i) { + if (i != 0) { + out << ", "; + } + const facts::LayoutDecl& layout = layouts[i]; + out << "{" + << "\"kind\": \"" << layout_kind_name(layout.kind) << "\", " + << "\"location\": "; + write_location(out, facts, layout.location); + out << ", \"fields\": ["; + const auto fields = facts.fields(layout.fields); + for (size_t field_index = 0; field_index < fields.size(); ++field_index) { + if (field_index != 0) { + out << ", "; + } + const facts::FieldDecl& field = fields[field_index]; + out << "{" + << "\"name\": \"" << json_escape(facts.string(field.name)) << "\", " + << "\"type\": \"" << json_escape(facts.string(field.type_spelling)) << "\", " + << "\"location\": "; + write_location(out, facts, field.location); + out << "}"; + } + out << "]}"; + } + out << "]"; +} + +void write_source_files(std::ostream& out, const facts::EntrypointFacts& facts) { + out << " \"source_files\": [\n"; + const auto source_files = facts.source_files(); + for (size_t i = 0; i < source_files.size(); ++i) { + out << " {\"path\": \"" << json_escape(facts.string(source_files[i].path)) << "\"}"; + if (i + 1 != source_files.size()) { + out << ","; + } + out << "\n"; + } + out << " ]"; +} + +void write_function(std::ostream& out, const facts::EntrypointFacts& facts, const facts::FunctionDecl& function) { + out << " {\n"; + out << " \"name\": \"" << json_escape(facts.string(function.name)) << "\",\n"; + out << " \"return_type\": \"" << json_escape(facts.string(function.return_type_spelling)) << "\",\n"; + out << " \"bridge_kind\": \"" << bridge_kind_name(function.bridge_kind) << "\",\n"; + out << " \"has_c_linkage\": " << (function.has_c_linkage ? "true" : "false") << ",\n"; + out << " \"documentation\": \"" << json_escape(facts.string(function.documentation)) << "\",\n"; + out << " \"location\": "; + write_location(out, facts, function.location); + out << ",\n"; + out << " \"params\": "; + write_params(out, facts, function.params); + out << ",\n"; + out << " \"layouts\": "; + write_layouts(out, facts, function.layouts); + out << "\n"; + out << " }"; +} + +void write_functions(std::ostream& out, const facts::EntrypointFacts& facts) { + out << " \"functions\": [\n"; + const auto functions = facts.functions(); + for (size_t i = 0; i < functions.size(); ++i) { + write_function(out, facts, functions[i]); + if (i + 1 != functions.size()) { + out << ","; + } + out << "\n"; + } + out << " ]"; +} + +void write_diagnostic_function(std::ostream& out, const facts::EntrypointFacts& facts, const facts::Diagnostic& diagnostic) { + if (diagnostic.function.has_value()) { + out << "\"" << json_escape(facts.string(facts.function(*diagnostic.function).name)) << "\""; + return; + } + out << "null"; +} + +void write_diagnostic(std::ostream& out, const facts::EntrypointFacts& facts, const facts::Diagnostic& diagnostic) { + const std::string_view message = diagnostic_message(facts, diagnostic); + out << " {" + << "\"code\": \"" << json_escape(diagnostic_code_name(diagnostic.code)) << "\", " + << "\"message\": \"" << json_escape(message) << "\", " + << "\"severity\": \"" << severity_name(diagnostic.severity) << "\", " + << "\"function\": "; + write_diagnostic_function(out, facts, diagnostic); + out << ", \"location\": "; + write_location(out, facts, diagnostic.location); + out << "}"; +} + +void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts) { + out << " \"diagnostics\": [\n"; + const auto diagnostics = facts.diagnostics(); + for (size_t i = 0; i < diagnostics.size(); ++i) { + write_diagnostic(out, facts, diagnostics[i]); + if (i + 1 != diagnostics.size()) { + out << ","; + } + out << "\n"; + } + out << " ]"; +} + +} // namespace + +void write_json(std::ostream& out, const facts::EntrypointFacts& facts) { + if (!out) { + throw std::runtime_error("output stream is not writable"); + } + + out << "{\n"; + write_source_files(out, facts); + out << ",\n"; + write_functions(out, facts); + out << ",\n"; + write_diagnostics(out, facts); + out << "\n"; + out << "}\n"; +} + +} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/json.h b/pp/tools/entrypoint_codegen/emit/json.h new file mode 100644 index 0000000000..3ad4877e4d --- /dev/null +++ b/pp/tools/entrypoint_codegen/emit/json.h @@ -0,0 +1,11 @@ +#pragma once + +#include "facts/entrypoint_facts.h" + +#include + +namespace entrypoint_codegen::emit { + +void write_json(std::ostream& out, const facts::EntrypointFacts& facts); + +} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/json_tests.cpp b/pp/tools/entrypoint_codegen/emit/json_tests.cpp new file mode 100644 index 0000000000..1a5d5432e9 --- /dev/null +++ b/pp/tools/entrypoint_codegen/emit/json_tests.cpp @@ -0,0 +1,43 @@ +#include "emit/json.h" + +#include + +#include +#include +#include + +namespace { + +using entrypoint_codegen::facts::Diagnostic; +using entrypoint_codegen::facts::DiagnosticCode; +using entrypoint_codegen::facts::EntrypointFacts; +using entrypoint_codegen::facts::Severity; +using entrypoint_codegen::facts::SourceLocation; + +TEST(EmitJsonTest, EscapesStringFieldsInOutput) { + // Arrange + EntrypointFacts facts; + const SourceLocation location{ + .file = facts.add_source_file("entry\"point.cpp"), + .line = 2, + .column = 4, + }; + facts.add_diagnostic(Diagnostic{ + .code = DiagnosticCode::kClangDiagnostic, + .message = facts.add_string("line\nmessage"), + .severity = Severity::kError, + .function = std::nullopt, + .location = location, + }); + std::ostringstream output; + + // Act + entrypoint_codegen::emit::write_json(output, facts); + + // Assert + const std::string json = output.str(); + EXPECT_NE(json.find("\"path\": \"entry\\\"point.cpp\""), std::string::npos); + EXPECT_NE(json.find("\"message\": \"line\\nmessage\""), std::string::npos); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/facts/entrypoint_facts.cpp b/pp/tools/entrypoint_codegen/facts/entrypoint_facts.cpp new file mode 100644 index 0000000000..0de55f79ac --- /dev/null +++ b/pp/tools/entrypoint_codegen/facts/entrypoint_facts.cpp @@ -0,0 +1,240 @@ +#include "facts/entrypoint_facts.h" + +#include "facts/string_table.h" + +#include +#include +#include +#include + +namespace entrypoint_codegen::facts { + +namespace { + +template +std::span range_span(const std::pmr::vector& values, uint32_t begin, uint32_t count) { + const uint32_t offset = begin; + assert(offset <= values.size()); + assert(static_cast(offset) + count <= values.size()); + return std::span(values.data() + offset, count); +} + +} // namespace + +class EntrypointFacts::Impl { + public: + explicit Impl(std::pmr::memory_resource* memory_resource) + : strings_(memory_resource), + source_files_(memory_resource), + functions_(memory_resource), + params_(memory_resource), + layouts_(memory_resource), + fields_(memory_resource), + diagnostics_(memory_resource) {} + + StringId add_string(std::string_view value) { return strings_.add(value); } + + SourceFileId add_source_file(std::string_view path) { + const auto id = SourceFileId(static_cast(source_files_.size())); + source_files_.push_back(SourceFileDecl{ + .path = strings_.add(path), + }); + return id; + } + + ParamRange add_params(std::span params) { + const auto begin = ParamId(static_cast(params_.size())); + params_.insert(params_.end(), params.begin(), params.end()); + return ParamRange{ + .begin = begin, + .count = static_cast(params.size()), + }; + } + + FieldRange add_fields(std::span fields) { + const auto begin = FieldId(static_cast(fields_.size())); + fields_.insert(fields_.end(), fields.begin(), fields.end()); + return FieldRange{ + .begin = begin, + .count = static_cast(fields.size()), + }; + } + + LayoutRange add_layouts(std::span layouts) { + const auto begin = LayoutId(static_cast(layouts_.size())); + layouts_.insert(layouts_.end(), layouts.begin(), layouts.end()); + return LayoutRange{ + .begin = begin, + .count = static_cast(layouts.size()), + }; + } + + FunctionId add_function(FunctionDecl function) { + const auto id = FunctionId(static_cast(functions_.size())); + functions_.push_back(function); + return id; + } + + void add_diagnostic(Diagnostic diagnostic) { diagnostics_.push_back(diagnostic); } + + [[nodiscard]] std::string_view string(StringId id) const { return strings_.get(id); } + + [[nodiscard]] const SourceFileDecl& source_file(SourceFileId id) const { + assert(id.get() < source_files_.size()); + return source_files_[id.get()]; + } + + [[nodiscard]] const FunctionDecl& function(FunctionId id) const { + assert(id.get() < functions_.size()); + return functions_[id.get()]; + } + + [[nodiscard]] std::span source_files() const { return source_files_; } + + [[nodiscard]] std::span functions() const { return functions_; } + + [[nodiscard]] std::span params(FunctionId id) const { return params(function(id).params); } + + [[nodiscard]] std::span params(ParamRange range) const { return range_span(params_, range.begin.get(), range.count); } + + [[nodiscard]] std::span layouts(FunctionId id) const { return layouts(function(id).layouts); } + + [[nodiscard]] std::span layouts(LayoutRange range) const { return range_span(layouts_, range.begin.get(), range.count); } + + [[nodiscard]] std::span fields(LayoutId id) const { + assert(id.get() < layouts_.size()); + return fields(layouts_[id.get()].fields); + } + + [[nodiscard]] std::span fields(FieldRange range) const { return range_span(fields_, range.begin.get(), range.count); } + + [[nodiscard]] std::span diagnostics() const { return diagnostics_; } + + [[nodiscard]] uint32_t diagnostic_count() const noexcept { return static_cast(diagnostics_.size()); } + + private: + StringTable strings_; + + std::pmr::vector source_files_; + std::pmr::vector functions_; + std::pmr::vector params_; + std::pmr::vector layouts_; + std::pmr::vector fields_; + std::pmr::vector diagnostics_; +}; + +EntrypointFacts::EntrypointFacts(std::pmr::memory_resource* memory_resource) : memory_resource_(memory_resource) { + std::pmr::polymorphic_allocator allocator(memory_resource_); + impl_ = allocator.allocate(1); + allocator.construct(impl_, memory_resource_); +} + +EntrypointFacts::~EntrypointFacts() { + if (impl_ == nullptr) { + return; + } + std::pmr::polymorphic_allocator allocator(memory_resource_); + allocator.destroy(impl_); + allocator.deallocate(impl_, 1); +} + +EntrypointFacts::EntrypointFacts(EntrypointFacts&& other) noexcept : impl_(other.impl_), memory_resource_(other.memory_resource_) { + other.impl_ = nullptr; +} + +EntrypointFacts& EntrypointFacts::operator=(EntrypointFacts&& other) noexcept { + if (this == &other) { + return *this; + } + if (impl_ != nullptr) { + std::pmr::polymorphic_allocator allocator(memory_resource_); + allocator.destroy(impl_); + allocator.deallocate(impl_, 1); + } + impl_ = other.impl_; + memory_resource_ = other.memory_resource_; + other.impl_ = nullptr; + return *this; +} + +StringId EntrypointFacts::add_string(std::string_view value) { + return impl_->add_string(value); +} + +SourceFileId EntrypointFacts::add_source_file(std::string_view path) { + return impl_->add_source_file(path); +} + +ParamRange EntrypointFacts::add_params(std::span params) { + return impl_->add_params(params); +} + +FieldRange EntrypointFacts::add_fields(std::span fields) { + return impl_->add_fields(fields); +} + +LayoutRange EntrypointFacts::add_layouts(std::span layouts) { + return impl_->add_layouts(layouts); +} + +FunctionId EntrypointFacts::add_function(FunctionDecl function) { + return impl_->add_function(function); +} + +void EntrypointFacts::add_diagnostic(Diagnostic diagnostic) { + impl_->add_diagnostic(diagnostic); +} + +std::string_view EntrypointFacts::string(StringId id) const { + return impl_->string(id); +} + +const SourceFileDecl& EntrypointFacts::source_file(SourceFileId id) const { + return impl_->source_file(id); +} + +const FunctionDecl& EntrypointFacts::function(FunctionId id) const { + return impl_->function(id); +} + +std::span EntrypointFacts::source_files() const { + return impl_->source_files(); +} + +std::span EntrypointFacts::functions() const { + return impl_->functions(); +} + +std::span EntrypointFacts::params(FunctionId id) const { + return impl_->params(id); +} + +std::span EntrypointFacts::params(ParamRange range) const { + return impl_->params(range); +} + +std::span EntrypointFacts::layouts(FunctionId id) const { + return impl_->layouts(id); +} + +std::span EntrypointFacts::layouts(LayoutRange range) const { + return impl_->layouts(range); +} + +std::span EntrypointFacts::fields(LayoutId id) const { + return impl_->fields(id); +} + +std::span EntrypointFacts::fields(FieldRange range) const { + return impl_->fields(range); +} + +std::span EntrypointFacts::diagnostics() const { + return impl_->diagnostics(); +} + +uint32_t EntrypointFacts::diagnostic_count() const noexcept { + return impl_->diagnostic_count(); +} + +} // namespace entrypoint_codegen::facts diff --git a/pp/tools/entrypoint_codegen/facts/entrypoint_facts.h b/pp/tools/entrypoint_codegen/facts/entrypoint_facts.h new file mode 100644 index 0000000000..d5bed8e819 --- /dev/null +++ b/pp/tools/entrypoint_codegen/facts/entrypoint_facts.h @@ -0,0 +1,57 @@ +#pragma once + +#include "facts/facts.h" + +#include +#include +#include +#include + +namespace entrypoint_codegen::facts { + +class EntrypointFacts { + public: + explicit EntrypointFacts(std::pmr::memory_resource* memory_resource = std::pmr::get_default_resource()); + ~EntrypointFacts(); + + EntrypointFacts(EntrypointFacts&&) noexcept; + EntrypointFacts& operator=(EntrypointFacts&&) noexcept; + + EntrypointFacts(const EntrypointFacts&) = delete; + EntrypointFacts& operator=(const EntrypointFacts&) = delete; + + StringId add_string(std::string_view value); + SourceFileId add_source_file(std::string_view path); + ParamRange add_params(std::span params); + FieldRange add_fields(std::span fields); + LayoutRange add_layouts(std::span layouts); + FunctionId add_function(FunctionDecl function); + void add_diagnostic(Diagnostic diagnostic); + + [[nodiscard]] std::string_view string(StringId id) const; + + [[nodiscard]] const SourceFileDecl& source_file(SourceFileId id) const; + [[nodiscard]] const FunctionDecl& function(FunctionId id) const; + + [[nodiscard]] std::span source_files() const; + [[nodiscard]] std::span functions() const; + + [[nodiscard]] std::span params(FunctionId id) const; + [[nodiscard]] std::span params(ParamRange range) const; + + [[nodiscard]] std::span layouts(FunctionId id) const; + [[nodiscard]] std::span layouts(LayoutRange range) const; + + [[nodiscard]] std::span fields(LayoutId id) const; + [[nodiscard]] std::span fields(FieldRange range) const; + + [[nodiscard]] std::span diagnostics() const; + [[nodiscard]] uint32_t diagnostic_count() const noexcept; + + private: + class Impl; + Impl* impl_ = nullptr; + std::pmr::memory_resource* memory_resource_ = std::pmr::get_default_resource(); +}; + +} // namespace entrypoint_codegen::facts diff --git a/pp/tools/entrypoint_codegen/facts/entrypoint_facts_tests.cpp b/pp/tools/entrypoint_codegen/facts/entrypoint_facts_tests.cpp new file mode 100644 index 0000000000..cf14db3754 --- /dev/null +++ b/pp/tools/entrypoint_codegen/facts/entrypoint_facts_tests.cpp @@ -0,0 +1,65 @@ +#include "facts/entrypoint_facts.h" + +#include + +#include + +namespace { + +using entrypoint_codegen::facts::BridgeKind; +using entrypoint_codegen::facts::EntrypointFacts; +using entrypoint_codegen::facts::FunctionDecl; +using entrypoint_codegen::facts::LayoutKind; +using entrypoint_codegen::facts::LayoutDecl; +using entrypoint_codegen::facts::ParamDecl; +using entrypoint_codegen::facts::ParamRole; +using entrypoint_codegen::facts::SourceLocation; + +TEST(EntrypointFactsTest, ResolvesFunctionRangesToStoredRecords) { + // Arrange + EntrypointFacts facts; + const SourceLocation location{ + .file = facts.add_source_file("entrypoint.cpp"), + .line = 3, + .column = 5, + }; + const std::vector params{ + ParamDecl{ + .name = facts.add_string("args"), + .type_spelling = facts.add_string("void*"), + .role = ParamRole::kArgs, + .location = location, + }, + }; + const std::vector layouts{ + LayoutDecl{ + .kind = LayoutKind::kArguments, + .fields = facts.add_fields({}), + .location = location, + }, + }; + const auto param_range = facts.add_params(params); + const auto layout_range = facts.add_layouts(layouts); + const auto function_id = facts.add_function(FunctionDecl{ + .name = facts.add_string("prompp_fn"), + .return_type_spelling = facts.add_string("void"), + .documentation = facts.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = param_range, + .layouts = layout_range, + .location = location, + .has_c_linkage = true, + }); + + // Act + const auto stored_params = facts.params(function_id); + const auto stored_layouts = facts.layouts(function_id); + + // Assert + ASSERT_EQ(stored_params.size(), 1); + EXPECT_EQ(facts.string(stored_params[0].name), "args"); + ASSERT_EQ(stored_layouts.size(), 1); + EXPECT_EQ(stored_layouts[0].kind, LayoutKind::kArguments); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/facts/facts.h b/pp/tools/entrypoint_codegen/facts/facts.h new file mode 100644 index 0000000000..a34aae8a58 --- /dev/null +++ b/pp/tools/entrypoint_codegen/facts/facts.h @@ -0,0 +1,121 @@ +#pragma once + +#include "tagged_index.h" + +#include +#include + +namespace entrypoint_codegen::facts { + +using SourceFileId = TaggedIndex; +using FunctionId = TaggedIndex; +using LayoutId = TaggedIndex; +using ParamId = TaggedIndex; +using FieldId = TaggedIndex; +using StringId = TaggedIndex; + +struct ParamRange { + ParamId begin; + uint32_t count; +}; + +struct LayoutRange { + LayoutId begin; + uint32_t count; +}; + +struct FieldRange { + FieldId begin; + uint32_t count; +}; + +enum class BridgeKind : uint8_t { + kUnknown, + kCGo, + kFastCGo, +}; + +enum class ParamRole : uint8_t { + kArgs, + kRes, + kOther, +}; + +enum class LayoutKind : uint8_t { + kArguments, + kResult, +}; + +enum class Severity : uint8_t { + kInfo, + kWarning, + kError, +}; + +enum class DiagnosticCode : uint8_t { + kClangDiagnostic, + kUnsupportedReturnType, + kUnsupportedParamCount, + kUnsupportedParamType, + kUnknownParamRole, + kInvalidTwoParamOrder, + kInvalidSecondParamRole, + kMissingArgumentsLayout, + kMissingResultLayout, + kUnexpectedArgumentsLayout, + kUnexpectedResultLayout, + kMissingNamePrefix, + kMissingCLinkage, + kMissingEntrypointAttribute, + kRuntimeMemoryUsage, +}; + +struct SourceLocation { + SourceFileId file; + uint32_t line; + uint32_t column; +}; + +struct SourceFileDecl { + StringId path; +}; + +struct ParamDecl { + StringId name; + StringId type_spelling; + ParamRole role; + SourceLocation location; +}; + +struct FieldDecl { + StringId name; + StringId type_spelling; + SourceLocation location; +}; + +struct LayoutDecl { + LayoutKind kind; + FieldRange fields; + SourceLocation location; +}; + +struct FunctionDecl { + StringId name; + StringId return_type_spelling; + StringId documentation; + BridgeKind bridge_kind; + ParamRange params; + LayoutRange layouts; + SourceLocation location; + bool has_c_linkage; +}; + +struct Diagnostic { + DiagnosticCode code; + std::optional message; + Severity severity; + std::optional function; + SourceLocation location; +}; + +} // namespace entrypoint_codegen::facts \ No newline at end of file diff --git a/pp/tools/entrypoint_codegen/facts/string_table.cpp b/pp/tools/entrypoint_codegen/facts/string_table.cpp new file mode 100644 index 0000000000..9ae0315393 --- /dev/null +++ b/pp/tools/entrypoint_codegen/facts/string_table.cpp @@ -0,0 +1,111 @@ +#include "string_table.h" + +#include +#include +#include +#include +#include +#include + +namespace entrypoint_codegen::facts { + +class StringTable::Impl { + public: + explicit Impl(std::pmr::memory_resource* memory_resource) : data_(memory_resource), strings_(memory_resource) {} + + StringId add(std::string_view value) { + assert(value.size() <= UINT32_MAX); + assert(strings_.size() <= UINT32_MAX); + + const auto offset = static_cast(data_.size()); + const auto length = static_cast(value.size()); + const auto id = StringId(static_cast(strings_.size())); + + data_.resize(data_.size() + value.size()); + if (!value.empty()) [[likely]] { + std::memcpy(data_.data() + offset, value.data(), value.size()); + } + strings_.push_back(StringData{ + .offset = offset, + .length = length, + }); + + return id; + } + + [[nodiscard]] std::string_view get(StringId id) const { + const uint32_t index = id.get(); + assert(index < strings_.size()); + return string_view_for(strings_[index]); + } + + [[nodiscard]] uint32_t size() const noexcept { return static_cast(strings_.size()); } + + [[nodiscard]] bool empty() const noexcept { return strings_.empty(); } + + private: + struct StringData { + uint32_t offset; + uint32_t length; + }; + + [[nodiscard]] std::string_view string_view_for(StringData string) const { + assert(static_cast(string.offset) + string.length <= data_.size()); + return std::string_view(reinterpret_cast(data_.data() + string.offset), string.length); + } + + std::pmr::vector data_; + std::pmr::vector strings_; +}; + +StringTable::StringTable(std::pmr::memory_resource* memory_resource) : memory_resource_(memory_resource) { + std::pmr::polymorphic_allocator allocator(memory_resource_); + impl_ = allocator.allocate(1); + allocator.construct(impl_, memory_resource_); +} + +StringTable::~StringTable() { + if (impl_ == nullptr) { + return; + } + std::pmr::polymorphic_allocator allocator(memory_resource_); + allocator.destroy(impl_); + allocator.deallocate(impl_, 1); +} + +StringTable::StringTable(StringTable&& other) noexcept : impl_(other.impl_), memory_resource_(other.memory_resource_) { + other.impl_ = nullptr; +} + +StringTable& StringTable::operator=(StringTable&& other) noexcept { + if (this == &other) { + return *this; + } + if (impl_ != nullptr) { + std::pmr::polymorphic_allocator allocator(memory_resource_); + allocator.destroy(impl_); + allocator.deallocate(impl_, 1); + } + impl_ = other.impl_; + memory_resource_ = other.memory_resource_; + other.impl_ = nullptr; + return *this; +} + +StringId StringTable::add(std::string_view value) { + return impl_->add(value); +} + +std::string_view StringTable::get(StringId id) const { + return impl_->get(id); +} + +uint32_t StringTable::size() const noexcept { + return impl_->size(); +} + +bool StringTable::empty() const noexcept { + return impl_->empty(); +} + +} // namespace entrypoint_codegen::facts diff --git a/pp/tools/entrypoint_codegen/facts/string_table.h b/pp/tools/entrypoint_codegen/facts/string_table.h new file mode 100644 index 0000000000..4aff072be3 --- /dev/null +++ b/pp/tools/entrypoint_codegen/facts/string_table.h @@ -0,0 +1,34 @@ +#pragma once + +#include "facts.h" + +#include +#include +#include + +namespace entrypoint_codegen::facts { + +class StringTable { + public: + explicit StringTable(std::pmr::memory_resource* memory_resource = std::pmr::get_default_resource()); + ~StringTable(); + + StringTable(StringTable&&) noexcept; + StringTable& operator=(StringTable&&) noexcept; + + StringTable(const StringTable&) = delete; + StringTable& operator=(const StringTable&) = delete; + + StringId add(std::string_view value); + [[nodiscard]] std::string_view get(StringId id) const; + + [[nodiscard]] uint32_t size() const noexcept; + [[nodiscard]] bool empty() const noexcept; + + private: + class Impl; + Impl* impl_{}; + std::pmr::memory_resource* memory_resource_{}; +}; + +} // namespace entrypoint_codegen::facts \ No newline at end of file diff --git a/pp/tools/entrypoint_codegen/facts/tagged_index.h b/pp/tools/entrypoint_codegen/facts/tagged_index.h new file mode 100644 index 0000000000..edad9f323f --- /dev/null +++ b/pp/tools/entrypoint_codegen/facts/tagged_index.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +namespace entrypoint_codegen::facts { + +template +class TaggedIndex { + public: + using tag_type = Tag; + using value_type = UInt; + + constexpr TaggedIndex() noexcept = default; + constexpr explicit TaggedIndex(UInt value) noexcept : value_(value) {} + + [[nodiscard]] constexpr UInt get() const noexcept { return value_; } + constexpr explicit operator UInt() const noexcept { return value_; } + + constexpr auto operator<=>(const TaggedIndex&) const noexcept = default; + + private: + UInt value_{}; +}; + +} // namespace entrypoint_codegen::facts \ No newline at end of file diff --git a/pp/tools/entrypoint_codegen/libclang_repository.bzl b/pp/tools/entrypoint_codegen/libclang_repository.bzl new file mode 100644 index 0000000000..69168abf7b --- /dev/null +++ b/pp/tools/entrypoint_codegen/libclang_repository.bzl @@ -0,0 +1,133 @@ +def _first_non_empty(values): + for value in values: + if value: + return value + return "" + +def _libclang_path(repository_ctx, attr_name, env_name, llvm_root, suffix): + attr_value = getattr(repository_ctx.attr, attr_name) + env_value = repository_ctx.os.environ.get(env_name, "") + if attr_value: + return attr_value + if env_value: + return env_value + if llvm_root: + return llvm_root + suffix + return "" + +def _libclang_repository_impl(repository_ctx): + llvm_root = _first_non_empty([ + repository_ctx.attr.llvm_root, + repository_ctx.os.environ.get("LIBCLANG_LLVM_ROOT", ""), + "/usr/lib/llvm-21", + ]) + include_dir = _libclang_path(repository_ctx, "include_dir", "LIBCLANG_INCLUDE_DIR", llvm_root, "/include") + lib_dir = _libclang_path(repository_ctx, "lib_dir", "LIBCLANG_LIB_DIR", llvm_root, "/lib") + library_name = _first_non_empty([ + repository_ctx.attr.library_name, + repository_ctx.os.environ.get("LIBCLANG_LIBRARY", ""), + "clang", + ]) + + if not include_dir: + fail("libclang include directory is not configured") + if not lib_dir: + fail("libclang library directory is not configured") + + include_path = repository_ctx.path(include_dir) + lib_path = repository_ctx.path(lib_dir) + if not include_path.exists: + fail("libclang include directory does not exist: " + include_dir) + if not lib_path.exists: + fail("libclang library directory does not exist: " + lib_dir) + + copy_result = repository_ctx.execute([ + "cp", + "-R", + include_dir, + "include", + ]) + if copy_result.return_code != 0: + fail("failed to copy libclang headers: " + copy_result.stderr) + + linkopts = [ + "-L" + lib_dir, + "-l" + library_name, + ] + if repository_ctx.attr.rpath: + linkopts.append("-Wl,-rpath," + lib_dir) + + repository_ctx.file( + "BUILD.bazel", + """package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "libclang", + hdrs = glob(["include/**/*.h"]), + includes = ["include"], + linkopts = {linkopts}, +) + +alias( + name = "headers", + actual = ":libclang", +) +""".format(linkopts = repr(linkopts)), + ) + +libclang_repository = repository_rule( + implementation = _libclang_repository_impl, + attrs = { + "include_dir": attr.string(), + "lib_dir": attr.string(), + "library_name": attr.string(default = "clang"), + "llvm_root": attr.string(), + "rpath": attr.bool(default = True), + }, + environ = [ + "LIBCLANG_INCLUDE_DIR", + "LIBCLANG_LIB_DIR", + "LIBCLANG_LIBRARY", + "LIBCLANG_LLVM_ROOT", + ], +) + +_libclang_config = tag_class( + attrs = { + "include_dir": attr.string(), + "lib_dir": attr.string(), + "library_name": attr.string(default = "clang"), + "llvm_root": attr.string(), + "rpath": attr.bool(default = True), + }, +) + +def _libclang_extension_impl(module_ctx): + configs = [] + for module in module_ctx.modules: + for config in module.tags.configure: + configs.append(config) + + if len(configs) > 1: + fail("libclang_extension accepts at most one configure(...) tag") + + if configs: + config = configs[0] + libclang_repository( + name = "system_libclang", + include_dir = config.include_dir, + lib_dir = config.lib_dir, + library_name = config.library_name, + llvm_root = config.llvm_root, + rpath = config.rpath, + ) + return + + libclang_repository(name = "system_libclang") + +libclang_extension = module_extension( + implementation = _libclang_extension_impl, + tag_classes = { + "configure": _libclang_config, + }, +) diff --git a/pp/tools/entrypoint_codegen/validate/validate.cpp b/pp/tools/entrypoint_codegen/validate/validate.cpp new file mode 100644 index 0000000000..992c1d99ea --- /dev/null +++ b/pp/tools/entrypoint_codegen/validate/validate.cpp @@ -0,0 +1,108 @@ +#include "validate/validate.h" + +#include +#include + +namespace entrypoint_codegen::validate { + +namespace { + +bool is_void_pointer_type(std::string_view type) { + return type == "void *" || type == "void*"; +} + +bool starts_with(std::string_view value, std::string_view prefix) { + return value.substr(0, prefix.size()) == prefix; +} + +void add_diagnostic(facts::EntrypointFacts& facts, facts::DiagnosticCode code, facts::FunctionId function_id, facts::SourceLocation location) { + facts.add_diagnostic(facts::Diagnostic{ + .code = code, + .message = std::nullopt, + .severity = facts::Severity::kError, + .function = function_id, + .location = location, + }); +} + +bool has_layout(const facts::EntrypointFacts& facts, const facts::FunctionDecl& function, facts::LayoutKind kind) { + for (const facts::LayoutDecl& layout : facts.layouts(function.layouts)) { + if (layout.kind == kind) { + return true; + } + } + return false; +} + +void validate_fastcgo_function(facts::EntrypointFacts& facts, facts::FunctionId function_id, const facts::FunctionDecl& function) { + if (facts.string(function.return_type_spelling) != "void") { + add_diagnostic(facts, facts::DiagnosticCode::kUnsupportedReturnType, function_id, function.location); + } + + const auto params = facts.params(function.params); + if (params.size() > 2) { + add_diagnostic(facts, facts::DiagnosticCode::kUnsupportedParamCount, function_id, function.location); + } + + bool uses_args = false; + bool uses_res = false; + for (size_t i = 0; i < params.size(); ++i) { + const facts::ParamDecl& param = params[i]; + if (!is_void_pointer_type(facts.string(param.type_spelling))) { + add_diagnostic(facts, facts::DiagnosticCode::kUnsupportedParamType, function_id, param.location); + } + if (param.role == facts::ParamRole::kOther) { + add_diagnostic(facts, facts::DiagnosticCode::kUnknownParamRole, function_id, param.location); + continue; + } + if (i == 0 && param.role == facts::ParamRole::kRes && params.size() == 2) { + add_diagnostic(facts, facts::DiagnosticCode::kInvalidTwoParamOrder, function_id, param.location); + } + if (i == 1 && param.role != facts::ParamRole::kRes) { + add_diagnostic(facts, facts::DiagnosticCode::kInvalidSecondParamRole, function_id, param.location); + } + uses_args |= param.role == facts::ParamRole::kArgs; + uses_res |= param.role == facts::ParamRole::kRes; + } + + const bool has_arguments = has_layout(facts, function, facts::LayoutKind::kArguments); + const bool has_result = has_layout(facts, function, facts::LayoutKind::kResult); + if (uses_args && !has_arguments) { + add_diagnostic(facts, facts::DiagnosticCode::kMissingArgumentsLayout, function_id, function.location); + } + if (uses_res && !has_result) { + add_diagnostic(facts, facts::DiagnosticCode::kMissingResultLayout, function_id, function.location); + } + if (!uses_args && has_arguments) { + add_diagnostic(facts, facts::DiagnosticCode::kUnexpectedArgumentsLayout, function_id, function.location); + } + if (!uses_res && has_result) { + add_diagnostic(facts, facts::DiagnosticCode::kUnexpectedResultLayout, function_id, function.location); + } +} + +} // namespace + +void validate_entrypoints(facts::EntrypointFacts& facts) { + const auto functions = facts.functions(); + for (size_t i = 0; i < functions.size(); ++i) { + const auto function_id = facts::FunctionId(static_cast(i)); + const facts::FunctionDecl& function = functions[i]; + const std::string_view name = facts.string(function.name); + if (!starts_with(name, "prompp_")) { + add_diagnostic(facts, facts::DiagnosticCode::kMissingNamePrefix, function_id, function.location); + } + if (!function.has_c_linkage) { + add_diagnostic(facts, facts::DiagnosticCode::kMissingCLinkage, function_id, function.location); + } + if (function.bridge_kind == facts::BridgeKind::kUnknown) { + add_diagnostic(facts, facts::DiagnosticCode::kMissingEntrypointAttribute, function_id, function.location); + continue; + } + if (function.bridge_kind == facts::BridgeKind::kFastCGo) { + validate_fastcgo_function(facts, function_id, function); + } + } +} + +} // namespace entrypoint_codegen::validate diff --git a/pp/tools/entrypoint_codegen/validate/validate.h b/pp/tools/entrypoint_codegen/validate/validate.h new file mode 100644 index 0000000000..af8a04b2ef --- /dev/null +++ b/pp/tools/entrypoint_codegen/validate/validate.h @@ -0,0 +1,9 @@ +#pragma once + +#include "facts/entrypoint_facts.h" + +namespace entrypoint_codegen::validate { + +void validate_entrypoints(facts::EntrypointFacts& facts); + +} // namespace entrypoint_codegen::validate diff --git a/pp/tools/entrypoint_codegen/validate/validate_tests.cpp b/pp/tools/entrypoint_codegen/validate/validate_tests.cpp new file mode 100644 index 0000000000..d1f895d910 --- /dev/null +++ b/pp/tools/entrypoint_codegen/validate/validate_tests.cpp @@ -0,0 +1,94 @@ +#include "validate/validate.h" + +#include + +#include +#include + +namespace { + +using entrypoint_codegen::facts::BridgeKind; +using entrypoint_codegen::facts::DiagnosticCode; +using entrypoint_codegen::facts::EntrypointFacts; +using entrypoint_codegen::facts::FunctionDecl; +using entrypoint_codegen::facts::LayoutDecl; +using entrypoint_codegen::facts::ParamDecl; +using entrypoint_codegen::facts::ParamRole; +using entrypoint_codegen::facts::Severity; +using entrypoint_codegen::facts::SourceLocation; + +SourceLocation add_source_file(EntrypointFacts& facts) { + return SourceLocation{ + .file = facts.add_source_file("entrypoint.cpp"), + .line = 7, + .column = 3, + }; +} + +entrypoint_codegen::facts::ParamRange add_params(EntrypointFacts& facts, std::span params) { + return facts.add_params(params); +} + +entrypoint_codegen::facts::LayoutRange add_layouts(EntrypointFacts& facts, std::span layouts) { + return facts.add_layouts(layouts); +} + +FunctionDecl make_function(EntrypointFacts& facts, SourceLocation location, BridgeKind bridge_kind) { + return FunctionDecl{ + .name = facts.add_string("prompp_fn"), + .return_type_spelling = facts.add_string("void"), + .documentation = facts.add_string(""), + .bridge_kind = bridge_kind, + .params = add_params(facts, {}), + .layouts = add_layouts(facts, {}), + .location = location, + .has_c_linkage = true, + }; +} + +TEST(ValidateEntrypointsTest, AddsMissingEntrypointAttributeForUnannotatedFunction) { + // Arrange + EntrypointFacts facts; + const SourceLocation location = add_source_file(facts); + const auto function_id = facts.add_function(make_function(facts, location, BridgeKind::kUnknown)); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts); + + // Assert + const auto diagnostics = facts.diagnostics(); + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingEntrypointAttribute); + EXPECT_EQ(diagnostics[0].severity, Severity::kError); + EXPECT_EQ(diagnostics[0].function, function_id); + EXPECT_EQ(diagnostics[0].location.line, location.line); +} + +TEST(ValidateEntrypointsTest, AddsMissingArgumentsLayoutWhenArgsParameterHasNoLayout) { + // Arrange + EntrypointFacts facts; + const SourceLocation location = add_source_file(facts); + const std::vector params{ + ParamDecl{ + .name = facts.add_string("args"), + .type_spelling = facts.add_string("void*"), + .role = ParamRole::kArgs, + .location = location, + }, + }; + FunctionDecl function = make_function(facts, location, BridgeKind::kFastCGo); + function.params = add_params(facts, params); + const auto function_id = facts.add_function(function); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts); + + // Assert + const auto diagnostics = facts.diagnostics(); + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingArgumentsLayout); + EXPECT_EQ(diagnostics[0].function, function_id); + EXPECT_EQ(diagnostics[0].location.line, location.line); +} + +} // namespace From f70ed157ecf86df8c4a35867211451ab49fa214e Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Mon, 6 Jul 2026 06:51:55 +0000 Subject: [PATCH 12/31] ref --- pp/tools/entrypoint_codegen/.bazelignore | 4 + pp/tools/entrypoint_codegen/BUILD | 180 ++++++- pp/tools/entrypoint_codegen/MODULE.bazel | 2 + pp/tools/entrypoint_codegen/app/argparse.cpp | 6 +- pp/tools/entrypoint_codegen/app/argparse.h | 2 +- .../entrypoint_codegen/app/argparse_tests.cpp | 127 +++++ pp/tools/entrypoint_codegen/app/options.h | 43 ++ pp/tools/entrypoint_codegen/app/run.cpp | 57 +- pp/tools/entrypoint_codegen/app/run.h | 38 +- pp/tools/entrypoint_codegen/app/run_tests.cpp | 68 +++ .../entrypoint_codegen/app/runtime_debug.cpp | 21 +- .../entrypoint_codegen/app/runtime_debug.h | 4 +- .../app/runtime_debug_tests.cpp | 40 ++ .../clang_adapter/aggregate_source.cpp | 19 + .../clang_adapter/aggregate_source.h | 17 + .../clang_adapter/clang_runtime.cpp | 500 ++++++++++++++++++ .../clang_adapter/clang_runtime.h | 112 ++++ .../clang_adapter/cursor_reader.hpp | 57 -- .../clang_adapter/function_extractor.hpp | 276 ---------- .../clang_adapter/parse.cpp | 302 ++++++++++- .../entrypoint_codegen/clang_adapter/parse.h | 3 +- .../clang_adapter/parse_tests.cpp | 214 ++++++++ .../clang_adapter/session.hpp | 321 ----------- .../contract/entrypoint_contract.cpp | 47 ++ .../contract/entrypoint_contract.h | 26 + .../contract/entrypoint_contract_tests.cpp | 40 ++ .../diagnostics/diagnostic_catalog.cpp | 129 +++++ .../diagnostics/diagnostic_catalog.h | 14 + .../diagnostics/diagnostic_catalog_tests.cpp | 94 ++++ .../diagnostics/diagnostics.cpp | 25 + .../diagnostics/diagnostics.h | 60 +++ .../diagnostics/diagnostics_tests.cpp | 49 ++ .../emit/diagnostic_text.cpp | 110 ---- .../entrypoint_codegen/emit/diagnostic_text.h | 14 - .../emit/diagnostic_text_tests.cpp | 61 --- .../entrypoint_codegen/emit/diagnostics.cpp | 34 +- .../entrypoint_codegen/emit/diagnostics.h | 3 +- .../emit/diagnostics_tests.cpp | 49 +- pp/tools/entrypoint_codegen/emit/json.cpp | 59 +-- pp/tools/entrypoint_codegen/emit/json.h | 3 +- .../entrypoint_codegen/emit/json_tests.cpp | 165 +++++- .../facts/entrypoint_facts.cpp | 30 +- .../facts/entrypoint_facts.h | 4 - .../facts/entrypoint_facts_tests.cpp | 102 +++- pp/tools/entrypoint_codegen/facts/facts.h | 35 +- .../entrypoint_codegen/facts/string_table.cpp | 8 +- .../entrypoint_codegen/validate/validate.cpp | 58 +- .../entrypoint_codegen/validate/validate.h | 3 +- .../validate/validate_tests.cpp | 427 +++++++++++++-- 49 files changed, 2833 insertions(+), 1229 deletions(-) create mode 100644 pp/tools/entrypoint_codegen/.bazelignore create mode 100644 pp/tools/entrypoint_codegen/app/argparse_tests.cpp create mode 100644 pp/tools/entrypoint_codegen/app/options.h create mode 100644 pp/tools/entrypoint_codegen/app/run_tests.cpp create mode 100644 pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h delete mode 100644 pp/tools/entrypoint_codegen/clang_adapter/cursor_reader.hpp delete mode 100644 pp/tools/entrypoint_codegen/clang_adapter/function_extractor.hpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp delete mode 100644 pp/tools/entrypoint_codegen/clang_adapter/session.hpp create mode 100644 pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp create mode 100644 pp/tools/entrypoint_codegen/contract/entrypoint_contract.h create mode 100644 pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp create mode 100644 pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.cpp create mode 100644 pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.h create mode 100644 pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog_tests.cpp create mode 100644 pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp create mode 100644 pp/tools/entrypoint_codegen/diagnostics/diagnostics.h create mode 100644 pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp delete mode 100644 pp/tools/entrypoint_codegen/emit/diagnostic_text.cpp delete mode 100644 pp/tools/entrypoint_codegen/emit/diagnostic_text.h delete mode 100644 pp/tools/entrypoint_codegen/emit/diagnostic_text_tests.cpp diff --git a/pp/tools/entrypoint_codegen/.bazelignore b/pp/tools/entrypoint_codegen/.bazelignore new file mode 100644 index 0000000000..41c9094078 --- /dev/null +++ b/pp/tools/entrypoint_codegen/.bazelignore @@ -0,0 +1,4 @@ +bazel-bin +bazel-entrypoint_codegen +bazel-out +bazel-testlogs diff --git a/pp/tools/entrypoint_codegen/BUILD b/pp/tools/entrypoint_codegen/BUILD index 98118eff73..626d429ff8 100644 --- a/pp/tools/entrypoint_codegen/BUILD +++ b/pp/tools/entrypoint_codegen/BUILD @@ -30,59 +30,116 @@ cc_test( ) cc_library( - name = "clang_adapter", - srcs = [ - "clang_adapter/cursor_reader.hpp", - "clang_adapter/function_extractor.hpp", - "clang_adapter/parse.cpp", - "clang_adapter/session.hpp", + name = "diagnostics", + srcs = ["diagnostics/diagnostics.cpp"], + hdrs = ["diagnostics/diagnostics.h"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [":facts"], +) + +cc_test( + name = "diagnostics_test", + srcs = ["diagnostics/diagnostics_tests.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":diagnostics", + ":facts", + "@gtest//:gtest_main", ], - hdrs = [ - "clang_adapter/parse.h", +) + +cc_library( + name = "diagnostic_catalog", + srcs = ["diagnostics/diagnostic_catalog.cpp"], + hdrs = ["diagnostics/diagnostic_catalog.h"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [":diagnostics"], +) + +cc_test( + name = "diagnostic_catalog_test", + srcs = ["diagnostics/diagnostic_catalog_tests.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":diagnostic_catalog", + ":diagnostics", + ":facts", + "@gtest//:gtest_main", + ], +) + +cc_library( + name = "entrypoint_contract", + srcs = ["contract/entrypoint_contract.cpp"], + hdrs = ["contract/entrypoint_contract.h"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":facts", ], +) + +cc_test( + name = "entrypoint_contract_test", + srcs = ["contract/entrypoint_contract_tests.cpp"], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ + ":entrypoint_contract", ":facts", - "@system_libclang//:libclang", + "@gtest//:gtest_main", ], ) cc_library( - name = "validate", - srcs = ["validate/validate.cpp"], - hdrs = ["validate/validate.h"], + name = "clang_adapter", + srcs = [ + "clang_adapter/aggregate_source.cpp", + "clang_adapter/aggregate_source.h", + "clang_adapter/clang_runtime.h", + "clang_adapter/clang_runtime.cpp", + "clang_adapter/parse.cpp", + ], + hdrs = ["clang_adapter/parse.h"], copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [":facts"], + deps = [ + ":diagnostics", + ":entrypoint_contract", + ":facts", + "@system_libclang//:libclang", + ], ) cc_test( - name = "validate_test", - srcs = ["validate/validate_tests.cpp"], + name = "clang_adapter_test", + srcs = ["clang_adapter/parse_tests.cpp"], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ + ":clang_adapter", + ":diagnostics", ":facts", - ":validate", "@gtest//:gtest_main", ], ) cc_library( - name = "diagnostic_text", - srcs = ["emit/diagnostic_text.cpp"], - hdrs = ["emit/diagnostic_text.h"], + name = "validate", + srcs = ["validate/validate.cpp"], + hdrs = ["validate/validate.h"], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ + ":diagnostics", + ":entrypoint_contract", ":facts", ], ) cc_test( - name = "diagnostic_text_test", - srcs = ["emit/diagnostic_text_tests.cpp"], + name = "validate_test", + srcs = ["validate/validate_tests.cpp"], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ - ":diagnostic_text", + ":diagnostics", ":facts", + ":validate", "@gtest//:gtest_main", ], ) @@ -93,7 +150,8 @@ cc_library( hdrs = ["emit/json.h"], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ - ":diagnostic_text", + ":diagnostic_catalog", + ":diagnostics", ":facts", ], ) @@ -103,6 +161,7 @@ cc_test( srcs = ["emit/json_tests.cpp"], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ + ":diagnostics", ":emit_json", ":facts", "@gtest//:gtest_main", @@ -115,7 +174,8 @@ cc_library( hdrs = ["emit/diagnostics.h"], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ - ":diagnostic_text", + ":diagnostic_catalog", + ":diagnostics", ":facts", ], ) @@ -125,6 +185,7 @@ cc_test( srcs = ["emit/diagnostics_tests.cpp"], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ + ":diagnostics", ":emit_diagnostics", ":facts", "@gtest//:gtest_main", @@ -132,20 +193,47 @@ cc_test( ) cc_library( - name = "app", + name = "app_options", + hdrs = ["app/options.h"], + copts = ENTRYPOINT_CODEGEN_COPTS, +) + +cc_library( + name = "app_argparse", srcs = [ "app/argparse.cpp", - "app/run.cpp", - "app/runtime_debug.cpp", ], hdrs = [ "app/argparse.h", + ], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":app_options", + ], +) + +cc_library( + name = "app_runtime_debug", + srcs = ["app/runtime_debug.cpp"], + hdrs = ["app/runtime_debug.h"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":diagnostics", + ], +) + +cc_library( + name = "app_run", + srcs = ["app/run.cpp"], + hdrs = [ "app/run.h", - "app/runtime_debug.h", ], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ + ":app_options", + ":app_runtime_debug", ":clang_adapter", + ":diagnostics", ":emit_diagnostics", ":emit_json", ":facts", @@ -153,10 +241,44 @@ cc_library( ], ) +cc_test( + name = "argparse_test", + srcs = ["app/argparse_tests.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":app_argparse", + "@gtest//:gtest_main", + ], +) + +cc_test( + name = "runtime_debug_test", + srcs = ["app/runtime_debug_tests.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":app_runtime_debug", + ":diagnostics", + "@gtest//:gtest_main", + ], +) + +cc_test( + name = "run_test", + srcs = ["app/run_tests.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":app_run", + "@gtest//:gtest_main", + ], +) + cc_binary( name = "entrypoint_codegen", srcs = ["app/main.cpp"], copts = ENTRYPOINT_CODEGEN_COPTS, visibility = ["//visibility:public"], - deps = [":app"], + deps = [ + ":app_argparse", + ":app_run", + ], ) diff --git a/pp/tools/entrypoint_codegen/MODULE.bazel b/pp/tools/entrypoint_codegen/MODULE.bazel index b9a92ba979..e6dbca3b67 100644 --- a/pp/tools/entrypoint_codegen/MODULE.bazel +++ b/pp/tools/entrypoint_codegen/MODULE.bazel @@ -1,5 +1,7 @@ module(name = "entrypoint_codegen") +bazel_dep(name = "rules_cc", version = "0.2.16") + bazel_dep( name = "googletest", repo_name = "gtest", diff --git a/pp/tools/entrypoint_codegen/app/argparse.cpp b/pp/tools/entrypoint_codegen/app/argparse.cpp index 89cd631ea7..127ffe2b33 100644 --- a/pp/tools/entrypoint_codegen/app/argparse.cpp +++ b/pp/tools/entrypoint_codegen/app/argparse.cpp @@ -21,7 +21,7 @@ std::vector collect_input_files(const std::vector files; for (const std::filesystem::path& input : inputs) { if (!std::filesystem::exists(input)) { - continue; + throw std::runtime_error("input path does not exist: " + input.string()); } if (std::filesystem::is_regular_file(input)) { if (has_cpp_extension(input)) { @@ -30,7 +30,7 @@ std::vector collect_input_files(const std::vector [...] -- [...]\n"; - out << " --source=PATH Additional source path, file or directory.\n"; + out << " --source=PATH Additional source path, file or recursive directory.\n"; out << " --input=PATH Alias for --source=PATH.\n"; out << " --output=PATH JSON output path. Defaults to ./entrypoint_facts.json.\n"; out << " --output-dir=PATH Directory for entrypoint_facts.json.\n"; diff --git a/pp/tools/entrypoint_codegen/app/argparse.h b/pp/tools/entrypoint_codegen/app/argparse.h index cc11f4318d..3f36616ef0 100644 --- a/pp/tools/entrypoint_codegen/app/argparse.h +++ b/pp/tools/entrypoint_codegen/app/argparse.h @@ -1,6 +1,6 @@ #pragma once -#include "app/run.h" +#include "app/options.h" #include diff --git a/pp/tools/entrypoint_codegen/app/argparse_tests.cpp b/pp/tools/entrypoint_codegen/app/argparse_tests.cpp new file mode 100644 index 0000000000..550f6b23c3 --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/argparse_tests.cpp @@ -0,0 +1,127 @@ +#include "app/argparse.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +entrypoint_codegen::app::CliOptions parse_args(std::initializer_list args) { + std::vector argv_storage; + argv_storage.reserve(args.size()); + for (std::string_view arg : args) { + argv_storage.emplace_back(arg); + } + + std::vector argv; + argv.reserve(argv_storage.size()); + for (std::string& arg : argv_storage) { + argv.push_back(arg.data()); + } + + return entrypoint_codegen::app::parse_arguments(static_cast(argv.size()), argv.data()); +} + +std::filesystem::path test_tmp_dir() { + if (const char* test_tmpdir = std::getenv("TEST_TMPDIR"); test_tmpdir != nullptr) { + return test_tmpdir; + } + return std::filesystem::temp_directory_path(); +} + +std::filesystem::path touch_file(std::string_view name) { + const std::filesystem::path path = test_tmp_dir() / name; + std::ofstream out(path, std::ios::trunc); + return path; +} + +TEST(ArgparseTest, ReturnsHelpForHelpFlag) { + // Act + const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--help"}); + + // Assert + EXPECT_TRUE(options.help); + EXPECT_TRUE(options.run_options.source_files.empty()); +} + +TEST(ArgparseTest, CollectsExistingCppInputFiles) { + // Arrange + const std::filesystem::path source_file = touch_file("entrypoint_argparse.cpp"); + touch_file("entrypoint_argparse.txt"); + + // Act + const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", source_file.string()}); + const auto source_files = options.run_options.source_files; + + // Assert + ASSERT_EQ(source_files.size(), 1); + EXPECT_EQ(source_files[0], std::filesystem::absolute(source_file).lexically_normal()); +} + +TEST(ArgparseTest, CollectsCppInputFilesRecursivelyFromDirectory) { + // Arrange + const std::filesystem::path root = test_tmp_dir() / "entrypoint_argparse_recursive"; + const std::filesystem::path nested = root / "nested"; + std::filesystem::create_directories(nested); + const std::filesystem::path source_file = nested / "entrypoint_argparse_nested.cpp"; + std::ofstream out(source_file, std::ios::trunc); + + // Act + const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", root.string()}); + const auto source_files = options.run_options.source_files; + + // Assert + ASSERT_EQ(source_files.size(), 1); + EXPECT_EQ(source_files[0], std::filesystem::absolute(source_file).lexically_normal()); +} + +TEST(ArgparseTest, RejectsMissingInputPath) { + // Arrange + const std::filesystem::path missing = test_tmp_dir() / "entrypoint_argparse_missing.cpp"; + std::filesystem::remove(missing); + + // Act / Assert + EXPECT_THROW(parse_args({"entrypoint_codegen", missing.string()}), std::runtime_error); +} + +TEST(ArgparseTest, ParsesOutputDirectoryAsFactsFilePath) { + // Arrange + const std::filesystem::path output_dir = test_tmp_dir() / "facts"; + + // Act + const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--output-dir=" + output_dir.string()}); + + // Assert + EXPECT_EQ(options.run_options.output_path, output_dir / "entrypoint_facts.json"); +} + +TEST(ArgparseTest, ParsesCheckModeAlias) { + // Act + const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--no-output"}); + + // Assert + EXPECT_EQ(options.run_options.output_mode, entrypoint_codegen::app::OutputMode::kCheck); +} + +TEST(ArgparseTest, CollectsClangArgsFromFlagAndSeparator) { + // Act + const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--clang-arg=-I.", "--", "-std=c++2b"}); + const auto clang_args = options.run_options.clang_args; + + // Assert + ASSERT_EQ(clang_args.size(), 2); + EXPECT_EQ(clang_args[0], "-I."); + EXPECT_EQ(clang_args[1], "-std=c++2b"); +} + +TEST(ArgparseTest, RejectsUnknownOutputMode) { + EXPECT_THROW(parse_args({"entrypoint_codegen", "--mode=xml"}), std::runtime_error); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/app/options.h b/pp/tools/entrypoint_codegen/app/options.h new file mode 100644 index 0000000000..4caa8b8408 --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/options.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace entrypoint_codegen::app { + +enum class OutputMode : uint8_t { + kJson, + kLint, + kCheck, +}; + +struct RunOptions { + std::vector source_files; + std::vector clang_args; + std::filesystem::path output_path; + OutputMode output_mode = OutputMode::kJson; + bool runtime_debug = false; + std::ostream* diagnostics_output = nullptr; +}; + +enum class ExitDecision : uint8_t { + kSuccess, + kAnalysisFailed, +}; + +struct RunReport { + ExitDecision decision; + uint32_t diagnostic_count; + uint32_t error_count; + uint32_t warning_count; + uint32_t info_count; + size_t app_allocated_bytes; + size_t app_deallocated_bytes; + size_t app_peak_live_bytes; +}; + +} // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/run.cpp b/pp/tools/entrypoint_codegen/app/run.cpp index 743e1684b9..9e7c656c97 100644 --- a/pp/tools/entrypoint_codegen/app/run.cpp +++ b/pp/tools/entrypoint_codegen/app/run.cpp @@ -2,6 +2,7 @@ #include "app/runtime_debug.h" #include "clang_adapter/parse.h" +#include "diagnostics/diagnostics.h" #include "emit/diagnostics.h" #include "emit/json.h" #include "validate/validate.h" @@ -18,8 +19,7 @@ namespace { class tracking_memory_resource : public std::pmr::memory_resource { public: - explicit tracking_memory_resource(std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) - : upstream_(upstream) {} + explicit tracking_memory_resource(std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) : upstream_(upstream) {} [[nodiscard]] size_t allocated_bytes() const noexcept { return allocated_bytes_; } [[nodiscard]] size_t deallocated_bytes() const noexcept { return deallocated_bytes_; } @@ -42,9 +42,7 @@ class tracking_memory_resource : public std::pmr::memory_resource { live_bytes_ -= bytes; } - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { - return this == &other; - } + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { return this == &other; } std::pmr::memory_resource* upstream_; size_t allocated_bytes_ = 0; @@ -60,20 +58,20 @@ struct DiagnosticCounts { uint32_t infos = 0; }; -DiagnosticCounts count_diagnostics(const facts::EntrypointFacts& facts) { +DiagnosticCounts count_diagnostics(const diagnostics::DiagnosticSet& diagnostic_set) { DiagnosticCounts counts; - for (const facts::Diagnostic& diagnostic : facts.diagnostics()) { + for (const diagnostics::Diagnostic& diagnostic : diagnostic_set.diagnostics()) { ++counts.total; switch (diagnostic.severity) { - case facts::Severity::kInfo: { + case diagnostics::Severity::kInfo: { ++counts.infos; break; } - case facts::Severity::kWarning: { + case diagnostics::Severity::kWarning: { ++counts.warnings; break; } - case facts::Severity::kError: { + case diagnostics::Severity::kError: { ++counts.errors; break; } @@ -82,7 +80,7 @@ DiagnosticCounts count_diagnostics(const facts::EntrypointFacts& facts) { return counts; } -void write_json_output(const RunOptions& options, const facts::EntrypointFacts& facts) { +void write_json_output(const RunOptions& options, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { if (options.output_path.has_parent_path()) { std::filesystem::create_directories(options.output_path.parent_path()); } @@ -91,41 +89,44 @@ void write_json_output(const RunOptions& options, const facts::EntrypointFacts& if (!output) { throw std::runtime_error("failed to open output file: " + options.output_path.string()); } - emit::write_json(output, facts); + emit::write_json(output, facts, diagnostic_set); } -void write_lint_output(const RunOptions& options, const facts::EntrypointFacts& facts) { +void write_lint_output(const RunOptions& options, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { std::ostream& output = options.diagnostics_output == nullptr ? std::cout : *options.diagnostics_output; - emit::write_diagnostics(output, facts); + emit::write_diagnostics(output, facts, diagnostic_set); } } // namespace RunReport run(const RunOptions& options) { tracking_memory_resource memory_resource; - facts::EntrypointFacts facts = clang_adapter::parse_files(clang_adapter::ParseOptions{ - .source_files = options.source_files, - .clang_args = options.clang_args, - .memory_resource = &memory_resource, - }); + diagnostics::DiagnosticSet diagnostic_set(&memory_resource); + facts::EntrypointFacts facts = clang_adapter::parse_files( + clang_adapter::ParseOptions{ + .source_files = options.source_files, + .clang_args = options.clang_args, + .memory_resource = &memory_resource, + }, + diagnostic_set); - validate::validate_entrypoints(facts); + validate::validate_entrypoints(facts, diagnostic_set); if (options.runtime_debug) { - append_runtime_debug_diagnostics(facts, RuntimeDebugSnapshot{ - .allocated_bytes = memory_resource.allocated_bytes(), - .deallocated_bytes = memory_resource.deallocated_bytes(), - .peak_live_bytes = memory_resource.peak_live_bytes(), - }); + append_runtime_debug_diagnostics(diagnostic_set, RuntimeDebugSnapshot{ + .allocated_bytes = memory_resource.allocated_bytes(), + .deallocated_bytes = memory_resource.deallocated_bytes(), + .peak_live_bytes = memory_resource.peak_live_bytes(), + }); } switch (options.output_mode) { case OutputMode::kJson: { - write_json_output(options, facts); + write_json_output(options, facts, diagnostic_set); break; } case OutputMode::kLint: { - write_lint_output(options, facts); + write_lint_output(options, facts, diagnostic_set); break; } case OutputMode::kCheck: { @@ -133,7 +134,7 @@ RunReport run(const RunOptions& options) { } } - const DiagnosticCounts diagnostic_counts = count_diagnostics(facts); + const DiagnosticCounts diagnostic_counts = count_diagnostics(diagnostic_set); return RunReport{ .decision = diagnostic_counts.errors == 0 ? ExitDecision::kSuccess : ExitDecision::kAnalysisFailed, .diagnostic_count = diagnostic_counts.total, diff --git a/pp/tools/entrypoint_codegen/app/run.h b/pp/tools/entrypoint_codegen/app/run.h index 470f585c2d..cd67916670 100644 --- a/pp/tools/entrypoint_codegen/app/run.h +++ b/pp/tools/entrypoint_codegen/app/run.h @@ -1,45 +1,9 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include "app/options.h" namespace entrypoint_codegen::app { -enum class OutputMode : uint8_t { - kJson, - kLint, - kCheck, -}; - -struct RunOptions { - std::vector source_files; - std::vector clang_args; - std::filesystem::path output_path; - OutputMode output_mode = OutputMode::kJson; - bool runtime_debug = false; - std::ostream* diagnostics_output = nullptr; -}; - -enum class ExitDecision : uint8_t { - kSuccess, - kAnalysisFailed, -}; - -struct RunReport { - ExitDecision decision; - uint32_t diagnostic_count; - uint32_t error_count; - uint32_t warning_count; - uint32_t info_count; - size_t app_allocated_bytes; - size_t app_deallocated_bytes; - size_t app_peak_live_bytes; -}; - RunReport run(const RunOptions& options); } // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/run_tests.cpp b/pp/tools/entrypoint_codegen/app/run_tests.cpp new file mode 100644 index 0000000000..56a1f923ba --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/run_tests.cpp @@ -0,0 +1,68 @@ +#include "app/run.h" + +#include + +#include +#include +#include +#include +#include + +namespace { + +std::filesystem::path test_tmp_dir() { + if (const char* test_tmpdir = std::getenv("TEST_TMPDIR"); test_tmpdir != nullptr) { + return test_tmpdir; + } + return std::filesystem::temp_directory_path(); +} + +std::filesystem::path write_source_file(std::string_view name, std::string_view source) { + const std::filesystem::path path = test_tmp_dir() / name; + std::ofstream out(path, std::ios::trunc); + out << source; + return path; +} + +entrypoint_codegen::app::RunOptions check_options_for(std::filesystem::path source_file) { + return entrypoint_codegen::app::RunOptions{ + .source_files = {std::move(source_file)}, + .clang_args = {"-std=c++2b"}, + .output_path = {}, + .output_mode = entrypoint_codegen::app::OutputMode::kCheck, + }; +} + +TEST(RunTest, CheckModeSucceedsForValidAnnotatedEntrypoint) { + // Arrange + const std::filesystem::path source_file = write_source_file("entrypoint_codegen_run_valid.cpp", R"cpp( + extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_store() {} + )cpp"); + const entrypoint_codegen::app::RunOptions options = check_options_for(source_file); + + // Act + const entrypoint_codegen::app::RunReport report = entrypoint_codegen::app::run(options); + + // Assert + EXPECT_EQ(report.decision, entrypoint_codegen::app::ExitDecision::kSuccess); + EXPECT_EQ(report.error_count, 0); + EXPECT_EQ(report.diagnostic_count, 0); +} + +TEST(RunTest, CheckModeFailsWhenValidationReportsErrors) { + // Arrange + const std::filesystem::path source_file = write_source_file("entrypoint_codegen_run_invalid.cpp", R"cpp( + extern "C" void prompp_store() {} + )cpp"); + const entrypoint_codegen::app::RunOptions options = check_options_for(source_file); + + // Act + const entrypoint_codegen::app::RunReport report = entrypoint_codegen::app::run(options); + + // Assert + EXPECT_EQ(report.decision, entrypoint_codegen::app::ExitDecision::kAnalysisFailed); + EXPECT_EQ(report.error_count, 1); + EXPECT_EQ(report.diagnostic_count, 1); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug.cpp index 31497d4f7b..e46d7ab5e0 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug.cpp +++ b/pp/tools/entrypoint_codegen/app/runtime_debug.cpp @@ -5,21 +5,16 @@ namespace entrypoint_codegen::app { -void append_runtime_debug_diagnostics(facts::EntrypointFacts& facts, RuntimeDebugSnapshot snapshot) { - const facts::SourceFileId source_file = facts.add_source_file(""); +void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, RuntimeDebugSnapshot snapshot) { const std::string message = "App PMR allocations: allocated=" + std::to_string(snapshot.allocated_bytes) + - " deallocated=" + std::to_string(snapshot.deallocated_bytes) + - " peak_live=" + std::to_string(snapshot.peak_live_bytes) + " bytes"; - facts.add_diagnostic(facts::Diagnostic{ - .code = facts::DiagnosticCode::kRuntimeMemoryUsage, - .message = facts.add_string(message), - .severity = facts::Severity::kInfo, + " deallocated=" + std::to_string(snapshot.deallocated_bytes) + " peak_live=" + std::to_string(snapshot.peak_live_bytes) + + " bytes"; + diagnostic_set.add(diagnostics::Diagnostic{ + .code = diagnostics::DiagnosticCode::kRuntimeMemoryUsage, + .message = message, + .severity = diagnostics::Severity::kInfo, .function = std::nullopt, - .location = facts::SourceLocation{ - .file = source_file, - .line = 0, - .column = 0, - }, + .location = std::nullopt, }); } diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.h b/pp/tools/entrypoint_codegen/app/runtime_debug.h index 601366c76e..5519490cb2 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug.h +++ b/pp/tools/entrypoint_codegen/app/runtime_debug.h @@ -1,6 +1,6 @@ #pragma once -#include "facts/entrypoint_facts.h" +#include "diagnostics/diagnostics.h" #include @@ -12,6 +12,6 @@ struct RuntimeDebugSnapshot { size_t peak_live_bytes; }; -void append_runtime_debug_diagnostics(facts::EntrypointFacts& facts, RuntimeDebugSnapshot snapshot); +void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, RuntimeDebugSnapshot snapshot); } // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp new file mode 100644 index 0000000000..9f5106b28b --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp @@ -0,0 +1,40 @@ +#include "app/runtime_debug.h" + +#include + +#include "diagnostics/diagnostics.h" + +#include + +namespace { + +using entrypoint_codegen::app::RuntimeDebugSnapshot; +using entrypoint_codegen::diagnostics::DiagnosticCode; +using entrypoint_codegen::diagnostics::DiagnosticSet; +using entrypoint_codegen::diagnostics::Severity; + +class RuntimeDebugTest : public testing::Test { + protected: + DiagnosticSet diagnostics_; +}; + +TEST_F(RuntimeDebugTest, AppendsMemoryUsageDiagnostic) { + // Act + entrypoint_codegen::app::append_runtime_debug_diagnostics(diagnostics_, + RuntimeDebugSnapshot{ + .allocated_bytes = 11, + .deallocated_bytes = 7, + .peak_live_bytes = 9, + }); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kRuntimeMemoryUsage); + EXPECT_EQ(diagnostics[0].severity, Severity::kInfo); + ASSERT_TRUE(diagnostics[0].message.has_value()); + EXPECT_EQ(*diagnostics[0].message, "App PMR allocations: allocated=11 deallocated=7 peak_live=9 bytes"); + EXPECT_FALSE(diagnostics[0].location.has_value()); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp b/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp new file mode 100644 index 0000000000..3e7164a7e5 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp @@ -0,0 +1,19 @@ +#include "clang_adapter/aggregate_source.h" + +namespace entrypoint_codegen::clang_adapter { + +AggregateSource build_aggregate_source(std::span source_files, std::pmr::memory_resource* memory_resource) { + AggregateSource source{ + .path = std::pmr::string("/tmp/entrypoint_codegen_aggregate.cpp", memory_resource), + .contents = std::pmr::string(memory_resource), + }; + + for (const std::filesystem::path& file : source_files) { + source.contents += "#include \""; + source.contents += file.string(); + source.contents += "\"\n"; + } + return source; +} + +} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h b/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h new file mode 100644 index 0000000000..45b992aafd --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include + +namespace entrypoint_codegen::clang_adapter { + +struct AggregateSource { + std::pmr::string path; + std::pmr::string contents; +}; + +[[nodiscard]] AggregateSource build_aggregate_source(std::span source_files, std::pmr::memory_resource* memory_resource); + +} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp new file mode 100644 index 0000000000..12a5ec7f9a --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp @@ -0,0 +1,500 @@ +#include "clang_adapter/clang_runtime.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace entrypoint_codegen::clang_adapter { + +namespace { + +CursorKind cursor_kind_for(CXCursorKind kind) { + switch (kind) { + case CXCursor_FunctionDecl: { + return CursorKind::kFunctionDecl; + } + case CXCursor_AnnotateAttr: { + return CursorKind::kAnnotateAttr; + } + case CXCursor_FieldDecl: { + return CursorKind::kFieldDecl; + } + case CXCursor_StructDecl: { + return CursorKind::kStructDecl; + } + case CXCursor_TypeAliasDecl: { + return CursorKind::kTypeAliasDecl; + } + default: { + return CursorKind::kOther; + } + } +} + +CXChildVisitResult child_visit_result_for(VisitResult result) { + switch (result) { + case VisitResult::kBreak: { + return CXChildVisit_Break; + } + case VisitResult::kContinue: { + return CXChildVisit_Continue; + } + case VisitResult::kRecurse: { + return CXChildVisit_Recurse; + } + } + return CXChildVisit_Continue; +} + +class ClangIndexWrapper { + public: + ClangIndexWrapper() : index_(clang_createIndex(0, 0)) {} + ~ClangIndexWrapper() { + if (index_ != nullptr) { + clang_disposeIndex(index_); + } + } + + ClangIndexWrapper(const ClangIndexWrapper&) = delete; + ClangIndexWrapper& operator=(const ClangIndexWrapper&) = delete; + + [[nodiscard]] CXIndex get() const { return index_; } + + private: + CXIndex index_ = nullptr; +}; + +class ClangTranslationUnitWrapper { + public: + ClangTranslationUnitWrapper() = default; + ~ClangTranslationUnitWrapper() { + if (translation_unit_ != nullptr) { + clang_disposeTranslationUnit(translation_unit_); + } + } + + ClangTranslationUnitWrapper(const ClangTranslationUnitWrapper&) = delete; + ClangTranslationUnitWrapper& operator=(const ClangTranslationUnitWrapper&) = delete; + + void reset(CXTranslationUnit translation_unit) { + if (translation_unit_ != nullptr) { + clang_disposeTranslationUnit(translation_unit_); + } + translation_unit_ = translation_unit; + } + + [[nodiscard]] CXTranslationUnit get() const { return translation_unit_; } + + private: + CXTranslationUnit translation_unit_ = nullptr; +}; + +std::string clang_string_to_string(CXString value) { + const char* raw = clang_getCString(value); + std::string out = raw == nullptr ? std::string() : std::string(raw); + clang_disposeString(value); + return out; +} + +std::string path_for_file(CXFile file) { + if (file == nullptr) { + return {}; + } + return normalize_path(clang_string_to_string(clang_getFileName(file))); +} + +std::string diagnostic_message(CXDiagnostic diagnostic) { + return clang_string_to_string(clang_formatDiagnostic(diagnostic, clang_defaultDiagnosticDisplayOptions())); +} + +diagnostics::Severity diagnostic_severity_for(CXDiagnosticSeverity severity) { + if (severity == CXDiagnostic_Warning) { + return diagnostics::Severity::kWarning; + } + if (severity == CXDiagnostic_Error || severity == CXDiagnostic_Fatal) { + return diagnostics::Severity::kError; + } + return diagnostics::Severity::kInfo; +} + +enum class SourceFileOrigin : uint8_t { + kInput, + kDiscovered, +}; + +struct SourceFile { + CXFile file = nullptr; + facts::SourceFileId id; + SourceFileOrigin origin = SourceFileOrigin::kDiscovered; +}; + +} // namespace + +std::string normalize_path(std::string path) { + if (path.empty()) { + return path; + } + + constexpr std::string_view execroot_marker = "/execroot/_main/"; + if (const size_t marker = path.find(execroot_marker); marker != std::string::npos) { + return path.substr(marker + execroot_marker.size()); + } + + const std::filesystem::path absolute_path(path); + if (!absolute_path.is_absolute()) { + return path; + } + + std::error_code error; + const std::filesystem::path relative_path = std::filesystem::relative(absolute_path, std::filesystem::current_path(), error); + if (error || relative_path.empty()) { + return path; + } + + std::string relative = relative_path.generic_string(); + if (relative == "." || relative.starts_with("../")) { + return path; + } + return relative; +} + +std::string TypeView::spelling() const { + return clang_string_to_string(clang_getTypeSpelling(type_)); +} + +CursorView TypeView::canonical_declaration() const { + return CursorView(clang_getTypeDeclaration(clang_getCanonicalType(type_))); +} + +std::string CursorView::spelling() const { + return clang_string_to_string(clang_getCursorSpelling(cursor_)); +} + +std::string CursorView::raw_comment() const { + return clang_string_to_string(clang_Cursor_getRawCommentText(cursor_)); +} + +TypeView CursorView::type() const { + return TypeView(clang_getCursorType(cursor_)); +} + +TypeView CursorView::result_type() const { + return TypeView(clang_getResultType(clang_getCursorType(cursor_))); +} + +CursorKind CursorView::kind() const { + return cursor_kind_for(clang_getCursorKind(cursor_)); +} + +bool CursorView::is_null() const { + return clang_Cursor_isNull(cursor_); +} + +bool CursorView::is_definition() const { + return clang_isCursorDefinition(cursor_); +} + +bool CursorView::has_c_language() const { + return clang_getCursorLanguage(cursor_) == CXLanguage_C; +} + +int CursorView::argument_count() const { + return clang_Cursor_getNumArguments(cursor_); +} + +CursorView CursorView::argument(int index) const { + return CursorView(clang_Cursor_getArgument(cursor_, index)); +} + +void visit_children(CursorView cursor, VisitResult (*visitor)(CursorView cursor, CursorView parent, void* data), void* data) { + std::pair state{visitor, data}; + clang_visitChildren( + cursor.cursor_, + [](CXCursor cursor, CXCursor parent, CXClientData data) { + auto& state = *static_cast*>(data); + return child_visit_result_for(state.first(CursorView(cursor), CursorView(parent), state.second)); + }, + &state); +} + +class ParseSession::Impl { + public: + Impl(ParseSession& owner, const ParseOptions& options, const std::filesystem::path& source_file) + : owner_(owner), + source_files_(options.memory_resource), + source_file_by_handle_(options.memory_resource), + source_file_by_path_(options.memory_resource), + args_(options.memory_resource) { + if (index_.get() == nullptr) { + throw std::runtime_error("failed to create libclang index"); + } + + register_input_file(source_file); + parse_translation_unit(options, source_file); + } + + Impl(ParseSession& owner, + const ParseOptions& options, + std::span source_files, + std::string_view virtual_source_path, + std::string_view virtual_source) + : owner_(owner), + source_files_(options.memory_resource), + source_file_by_handle_(options.memory_resource), + source_file_by_path_(options.memory_resource), + args_(options.memory_resource) { + if (index_.get() == nullptr) { + throw std::runtime_error("failed to create libclang index"); + } + + register_input_files(source_files); + parse_translation_unit(options, virtual_source_path, virtual_source); + } + + [[nodiscard]] CXTranslationUnit translation_unit() const { return translation_unit_.get(); } + + void add_clang_diagnostics() { + const unsigned count = clang_getNumDiagnostics(translation_unit_.get()); + for (unsigned i = 0; i < count; ++i) { + CXDiagnostic diagnostic = clang_getDiagnostic(translation_unit_.get(), i); + + owner_.diagnostics_.add(diagnostics::Diagnostic{ + .code = diagnostics::DiagnosticCode::kClangDiagnostic, + .message = diagnostic_message(diagnostic), + .severity = diagnostic_severity_for(clang_getDiagnosticSeverity(diagnostic)), + .function = std::nullopt, + .location = source_location_for(clang_getDiagnosticLocation(diagnostic)), + }); + + clang_disposeDiagnostic(diagnostic); + } + } + + [[nodiscard]] facts::SourceLocation source_location_for(CXSourceLocation location) { + CXFile file = nullptr; + unsigned line = 0; + unsigned column = 0; + unsigned offset = 0; + + clang_getSpellingLocation(location, &file, &line, &column, &offset); + + return facts::SourceLocation{ + .file = get_source_file(file), + .line = line, + .column = column, + }; + } + + [[nodiscard]] bool is_input_source_file(CursorView cursor) { + CXFile file = nullptr; + unsigned line = 0; + unsigned column = 0; + unsigned offset = 0; + + clang_getSpellingLocation(clang_getCursorLocation(cursor.cursor_), &file, &line, &column, &offset); + + if (file == nullptr) { + return false; + } + if (const SourceFile* source_file = find_source_file_by_handle(file); source_file != nullptr) { + return source_file->origin == SourceFileOrigin::kInput; + } + + const std::string path = path_for_file(file); + const SourceFile* source_file = find_source_file_by_path(file, path); + return source_file != nullptr && source_file->origin == SourceFileOrigin::kInput; + } + + private: + void register_input_file(const std::filesystem::path& source_file) { + const std::string path = normalize_path(std::filesystem::absolute(source_file).lexically_normal().string()); + register_source_file(path, SourceFileOrigin::kInput, nullptr); + } + + void register_input_files(std::span source_files) { + source_files_.reserve(source_files.size()); + source_file_by_path_.reserve(source_files.size()); + + for (const std::filesystem::path& file : source_files) { + register_input_file(file); + } + } + + void parse_translation_unit(const ParseOptions& options, const std::filesystem::path& source_file) { + args_.reserve(options.clang_args.size()); + for (const std::string& arg : options.clang_args) { + args_.push_back(arg.c_str()); + } + + const std::string source_path = std::filesystem::absolute(source_file).lexically_normal().string(); + CXTranslationUnit raw_tu = nullptr; + const CXErrorCode parse_result = clang_parseTranslationUnit2(index_.get(), source_path.c_str(), args_.data(), static_cast(args_.size()), nullptr, 0, + CXTranslationUnit_KeepGoing, &raw_tu); + if (parse_result != CXError_Success || raw_tu == nullptr) { + throw std::runtime_error("failed to parse libclang translation unit"); + } + translation_unit_.reset(raw_tu); + } + + void parse_translation_unit(const ParseOptions& options, std::string_view virtual_source_path, std::string_view virtual_source) { + args_.reserve(options.clang_args.size()); + for (const std::string& arg : options.clang_args) { + args_.push_back(arg.c_str()); + } + + const std::string source_path(virtual_source_path); + CXUnsavedFile unsaved_file{ + .Filename = source_path.c_str(), + .Contents = virtual_source.data(), + .Length = static_cast(virtual_source.size()), + }; + + CXTranslationUnit raw_tu = nullptr; + const CXErrorCode parse_result = clang_parseTranslationUnit2(index_.get(), unsaved_file.Filename, args_.data(), static_cast(args_.size()), + &unsaved_file, 1, CXTranslationUnit_KeepGoing, &raw_tu); + if (parse_result != CXError_Success || raw_tu == nullptr) { + throw std::runtime_error("failed to parse aggregate libclang translation unit"); + } + translation_unit_.reset(raw_tu); + } + + [[nodiscard]] facts::SourceFileId get_source_file(CXFile file) { + if (SourceFile* source_file = find_source_file_by_handle(file); source_file != nullptr) { + return source_file->id; + } + + std::string path = path_for_file(file); + if (path.empty()) { + path = ""; + } + if (SourceFile* source_file = find_source_file_by_path(file, path); source_file != nullptr) { + return source_file->id; + } + + return register_source_file(path, SourceFileOrigin::kDiscovered, file)->id; + } + + [[nodiscard]] SourceFile* find_source_file_by_handle(CXFile file) { + if (file == nullptr) { + return nullptr; + } + const auto it = source_file_by_handle_.find(file); + if (it == source_file_by_handle_.end()) { + return nullptr; + } + return &source_files_[it->second]; + } + + [[nodiscard]] SourceFile* find_source_file_by_path(CXFile file, std::string_view path) { + if (path.empty()) { + return nullptr; + } + + const std::pmr::string key(path, owner_.memory_resource()); + const auto it = source_file_by_path_.find(key); + if (it == source_file_by_path_.end()) { + return nullptr; + } + SourceFile& source_file = source_files_[it->second]; + if (file != nullptr && source_file.file == nullptr) { + source_file.file = file; + source_file_by_handle_.emplace(file, it->second); + } + return &source_file; + } + + SourceFile* register_source_file(std::string_view path, SourceFileOrigin origin, CXFile file) { + if (SourceFile* existing = find_source_file_by_path(file, path); existing != nullptr) { + if (origin == SourceFileOrigin::kInput) { + existing->origin = SourceFileOrigin::kInput; + } + return existing; + } + + const facts::SourceFileId id = owner_.facts().add_source_file(path); + const size_t index = source_files_.size(); + source_files_.push_back(SourceFile{ + .file = file, + .id = id, + .origin = origin, + }); + source_file_by_path_.emplace(std::pmr::string(path, owner_.memory_resource()), index); + if (file != nullptr) { + source_file_by_handle_.emplace(file, index); + } + return &source_files_.back(); + } + + ParseSession& owner_; + std::pmr::vector source_files_; + std::pmr::unordered_map source_file_by_handle_; + std::pmr::unordered_map source_file_by_path_; + std::pmr::vector args_; + ClangIndexWrapper index_; + ClangTranslationUnitWrapper translation_unit_; +}; + +ParseSession::ParseSession(const ParseOptions& options, + diagnostics::DiagnosticSet& diagnostic_set, + facts::EntrypointFacts& facts, + const std::filesystem::path& source_file) + : memory_resource_(options.memory_resource), diagnostics_(diagnostic_set), facts_(facts) { + std::pmr::polymorphic_allocator allocator(memory_resource_); + impl_ = allocator.allocate(1); + try { + allocator.construct(impl_, *this, options, source_file); + } catch (...) { + allocator.deallocate(impl_, 1); + impl_ = nullptr; + throw; + } +} + +ParseSession::ParseSession(const ParseOptions& options, + diagnostics::DiagnosticSet& diagnostic_set, + facts::EntrypointFacts& facts, + std::span source_files, + std::string_view virtual_source_path, + std::string_view virtual_source) + : memory_resource_(options.memory_resource), diagnostics_(diagnostic_set), facts_(facts) { + std::pmr::polymorphic_allocator allocator(memory_resource_); + impl_ = allocator.allocate(1); + try { + allocator.construct(impl_, *this, options, source_files, virtual_source_path, virtual_source); + } catch (...) { + allocator.deallocate(impl_, 1); + impl_ = nullptr; + throw; + } +} + +ParseSession::~ParseSession() { + if (impl_ == nullptr) { + return; + } + std::pmr::polymorphic_allocator allocator(memory_resource_); + allocator.destroy(impl_); + allocator.deallocate(impl_, 1); +} + +CursorView ParseSession::root_cursor() const { + return CursorView(clang_getTranslationUnitCursor(impl_->translation_unit())); +} + +void ParseSession::add_clang_diagnostics() { + impl_->add_clang_diagnostics(); +} + +facts::SourceLocation ParseSession::source_location_for(CursorView cursor) { + return impl_->source_location_for(clang_getCursorLocation(cursor.cursor_)); +} + +bool ParseSession::is_input_source_file(CursorView cursor) { + return impl_->is_input_source_file(cursor); +} + +} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h new file mode 100644 index 0000000000..3976c7a735 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h @@ -0,0 +1,112 @@ +#pragma once + +#include "clang_adapter/parse.h" +#include "diagnostics/diagnostics.h" +#include "facts/entrypoint_facts.h" +#include "facts/facts.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace entrypoint_codegen::clang_adapter { + +enum class CursorKind : uint8_t { + kOther, + kFunctionDecl, + kAnnotateAttr, + kFieldDecl, + kStructDecl, + kTypeAliasDecl, +}; + +enum class VisitResult : uint8_t { + kBreak, + kContinue, + kRecurse, +}; + +[[nodiscard]] std::string normalize_path(std::string path); + +class CursorView; + +class TypeView { + public: + explicit TypeView(CXType type) : type_(type) {} + + [[nodiscard]] std::string spelling() const; + [[nodiscard]] CursorView canonical_declaration() const; + + private: + friend class CursorView; + + CXType type_; +}; + +class CursorView { + public: + explicit CursorView(CXCursor cursor) : cursor_(cursor) {} + + [[nodiscard]] std::string spelling() const; + [[nodiscard]] std::string raw_comment() const; + [[nodiscard]] TypeView type() const; + [[nodiscard]] TypeView result_type() const; + [[nodiscard]] CursorKind kind() const; + [[nodiscard]] bool is_null() const; + [[nodiscard]] bool is_definition() const; + [[nodiscard]] bool has_c_language() const; + [[nodiscard]] int argument_count() const; + [[nodiscard]] CursorView argument(int index) const; + + private: + friend class ParseSession; + friend void visit_children(CursorView cursor, VisitResult (*visitor)(CursorView cursor, CursorView parent, void* data), void* data); + + CXCursor cursor_; +}; + +void visit_children(CursorView cursor, VisitResult (*visitor)(CursorView cursor, CursorView parent, void* data), void* data); + +class ParseSession { + public: + ParseSession(const ParseOptions& options, + diagnostics::DiagnosticSet& diagnostic_set, + facts::EntrypointFacts& facts, + const std::filesystem::path& source_file); + ParseSession(const ParseOptions& options, + diagnostics::DiagnosticSet& diagnostic_set, + facts::EntrypointFacts& facts, + std::span source_files, + std::string_view virtual_source_path, + std::string_view virtual_source); + ~ParseSession(); + + ParseSession(const ParseSession&) = delete; + ParseSession& operator=(const ParseSession&) = delete; + + [[nodiscard]] std::pmr::memory_resource* memory_resource() const { return memory_resource_; } + facts::EntrypointFacts& facts() { return facts_; } + [[nodiscard]] const facts::EntrypointFacts& facts() const { return facts_; } + + void add_clang_diagnostics(); + + [[nodiscard]] CursorView root_cursor() const; + [[nodiscard]] facts::SourceLocation source_location_for(CursorView cursor); + [[nodiscard]] bool is_input_source_file(CursorView cursor); + + private: + class Impl; + + std::pmr::memory_resource* memory_resource_; + diagnostics::DiagnosticSet& diagnostics_; + facts::EntrypointFacts& facts_; + Impl* impl_ = nullptr; +}; + +} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/cursor_reader.hpp b/pp/tools/entrypoint_codegen/clang_adapter/cursor_reader.hpp deleted file mode 100644 index e7cd33e719..0000000000 --- a/pp/tools/entrypoint_codegen/clang_adapter/cursor_reader.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "facts/entrypoint_facts.h" -#include "facts/facts.h" - -#include - -#include - -namespace entrypoint_codegen::clang_adapter { - -inline facts::StringId add_cursor_spelling(facts::EntrypointFacts& facts, CXCursor cursor) { - CXString spelling = clang_getCursorSpelling(cursor); - const char* raw = clang_getCString(spelling); - const facts::StringId id = facts.add_string(raw == nullptr ? std::string_view() : std::string_view(raw)); - clang_disposeString(spelling); - return id; -} - -inline facts::StringId add_type_spelling(facts::EntrypointFacts& facts, CXType type) { - CXString spelling = clang_getTypeSpelling(type); - const char* raw = clang_getCString(spelling); - const facts::StringId id = facts.add_string(raw == nullptr ? std::string_view() : std::string_view(raw)); - clang_disposeString(spelling); - return id; -} - -inline facts::StringId add_cursor_raw_comment(facts::EntrypointFacts& facts, CXCursor cursor) { - CXString comment = clang_Cursor_getRawCommentText(cursor); - const char* raw = clang_getCString(comment); - const facts::StringId id = facts.add_string(raw == nullptr ? std::string_view() : std::string_view(raw)); - clang_disposeString(comment); - return id; -} - -inline bool cursor_spelling_equals(CXCursor cursor, std::string_view expected) { - CXString spelling = clang_getCursorSpelling(cursor); - const char* raw = clang_getCString(spelling); - const bool result = (raw == nullptr ? std::string_view() : std::string_view(raw)) == expected; - clang_disposeString(spelling); - return result; -} - -inline bool cursor_spelling_starts_with(CXCursor cursor, std::string_view prefix) { - CXString spelling = clang_getCursorSpelling(cursor); - const char* raw = clang_getCString(spelling); - const std::string_view view = raw == nullptr ? std::string_view() : std::string_view(raw); - const bool result = view.substr(0, prefix.size()) == prefix; - clang_disposeString(spelling); - return result; -} - -inline bool has_c_linkage(CXCursor function_cursor) { - return clang_getCursorLanguage(function_cursor) == CXLanguage_C; -} - -} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/function_extractor.hpp b/pp/tools/entrypoint_codegen/clang_adapter/function_extractor.hpp deleted file mode 100644 index 06f71cc909..0000000000 --- a/pp/tools/entrypoint_codegen/clang_adapter/function_extractor.hpp +++ /dev/null @@ -1,276 +0,0 @@ -#pragma once - -#include "clang_adapter/cursor_reader.hpp" -#include "clang_adapter/session.hpp" -#include "facts/facts.h" - -#include - -#include -#include - -namespace entrypoint_codegen::clang_adapter { - -struct ParamNameAndRole { - facts::StringId name; - facts::ParamRole role; -}; - -inline facts::ParamRole param_role_for(std::string_view name) { - if (name == "args") { - return facts::ParamRole::kArgs; - } - if (name == "res") { - return facts::ParamRole::kRes; - } - return facts::ParamRole::kOther; -} - -inline ParamNameAndRole add_param_name_and_role(facts::EntrypointFacts& facts, CXCursor cursor) { - CXString spelling = clang_getCursorSpelling(cursor); - const char* raw = clang_getCString(spelling); - const std::string_view name = raw == nullptr ? std::string_view() : std::string_view(raw); - const ParamNameAndRole result{ - .name = facts.add_string(name), - .role = param_role_for(name), - }; - clang_disposeString(spelling); - return result; -} - -inline facts::BridgeKind bridge_kind_from_annotation(std::string_view annotation) { - if (annotation == "prompp.entrypoint.cgo") { - return facts::BridgeKind::kCGo; - } - if (annotation == "prompp.entrypoint.fastcgo") { - return facts::BridgeKind::kFastCGo; - } - return facts::BridgeKind::kUnknown; -} - -inline facts::BridgeKind bridge_kind_from_annotation_cursor(CXCursor cursor) { - CXString spelling = clang_getCursorSpelling(cursor); - const char* raw = clang_getCString(spelling); - const facts::BridgeKind bridge_kind = bridge_kind_from_annotation(raw == nullptr ? std::string_view() : std::string_view(raw)); - clang_disposeString(spelling); - return bridge_kind; -} - -class FunctionExtractor { - public: - explicit FunctionExtractor(ParseSession& session) : session_(session) {} - - [[nodiscard]] bool is_candidate_function(CXCursor function_cursor) const { - return has_c_linkage(function_cursor) || cursor_spelling_starts_with(function_cursor, "prompp_"); - } - - void add_function(CXCursor function_cursor) { - std::pmr::vector params = extract_params(function_cursor); - - const facts::BridgeKind bridge_kind = extract_bridge_kind(function_cursor); - std::pmr::vector layouts(session_.memory_resource()); - if (bridge_kind == facts::BridgeKind::kFastCGo) { - layouts = extract_layouts(function_cursor); - } - - const CXType function_type = clang_getCursorType(function_cursor); - session_.facts().add_function(facts::FunctionDecl{ - .name = add_cursor_spelling(session_.facts(), function_cursor), - .return_type_spelling = add_type_spelling(session_.facts(), clang_getResultType(function_type)), - .documentation = add_cursor_raw_comment(session_.facts(), function_cursor), - .bridge_kind = bridge_kind, - .params = session_.facts().add_params(params), - .layouts = session_.facts().add_layouts(layouts), - .location = session_.source_location_for(clang_getCursorLocation(function_cursor)), - .has_c_linkage = has_c_linkage(function_cursor), - }); - } - - private: - [[nodiscard]] facts::BridgeKind extract_bridge_kind(CXCursor function_cursor) { - struct BridgeVisitorState { - facts::BridgeKind bridge_kind = facts::BridgeKind::kUnknown; - } visitor_state; - - clang_visitChildren( - function_cursor, - [](CXCursor cursor, CXCursor /*parent*/, CXClientData data) { - if (clang_getCursorKind(cursor) != CXCursor_AnnotateAttr) { - return CXChildVisit_Continue; - } - - auto& state = *static_cast(data); - const facts::BridgeKind next = bridge_kind_from_annotation_cursor(cursor); - if (next != facts::BridgeKind::kUnknown) { - state.bridge_kind = next; - return CXChildVisit_Break; - } - return CXChildVisit_Continue; - }, - &visitor_state); - - return visitor_state.bridge_kind; - } - - [[nodiscard]] std::pmr::vector extract_params(CXCursor function_cursor) { - std::pmr::vector params(session_.memory_resource()); - const int count = clang_Cursor_getNumArguments(function_cursor); - if (count <= 0) { - return params; - } - - params.reserve(static_cast(count)); - for (int i = 0; i < count; ++i) { - const CXCursor arg = clang_Cursor_getArgument(function_cursor, i); - const ParamNameAndRole name_and_role = add_param_name_and_role(session_.facts(), arg); - params.push_back(facts::ParamDecl{ - .name = name_and_role.name, - .type_spelling = add_type_spelling(session_.facts(), clang_getCursorType(arg)), - .role = name_and_role.role, - .location = session_.source_location_for(clang_getCursorLocation(arg)), - }); - } - return params; - } - - [[nodiscard]] std::pmr::vector extract_layouts(CXCursor function_cursor) { - std::pmr::vector layouts(session_.memory_resource()); - struct LayoutVisitorState { - FunctionExtractor& extractor; - std::pmr::vector& layouts; - } visitor_state{ - .extractor = *this, - .layouts = layouts, - }; - - clang_visitChildren( - function_cursor, - [](CXCursor cursor, CXCursor /*parent*/, CXClientData data) { - auto& state = *static_cast(data); - if (state.extractor.try_append_layout(cursor, state.layouts)) { - return CXChildVisit_Continue; - } - return CXChildVisit_Recurse; - }, - &visitor_state); - return layouts; - } - - [[nodiscard]] std::pmr::vector extract_fields(CXCursor struct_cursor) { - std::pmr::vector fields(session_.memory_resource()); - struct FieldVisitorState { - FunctionExtractor& extractor; - std::pmr::vector& fields; - } visitor_state{ - .extractor = *this, - .fields = fields, - }; - - clang_visitChildren( - struct_cursor, - [](CXCursor cursor, CXCursor /*parent*/, CXClientData data) { - if (clang_getCursorKind(cursor) != CXCursor_FieldDecl) { - return CXChildVisit_Continue; - } - - auto& state = *static_cast(data); - state.fields.push_back(facts::FieldDecl{ - .name = add_cursor_spelling(state.extractor.session_.facts(), cursor), - .type_spelling = add_type_spelling(state.extractor.session_.facts(), clang_getCursorType(cursor)), - .location = state.extractor.session_.source_location_for(clang_getCursorLocation(cursor)), - }); - return CXChildVisit_Continue; - }, - &visitor_state); - return fields; - } - - [[nodiscard]] std::pmr::vector extract_fields_from_alias(CXCursor alias_cursor) { - std::pmr::vector fields(session_.memory_resource()); - struct AliasVisitorState { - FunctionExtractor& extractor; - std::pmr::vector& fields; - bool found = false; - } visitor_state{ - .extractor = *this, - .fields = fields, - }; - - clang_visitChildren( - alias_cursor, - [](CXCursor cursor, CXCursor /*parent*/, CXClientData data) { - auto& state = *static_cast(data); - if (clang_getCursorKind(cursor) == CXCursor_StructDecl) { - state.fields = state.extractor.extract_fields(cursor); - state.found = true; - return CXChildVisit_Break; - } - return CXChildVisit_Recurse; - }, - &visitor_state); - - if (visitor_state.found) { - return fields; - } - - const CXType canonical_type = clang_getCanonicalType(clang_getCursorType(alias_cursor)); - const CXCursor declaration = clang_getTypeDeclaration(canonical_type); - if (!clang_Cursor_isNull(declaration) && clang_getCursorKind(declaration) == CXCursor_StructDecl) { - return extract_fields(declaration); - } - return fields; - } - - void append_struct_layout(CXCursor struct_cursor, facts::LayoutKind kind, std::pmr::vector& layouts) { - std::pmr::vector fields = extract_fields(struct_cursor); - layouts.push_back(facts::LayoutDecl{ - .kind = kind, - .fields = session_.facts().add_fields(fields), - .location = session_.source_location_for(clang_getCursorLocation(struct_cursor)), - }); - } - - void append_alias_layout(CXCursor alias_cursor, facts::LayoutKind kind, std::pmr::vector& layouts) { - std::pmr::vector fields = extract_fields_from_alias(alias_cursor); - if (fields.empty()) { - return; - } - - layouts.push_back(facts::LayoutDecl{ - .kind = kind, - .fields = session_.facts().add_fields(fields), - .location = session_.source_location_for(clang_getCursorLocation(alias_cursor)), - }); - } - - [[nodiscard]] bool try_append_layout(CXCursor cursor, std::pmr::vector& layouts) { - const CXCursorKind kind = clang_getCursorKind(cursor); - if (kind == CXCursor_StructDecl) { - if (cursor_spelling_equals(cursor, "Arguments")) { - append_struct_layout(cursor, facts::LayoutKind::kArguments, layouts); - return true; - } - if (cursor_spelling_equals(cursor, "Result")) { - append_struct_layout(cursor, facts::LayoutKind::kResult, layouts); - return true; - } - return false; - } - - if (kind == CXCursor_TypeAliasDecl) { - if (cursor_spelling_equals(cursor, "Arguments")) { - append_alias_layout(cursor, facts::LayoutKind::kArguments, layouts); - return true; - } - if (cursor_spelling_equals(cursor, "Result")) { - append_alias_layout(cursor, facts::LayoutKind::kResult, layouts); - return true; - } - } - return false; - } - - ParseSession& session_; -}; - -} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp b/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp index c5b49590a7..1d95d6c829 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp @@ -1,21 +1,270 @@ #include "clang_adapter/parse.h" -#include "clang_adapter/function_extractor.hpp" -#include "clang_adapter/session.hpp" - -#include +#include "clang_adapter/aggregate_source.h" +#include "clang_adapter/clang_runtime.h" +#include "contract/entrypoint_contract.h" +#include #include +#include +#include +#include + namespace entrypoint_codegen::clang_adapter { -facts::EntrypointFacts parse_files(const ParseOptions& options) { - if (options.source_files.empty()) { - throw std::invalid_argument("no source files provided"); +namespace { + +struct ParamNameAndRole { + facts::StringId name; + facts::ParamRole role; +}; + +ParamNameAndRole add_param_name_and_role(facts::EntrypointFacts& facts, CursorView cursor) { + const std::string name = cursor.spelling(); + return ParamNameAndRole{ + .name = facts.add_string(name), + .role = contract::param_role_for_name(name), + }; +} + +facts::BridgeKind bridge_kind_from_annotation_cursor(CursorView cursor) { + return contract::bridge_kind_for_annotation(cursor.spelling()); +} + +class FunctionExtractor { + public: + explicit FunctionExtractor(ParseSession& session) : session_(session) {} + + [[nodiscard]] bool is_candidate_function(CursorView function_cursor) const { + return contract::is_entrypoint_function_name(function_cursor.spelling()) || has_known_entrypoint_annotation(function_cursor); } - ParseSession session(options); - session.add_clang_diagnostics(); + void add_function(CursorView function_cursor) { + std::pmr::vector params = extract_params(function_cursor); + + const facts::BridgeKind bridge_kind = extract_bridge_kind(function_cursor); + std::pmr::vector layouts(session_.memory_resource()); + if (bridge_kind == facts::BridgeKind::kFastCGo) { + layouts = extract_layouts(function_cursor); + } + + session_.facts().add_function(facts::FunctionDecl{ + .name = session_.facts().add_string(function_cursor.spelling()), + .return_type_spelling = session_.facts().add_string(function_cursor.result_type().spelling()), + .documentation = session_.facts().add_string(function_cursor.raw_comment()), + .bridge_kind = bridge_kind, + .params = session_.facts().add_params(params), + .layouts = session_.facts().add_layouts(layouts), + .location = session_.source_location_for(function_cursor), + .has_c_linkage = function_cursor.has_c_language(), + }); + } + + private: + [[nodiscard]] facts::BridgeKind extract_bridge_kind(CursorView function_cursor) { + struct BridgeVisitorState { + facts::BridgeKind bridge_kind = facts::BridgeKind::kUnknown; + } visitor_state; + + visit_children( + function_cursor, + [](CursorView cursor, CursorView /*parent*/, void* data) { + if (cursor.kind() != CursorKind::kAnnotateAttr) { + return VisitResult::kContinue; + } + + auto& state = *static_cast(data); + const facts::BridgeKind next = bridge_kind_from_annotation_cursor(cursor); + if (next != facts::BridgeKind::kUnknown) { + state.bridge_kind = next; + return VisitResult::kBreak; + } + return VisitResult::kContinue; + }, + &visitor_state); + + return visitor_state.bridge_kind; + } + + [[nodiscard]] bool has_known_entrypoint_annotation(CursorView function_cursor) const { + struct AnnotationVisitorState { + bool found = false; + } visitor_state; + + visit_children( + function_cursor, + [](CursorView cursor, CursorView /*parent*/, void* data) { + if (cursor.kind() != CursorKind::kAnnotateAttr) { + return VisitResult::kContinue; + } + + auto& state = *static_cast(data); + if (bridge_kind_from_annotation_cursor(cursor) != facts::BridgeKind::kUnknown) { + state.found = true; + return VisitResult::kBreak; + } + return VisitResult::kContinue; + }, + &visitor_state); + + return visitor_state.found; + } + + [[nodiscard]] std::pmr::vector extract_params(CursorView function_cursor) { + std::pmr::vector params(session_.memory_resource()); + const int count = function_cursor.argument_count(); + if (count <= 0) { + return params; + } + + params.reserve(static_cast(count)); + for (int i = 0; i < count; ++i) { + const CursorView arg = function_cursor.argument(i); + const ParamNameAndRole name_and_role = add_param_name_and_role(session_.facts(), arg); + params.push_back(facts::ParamDecl{ + .name = name_and_role.name, + .type_spelling = session_.facts().add_string(arg.type().spelling()), + .role = name_and_role.role, + .location = session_.source_location_for(arg), + }); + } + return params; + } + [[nodiscard]] std::pmr::vector extract_layouts(CursorView function_cursor) { + std::pmr::vector layouts(session_.memory_resource()); + struct LayoutVisitorState { + FunctionExtractor& extractor; + std::pmr::vector& layouts; + } visitor_state{ + .extractor = *this, + .layouts = layouts, + }; + + visit_children( + function_cursor, + [](CursorView cursor, CursorView /*parent*/, void* data) { + auto& state = *static_cast(data); + if (state.extractor.try_append_layout(cursor, state.layouts)) { + return VisitResult::kContinue; + } + return VisitResult::kRecurse; + }, + &visitor_state); + return layouts; + } + + [[nodiscard]] std::pmr::vector extract_fields(CursorView struct_cursor) { + std::pmr::vector fields(session_.memory_resource()); + struct FieldVisitorState { + FunctionExtractor& extractor; + std::pmr::vector& fields; + } visitor_state{ + .extractor = *this, + .fields = fields, + }; + + visit_children( + struct_cursor, + [](CursorView field, CursorView /*parent*/, void* data) { + if (field.kind() != CursorKind::kFieldDecl) { + return VisitResult::kContinue; + } + + auto& state = *static_cast(data); + state.fields.push_back(facts::FieldDecl{ + .name = state.extractor.session_.facts().add_string(field.spelling()), + .type_spelling = state.extractor.session_.facts().add_string(field.type().spelling()), + .location = state.extractor.session_.source_location_for(field), + }); + return VisitResult::kContinue; + }, + &visitor_state); + return fields; + } + + [[nodiscard]] std::pmr::vector extract_fields_from_alias(CursorView alias_cursor) { + std::pmr::vector fields(session_.memory_resource()); + struct AliasVisitorState { + FunctionExtractor& extractor; + std::pmr::vector& fields; + bool found = false; + } visitor_state{ + .extractor = *this, + .fields = fields, + }; + + visit_children( + alias_cursor, + [](CursorView child, CursorView /*parent*/, void* data) { + auto& state = *static_cast(data); + if (child.kind() == CursorKind::kStructDecl) { + state.fields = state.extractor.extract_fields(child); + state.found = true; + return VisitResult::kBreak; + } + return VisitResult::kRecurse; + }, + &visitor_state); + + if (visitor_state.found) { + return fields; + } + + const CursorView declaration = alias_cursor.type().canonical_declaration(); + if (!declaration.is_null() && declaration.kind() == CursorKind::kStructDecl) { + return extract_fields(declaration); + } + return fields; + } + + void append_struct_layout(CursorView struct_cursor, facts::LayoutKind kind, std::pmr::vector& layouts) { + std::pmr::vector fields = extract_fields(struct_cursor); + layouts.push_back(facts::LayoutDecl{ + .kind = kind, + .fields = session_.facts().add_fields(fields), + .location = session_.source_location_for(struct_cursor), + }); + } + + void append_alias_layout(CursorView alias_cursor, facts::LayoutKind kind, std::pmr::vector& layouts) { + std::pmr::vector fields = extract_fields_from_alias(alias_cursor); + if (fields.empty()) { + return; + } + + layouts.push_back(facts::LayoutDecl{ + .kind = kind, + .fields = session_.facts().add_fields(fields), + .location = session_.source_location_for(alias_cursor), + }); + } + + [[nodiscard]] bool try_append_layout(CursorView cursor, std::pmr::vector& layouts) { + const CursorKind kind = cursor.kind(); + if (kind == CursorKind::kStructDecl) { + const std::optional layout_kind = contract::layout_kind_for_name(cursor.spelling()); + if (layout_kind.has_value()) { + append_struct_layout(cursor, *layout_kind, layouts); + return true; + } + return false; + } + + if (kind == CursorKind::kTypeAliasDecl) { + const std::optional layout_kind = contract::layout_kind_for_name(cursor.spelling()); + if (layout_kind.has_value()) { + append_alias_layout(cursor, *layout_kind, layouts); + return true; + } + } + return false; + } + + ParseSession& session_; +}; + +void scan_translation_unit(ParseSession& session) { FunctionExtractor extractor(session); struct TranslationUnitScanState { @@ -28,23 +277,36 @@ facts::EntrypointFacts parse_files(const ParseOptions& options) { .extractor = extractor, }; - CXCursor root = clang_getTranslationUnitCursor(session.translation_unit()); - clang_visitChildren( - root, - [](CXCursor cursor, CXCursor /*parent*/, CXClientData data) { + visit_children( + session.root_cursor(), + [](CursorView function, CursorView /*parent*/, void* data) { auto& state = *static_cast(data); - if (clang_getCursorKind(cursor) != CXCursor_FunctionDecl || !clang_isCursorDefinition(cursor)) { - return CXChildVisit_Recurse; + if (function.kind() != CursorKind::kFunctionDecl || !function.is_definition()) { + return VisitResult::kRecurse; } - if (!state.session.is_input_source_file(cursor) || !state.extractor.is_candidate_function(cursor)) { - return CXChildVisit_Continue; + if (!state.session.is_input_source_file(function) || !state.extractor.is_candidate_function(function)) { + return VisitResult::kContinue; } - state.extractor.add_function(cursor); - return CXChildVisit_Continue; + state.extractor.add_function(function); + return VisitResult::kContinue; }, &scan_state); +} + +} // namespace + +facts::EntrypointFacts parse_files(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set) { + if (options.source_files.empty()) { + throw std::invalid_argument("no source files provided"); + } + + facts::EntrypointFacts facts(options.memory_resource); + const AggregateSource aggregate_source = build_aggregate_source(options.source_files, options.memory_resource); + ParseSession session(options, diagnostic_set, facts, options.source_files, aggregate_source.path, aggregate_source.contents); + session.add_clang_diagnostics(); + scan_translation_unit(session); - return session.take_facts(); + return facts; } } // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse.h b/pp/tools/entrypoint_codegen/clang_adapter/parse.h index bc2f257bed..f85116247b 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse.h +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse.h @@ -1,5 +1,6 @@ #pragma once +#include "diagnostics/diagnostics.h" #include "facts/entrypoint_facts.h" #include @@ -15,6 +16,6 @@ struct ParseOptions { std::pmr::memory_resource* memory_resource = std::pmr::get_default_resource(); }; -facts::EntrypointFacts parse_files(const ParseOptions& options); +facts::EntrypointFacts parse_files(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set); } // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp new file mode 100644 index 0000000000..e93539661d --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp @@ -0,0 +1,214 @@ +#include "clang_adapter/parse.h" + +#include + +#include "diagnostics/diagnostics.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +std::filesystem::path test_tmp_dir() { + if (const char* test_tmpdir = std::getenv("TEST_TMPDIR"); test_tmpdir != nullptr) { + return test_tmpdir; + } + return std::filesystem::temp_directory_path(); +} + +std::filesystem::path write_source_file(std::string_view name, std::string_view source) { + const std::filesystem::path path = test_tmp_dir() / name; + std::ofstream out(path, std::ios::trunc); + out << source; + return path; +} + +TEST(ClangAdapterParseTest, RejectsEmptyInputList) { + const entrypoint_codegen::clang_adapter::ParseOptions options; + entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + + EXPECT_THROW(entrypoint_codegen::clang_adapter::parse_files(options, diagnostics), std::invalid_argument); +} + +TEST(ClangAdapterParseTest, ExtractsFastCgoFunctionFactsFromSourceFile) { + // Arrange + const std::filesystem::path source_file = write_source_file("entrypoint_codegen_parse_test.cpp", R"cpp( + extern "C" __attribute__((annotate("prompp.entrypoint.fastcgo"))) void prompp_store(void* args, void* res) { + struct Arguments { + int series; + }; + struct Result { + double value; + }; + } + )cpp"); + entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + + // Act + entrypoint_codegen::facts::EntrypointFacts facts = entrypoint_codegen::clang_adapter::parse_files( + entrypoint_codegen::clang_adapter::ParseOptions{ + .source_files = {source_file}, + .clang_args = {"-std=c++2b"}, + }, + diagnostics); + const auto functions = facts.functions(); + const entrypoint_codegen::facts::FunctionDecl* function = functions.empty() ? nullptr : &functions[0]; + const std::span params = + function == nullptr ? std::span() : facts.params(function->params); + const std::span layouts = + function == nullptr ? std::span() : facts.layouts(function->layouts); + const std::span argument_fields = + layouts.empty() ? std::span() : facts.fields(layouts[0].fields); + const std::span result_fields = + layouts.size() < 2 ? std::span() : facts.fields(layouts[1].fields); + + // Assert + ASSERT_EQ(functions.size(), 1); + ASSERT_NE(function, nullptr); + EXPECT_EQ(facts.string(function->name), "prompp_store"); + EXPECT_EQ(function->bridge_kind, entrypoint_codegen::facts::BridgeKind::kFastCGo); + EXPECT_TRUE(function->has_c_linkage); + ASSERT_EQ(params.size(), 2); + EXPECT_EQ(facts.string(params[0].name), "args"); + EXPECT_EQ(params[0].role, entrypoint_codegen::facts::ParamRole::kArgs); + EXPECT_EQ(facts.string(params[1].name), "res"); + EXPECT_EQ(params[1].role, entrypoint_codegen::facts::ParamRole::kRes); + ASSERT_EQ(layouts.size(), 2); + EXPECT_EQ(layouts[0].kind, entrypoint_codegen::facts::LayoutKind::kArguments); + EXPECT_EQ(layouts[1].kind, entrypoint_codegen::facts::LayoutKind::kResult); + ASSERT_EQ(argument_fields.size(), 1); + EXPECT_EQ(facts.string(argument_fields[0].name), "series"); + EXPECT_EQ(facts.string(argument_fields[0].type_spelling), "int"); + ASSERT_EQ(result_fields.size(), 1); + EXPECT_EQ(facts.string(result_fields[0].name), "value"); + EXPECT_EQ(facts.string(result_fields[0].type_spelling), "double"); +} + +TEST(ClangAdapterParseTest, RecordsClangDiagnosticsSeparately) { + // Arrange + const std::filesystem::path source_file = write_source_file("entrypoint_codegen_invalid_parse_test.cpp", "int broken = ;\n"); + entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + + // Act + entrypoint_codegen::clang_adapter::parse_files( + entrypoint_codegen::clang_adapter::ParseOptions{ + .source_files = {source_file}, + .clang_args = {"-std=c++2b"}, + }, + diagnostics); + const auto diagnostic_values = diagnostics.diagnostics(); + + // Assert + ASSERT_EQ(diagnostic_values.size(), 1); + EXPECT_EQ(diagnostic_values[0].code, entrypoint_codegen::diagnostics::DiagnosticCode::kClangDiagnostic); + EXPECT_EQ(diagnostic_values[0].severity, entrypoint_codegen::diagnostics::Severity::kError); +} + +TEST(ClangAdapterParseTest, ExtractsFunctionsFromMultipleInputFiles) { + // Arrange + const std::filesystem::path first_source_file = write_source_file("entrypoint_codegen_batch_first.cpp", R"cpp( + extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_first() {} + )cpp"); + const std::filesystem::path second_source_file = write_source_file("entrypoint_codegen_batch_second.cpp", R"cpp( + extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_second() {} + )cpp"); + entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + + // Act + entrypoint_codegen::facts::EntrypointFacts facts = entrypoint_codegen::clang_adapter::parse_files( + entrypoint_codegen::clang_adapter::ParseOptions{ + .source_files = {first_source_file, second_source_file}, + .clang_args = {"-std=c++2b"}, + }, + diagnostics); + const auto functions = facts.functions(); + + // Assert + EXPECT_TRUE(diagnostics.empty()); + ASSERT_EQ(functions.size(), 2); + EXPECT_EQ(facts.string(functions[0].name), "prompp_first"); + EXPECT_EQ(facts.string(functions[1].name), "prompp_second"); +} + +TEST(ClangAdapterParseTest, ReportsAggregateTranslationUnitInternalLinkageCollisions) { + // Arrange + const std::filesystem::path first_source_file = write_source_file("entrypoint_codegen_collision_first.cpp", R"cpp( + static int helper() { + return 1; + } + extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_first() { + (void)helper(); + } + )cpp"); + const std::filesystem::path second_source_file = write_source_file("entrypoint_codegen_collision_second.cpp", R"cpp( + static int helper() { + return 2; + } + extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_second() { + (void)helper(); + } + )cpp"); + entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + + // Act + entrypoint_codegen::clang_adapter::parse_files( + entrypoint_codegen::clang_adapter::ParseOptions{ + .source_files = {first_source_file, second_source_file}, + .clang_args = {"-std=c++2b"}, + }, + diagnostics); + const auto diagnostic_values = diagnostics.diagnostics(); + + // Assert + ASSERT_FALSE(diagnostic_values.empty()); + EXPECT_EQ(diagnostic_values[0].code, entrypoint_codegen::diagnostics::DiagnosticCode::kClangDiagnostic); + EXPECT_EQ(diagnostic_values[0].severity, entrypoint_codegen::diagnostics::Severity::kError); +} + +TEST(ClangAdapterParseTest, IgnoresUnannotatedExternCFunctionWithoutEntrypointPrefix) { + // Arrange + const std::filesystem::path source_file = write_source_file("entrypoint_codegen_c_helper.cpp", R"cpp( + extern "C" void helper_for_c_abi() {} + )cpp"); + entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + + // Act + entrypoint_codegen::facts::EntrypointFacts facts = entrypoint_codegen::clang_adapter::parse_files( + entrypoint_codegen::clang_adapter::ParseOptions{ + .source_files = {source_file}, + .clang_args = {"-std=c++2b"}, + }, + diagnostics); + + // Assert + EXPECT_TRUE(facts.functions().empty()); +} + +TEST(ClangAdapterParseTest, ExtractsAnnotatedFunctionWithoutEntrypointPrefix) { + // Arrange + const std::filesystem::path source_file = write_source_file("entrypoint_codegen_annotated_without_prefix.cpp", R"cpp( + extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void store() {} + )cpp"); + entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + + // Act + entrypoint_codegen::facts::EntrypointFacts facts = entrypoint_codegen::clang_adapter::parse_files( + entrypoint_codegen::clang_adapter::ParseOptions{ + .source_files = {source_file}, + .clang_args = {"-std=c++2b"}, + }, + diagnostics); + const auto functions = facts.functions(); + + // Assert + ASSERT_EQ(functions.size(), 1); + EXPECT_EQ(facts.string(functions[0].name), "store"); + EXPECT_EQ(functions[0].bridge_kind, entrypoint_codegen::facts::BridgeKind::kCGo); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/clang_adapter/session.hpp b/pp/tools/entrypoint_codegen/clang_adapter/session.hpp deleted file mode 100644 index 147a3be57f..0000000000 --- a/pp/tools/entrypoint_codegen/clang_adapter/session.hpp +++ /dev/null @@ -1,321 +0,0 @@ -#pragma once - -#include "clang_adapter/parse.h" -#include "facts/entrypoint_facts.h" -#include "facts/facts.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace entrypoint_codegen::clang_adapter { - -inline std::string clang_string_to_std_string(CXString value) { - const char* raw = clang_getCString(value); - std::string out = raw == nullptr ? std::string() : std::string(raw); - clang_disposeString(value); - return out; -} - -inline std::string normalize_path(std::string path) { - if (path.empty()) { - return path; - } - - constexpr std::string_view execroot_marker = "/execroot/_main/"; - if (const size_t marker = path.find(execroot_marker); marker != std::string::npos) { - return path.substr(marker + execroot_marker.size()); - } - - const std::filesystem::path absolute_path(path); - if (!absolute_path.is_absolute()) { - return path; - } - - std::error_code error; - const std::filesystem::path relative_path = std::filesystem::relative(absolute_path, std::filesystem::current_path(), error); - if (error || relative_path.empty()) { - return path; - } - - std::string relative = relative_path.generic_string(); - if (relative == "." || relative.starts_with("../")) { - return path; - } - return relative; -} - -inline std::string path_for_file(CXFile file) { - if (file == nullptr) { - return {}; - } - return normalize_path(clang_string_to_std_string(clang_getFileName(file))); -} - -inline std::pmr::string build_synthetic_source(std::span source_files, std::pmr::memory_resource* memory_resource) { - std::pmr::string source(memory_resource); - for (const std::filesystem::path& file : source_files) { - source += "#include \""; - source += file.string(); - source += "\"\n"; - } - return source; -} - -inline facts::StringId add_diagnostic_message(facts::EntrypointFacts& facts, CXDiagnostic diagnostic) { - CXString message = clang_formatDiagnostic(diagnostic, clang_defaultDiagnosticDisplayOptions()); - const char* raw = clang_getCString(message); - const facts::StringId id = facts.add_string(raw == nullptr ? std::string_view() : std::string_view(raw)); - clang_disposeString(message); - return id; -} - -inline facts::Severity diagnostic_severity_for(CXDiagnosticSeverity severity) { - if (severity == CXDiagnostic_Warning) { - return facts::Severity::kWarning; - } - if (severity == CXDiagnostic_Error || severity == CXDiagnostic_Fatal) { - return facts::Severity::kError; - } - return facts::Severity::kInfo; -} - -class ClangIndexWrapper { - public: - ClangIndexWrapper() : index_(clang_createIndex(0, 0)) {} - ~ClangIndexWrapper() { - if (index_ != nullptr) { - clang_disposeIndex(index_); - } - } - - ClangIndexWrapper(const ClangIndexWrapper&) = delete; - ClangIndexWrapper& operator=(const ClangIndexWrapper&) = delete; - - [[nodiscard]] CXIndex get() const { return index_; } - - private: - CXIndex index_ = nullptr; -}; - -class ClangTranslationUnitWrapper { - public: - ClangTranslationUnitWrapper() = default; - explicit ClangTranslationUnitWrapper(CXTranslationUnit translation_unit) : translation_unit_(translation_unit) {} - ~ClangTranslationUnitWrapper() { - if (translation_unit_ != nullptr) { - clang_disposeTranslationUnit(translation_unit_); - } - } - - ClangTranslationUnitWrapper(const ClangTranslationUnitWrapper&) = delete; - ClangTranslationUnitWrapper& operator=(const ClangTranslationUnitWrapper&) = delete; - - void reset(CXTranslationUnit translation_unit) { - if (translation_unit_ != nullptr) { - clang_disposeTranslationUnit(translation_unit_); - } - translation_unit_ = translation_unit; - } - - [[nodiscard]] CXTranslationUnit get() const { return translation_unit_; } - - private: - CXTranslationUnit translation_unit_ = nullptr; -}; - -class ParseSession { - public: - explicit ParseSession(const ParseOptions& options) - : memory_resource_(options.memory_resource), - facts_(options.memory_resource), - source_files_(options.memory_resource), - synthetic_source_(options.memory_resource), - args_(options.memory_resource) { - if (index_.get() == nullptr) { - throw std::runtime_error("failed to create libclang index"); - } - - register_input_files(options.source_files); - parse_translation_unit(options); - } - - [[nodiscard]] std::pmr::memory_resource* memory_resource() const { return memory_resource_; } - [[nodiscard]] CXTranslationUnit translation_unit() const { return translation_unit_.get(); } - - facts::EntrypointFacts& facts() { return facts_; } - [[nodiscard]] const facts::EntrypointFacts& facts() const { return facts_; } - - [[nodiscard]] facts::EntrypointFacts take_facts() { return std::move(facts_); } - - void add_clang_diagnostics() { - const unsigned count = clang_getNumDiagnostics(translation_unit_.get()); - for (unsigned i = 0; i < count; ++i) { - CXDiagnostic diagnostic = clang_getDiagnostic(translation_unit_.get(), i); - - facts_.add_diagnostic(facts::Diagnostic{ - .code = facts::DiagnosticCode::kClangDiagnostic, - .message = add_diagnostic_message(facts_, diagnostic), - .severity = diagnostic_severity_for(clang_getDiagnosticSeverity(diagnostic)), - .function = std::nullopt, - .location = source_location_for(clang_getDiagnosticLocation(diagnostic)), - }); - - clang_disposeDiagnostic(diagnostic); - } - } - - [[nodiscard]] facts::SourceLocation source_location_for(CXSourceLocation location) { - CXFile file = nullptr; - unsigned line = 0; - unsigned column = 0; - unsigned offset = 0; - - clang_getSpellingLocation(location, &file, &line, &column, &offset); - - return facts::SourceLocation{ - .file = get_source_file(file), - .line = line, - .column = column, - }; - } - - [[nodiscard]] bool is_input_source_file(CXCursor cursor) { - CXFile file = nullptr; - unsigned line = 0; - unsigned column = 0; - unsigned offset = 0; - - clang_getSpellingLocation(clang_getCursorLocation(cursor), &file, &line, &column, &offset); - - if (file == nullptr) { - return false; - } - if (const SourceFile* source_file = find_source_file_by_handle(file); source_file != nullptr) { - return source_file->origin == SourceFileOrigin::kInput; - } - - const std::string path = path_for_file(file); - const SourceFile* source_file = find_source_file_by_path(file, path); - return source_file != nullptr && source_file->origin == SourceFileOrigin::kInput; - } - - private: - enum class SourceFileOrigin : uint8_t { - kInput, - kDiscovered, - }; - - struct SourceFile { - CXFile file = nullptr; - facts::SourceFileId id; - SourceFileOrigin origin = SourceFileOrigin::kDiscovered; - }; - - void register_input_files(const std::vector& source_files) { - source_files_.reserve(source_files.size()); - - for (const std::filesystem::path& file : source_files) { - const std::string path = normalize_path(std::filesystem::absolute(file).lexically_normal().string()); - const facts::SourceFileId id = facts_.add_source_file(path); - source_files_.push_back(SourceFile{ - .file = nullptr, - .id = id, - .origin = SourceFileOrigin::kInput, - }); - } - } - - void parse_translation_unit(const ParseOptions& options) { - const std::string synthetic_path = "/tmp/entrypoint_codegen_batch.cpp"; - synthetic_source_ = build_synthetic_source(options.source_files, memory_resource_); - - CXUnsavedFile unsaved_file{ - .Filename = synthetic_path.c_str(), - .Contents = synthetic_source_.c_str(), - .Length = static_cast(synthetic_source_.size()), - }; - - args_.reserve(options.clang_args.size()); - for (const std::string& arg : options.clang_args) { - args_.push_back(arg.c_str()); - } - - CXTranslationUnit raw_tu = nullptr; - const CXErrorCode parse_result = clang_parseTranslationUnit2(index_.get(), synthetic_path.c_str(), args_.data(), static_cast(args_.size()), - &unsaved_file, 1, CXTranslationUnit_KeepGoing, &raw_tu); - if (parse_result != CXError_Success || raw_tu == nullptr) { - throw std::runtime_error("failed to parse libclang translation unit"); - } - translation_unit_.reset(raw_tu); - } - - [[nodiscard]] facts::SourceFileId get_source_file(CXFile file) { - if (SourceFile* source_file = find_source_file_by_handle(file); source_file != nullptr) { - return source_file->id; - } - - std::string path = path_for_file(file); - if (path.empty()) { - path = ""; - } - if (SourceFile* source_file = find_source_file_by_path(file, path); source_file != nullptr) { - return source_file->id; - } - - const facts::SourceFileId id = facts_.add_source_file(path); - source_files_.push_back(SourceFile{ - .file = file, - .id = id, - .origin = SourceFileOrigin::kDiscovered, - }); - return id; - } - - [[nodiscard]] SourceFile* find_source_file_by_handle(CXFile file) { - if (file == nullptr) { - return nullptr; - } - for (SourceFile& source_file : source_files_) { - if (source_file.file == file) { - return &source_file; - } - } - return nullptr; - } - - [[nodiscard]] SourceFile* find_source_file_by_path(CXFile file, std::string_view path) { - if (path.empty()) { - return nullptr; - } - - for (SourceFile& source_file : source_files_) { - if (facts_.string(facts_.source_file(source_file.id).path) == path) { - if (source_file.file == nullptr) { - source_file.file = file; - } - return &source_file; - } - } - return nullptr; - } - - std::pmr::memory_resource* memory_resource_; - facts::EntrypointFacts facts_; - std::pmr::vector source_files_; - std::pmr::string synthetic_source_; - std::pmr::vector args_; - ClangIndexWrapper index_; - ClangTranslationUnitWrapper translation_unit_; -}; - -} // namespace entrypoint_codegen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp new file mode 100644 index 0000000000..9a9fc27fee --- /dev/null +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp @@ -0,0 +1,47 @@ +#include "contract/entrypoint_contract.h" + +namespace entrypoint_codegen::contract { + +bool starts_with(std::string_view value, std::string_view prefix) { + return value.substr(0, prefix.size()) == prefix; +} + +bool is_entrypoint_function_name(std::string_view name) { + return starts_with(name, kFunctionNamePrefix); +} + +bool is_void_pointer_type(std::string_view type) { + return type == "void *" || type == "void*"; +} + +facts::BridgeKind bridge_kind_for_annotation(std::string_view annotation) { + if (annotation == kCGoAnnotation) { + return facts::BridgeKind::kCGo; + } + if (annotation == kFastCGoAnnotation) { + return facts::BridgeKind::kFastCGo; + } + return facts::BridgeKind::kUnknown; +} + +facts::ParamRole param_role_for_name(std::string_view name) { + if (name == kArgsParamName) { + return facts::ParamRole::kArgs; + } + if (name == kResParamName) { + return facts::ParamRole::kRes; + } + return facts::ParamRole::kOther; +} + +std::optional layout_kind_for_name(std::string_view name) { + if (name == kArgumentsLayoutName) { + return facts::LayoutKind::kArguments; + } + if (name == kResultLayoutName) { + return facts::LayoutKind::kResult; + } + return std::nullopt; +} + +} // namespace entrypoint_codegen::contract diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h new file mode 100644 index 0000000000..e63101db0f --- /dev/null +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h @@ -0,0 +1,26 @@ +#pragma once + +#include "facts/facts.h" + +#include +#include + +namespace entrypoint_codegen::contract { + +constexpr std::string_view kFunctionNamePrefix = "prompp_"; +constexpr std::string_view kCGoAnnotation = "prompp.entrypoint.cgo"; +constexpr std::string_view kFastCGoAnnotation = "prompp.entrypoint.fastcgo"; +constexpr std::string_view kArgumentsLayoutName = "Arguments"; +constexpr std::string_view kResultLayoutName = "Result"; +constexpr std::string_view kArgsParamName = "args"; +constexpr std::string_view kResParamName = "res"; + +[[nodiscard]] bool starts_with(std::string_view value, std::string_view prefix); +[[nodiscard]] bool is_entrypoint_function_name(std::string_view name); +[[nodiscard]] bool is_void_pointer_type(std::string_view type); + +[[nodiscard]] facts::BridgeKind bridge_kind_for_annotation(std::string_view annotation); +[[nodiscard]] facts::ParamRole param_role_for_name(std::string_view name); +[[nodiscard]] std::optional layout_kind_for_name(std::string_view name); + +} // namespace entrypoint_codegen::contract diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp new file mode 100644 index 0000000000..a105eec262 --- /dev/null +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp @@ -0,0 +1,40 @@ +#include "contract/entrypoint_contract.h" + +#include + +namespace { + +using entrypoint_codegen::facts::BridgeKind; +using entrypoint_codegen::facts::LayoutKind; +using entrypoint_codegen::facts::ParamRole; + +TEST(EntrypointContractTest, RecognizesPromppFunctionPrefix) { + EXPECT_TRUE(entrypoint_codegen::contract::is_entrypoint_function_name("prompp_fn")); + EXPECT_FALSE(entrypoint_codegen::contract::is_entrypoint_function_name("other_fn")); +} + +TEST(EntrypointContractTest, ClassifiesBridgeAnnotationNames) { + EXPECT_EQ(entrypoint_codegen::contract::bridge_kind_for_annotation("prompp.entrypoint.cgo"), BridgeKind::kCGo); + EXPECT_EQ(entrypoint_codegen::contract::bridge_kind_for_annotation("prompp.entrypoint.fastcgo"), BridgeKind::kFastCGo); + EXPECT_EQ(entrypoint_codegen::contract::bridge_kind_for_annotation("other"), BridgeKind::kUnknown); +} + +TEST(EntrypointContractTest, ClassifiesParameterNames) { + EXPECT_EQ(entrypoint_codegen::contract::param_role_for_name("args"), ParamRole::kArgs); + EXPECT_EQ(entrypoint_codegen::contract::param_role_for_name("res"), ParamRole::kRes); + EXPECT_EQ(entrypoint_codegen::contract::param_role_for_name("other"), ParamRole::kOther); +} + +TEST(EntrypointContractTest, ClassifiesLayoutNames) { + EXPECT_EQ(entrypoint_codegen::contract::layout_kind_for_name("Arguments"), LayoutKind::kArguments); + EXPECT_EQ(entrypoint_codegen::contract::layout_kind_for_name("Result"), LayoutKind::kResult); + EXPECT_FALSE(entrypoint_codegen::contract::layout_kind_for_name("Other").has_value()); +} + +TEST(EntrypointContractTest, RecognizesVoidPointerSpellings) { + EXPECT_TRUE(entrypoint_codegen::contract::is_void_pointer_type("void*")); + EXPECT_TRUE(entrypoint_codegen::contract::is_void_pointer_type("void *")); + EXPECT_FALSE(entrypoint_codegen::contract::is_void_pointer_type("const void*")); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.cpp new file mode 100644 index 0000000000..c372219db1 --- /dev/null +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.cpp @@ -0,0 +1,129 @@ +#include "diagnostics/diagnostic_catalog.h" + +namespace entrypoint_codegen::diagnostics { + +std::string_view diagnostic_code_name(DiagnosticCode code) { + switch (code) { + case DiagnosticCode::kClangDiagnostic: { + return "clang_diagnostic"; + } + case DiagnosticCode::kUnsupportedReturnType: { + return "unsupported_return_type"; + } + case DiagnosticCode::kUnsupportedParamCount: { + return "unsupported_param_count"; + } + case DiagnosticCode::kUnsupportedParamType: { + return "unsupported_param_type"; + } + case DiagnosticCode::kUnknownParamRole: { + return "unknown_param_role"; + } + case DiagnosticCode::kInvalidTwoParamOrder: { + return "invalid_two_param_order"; + } + case DiagnosticCode::kInvalidSecondParamRole: { + return "invalid_second_param_role"; + } + case DiagnosticCode::kMissingArgumentsLayout: { + return "missing_arguments_layout"; + } + case DiagnosticCode::kMissingResultLayout: { + return "missing_result_layout"; + } + case DiagnosticCode::kUnexpectedArgumentsLayout: { + return "unexpected_arguments_layout"; + } + case DiagnosticCode::kUnexpectedResultLayout: { + return "unexpected_result_layout"; + } + case DiagnosticCode::kMissingNamePrefix: { + return "missing_name_prefix"; + } + case DiagnosticCode::kMissingCLinkage: { + return "missing_c_linkage"; + } + case DiagnosticCode::kMissingEntrypointAttribute: { + return "missing_entrypoint_attribute"; + } + case DiagnosticCode::kRuntimeMemoryUsage: { + return "runtime_memory_usage"; + } + } + return "unknown_diagnostic"; +} + +std::string_view diagnostic_default_message(DiagnosticCode code) { + switch (code) { + case DiagnosticCode::kClangDiagnostic: { + return "Clang diagnostic"; + } + case DiagnosticCode::kUnsupportedReturnType: { + return "FastCGo entrypoint must return void"; + } + case DiagnosticCode::kUnsupportedParamCount: { + return "FastCGo entrypoint supports 0, 1, or 2 parameters"; + } + case DiagnosticCode::kUnsupportedParamType: { + return "FastCGo entrypoint parameters must be void*"; + } + case DiagnosticCode::kUnknownParamRole: { + return "FastCGo parameter must be named args or res"; + } + case DiagnosticCode::kInvalidTwoParamOrder: { + return "FastCGo two-parameter form must be args, res"; + } + case DiagnosticCode::kInvalidSecondParamRole: { + return "FastCGo second parameter must be res"; + } + case DiagnosticCode::kMissingArgumentsLayout: { + return "args parameter requires local Arguments layout"; + } + case DiagnosticCode::kMissingResultLayout: { + return "res parameter requires local Result layout"; + } + case DiagnosticCode::kUnexpectedArgumentsLayout: { + return "Arguments layout exists but args parameter is absent"; + } + case DiagnosticCode::kUnexpectedResultLayout: { + return "Result layout exists but res parameter is absent"; + } + case DiagnosticCode::kMissingNamePrefix: { + return "entrypoint function must use prompp_ prefix"; + } + case DiagnosticCode::kMissingCLinkage: { + return "entrypoint function must be declared extern \"C\""; + } + case DiagnosticCode::kMissingEntrypointAttribute: { + return "entrypoint function requires CGo or FastCGo annotation"; + } + case DiagnosticCode::kRuntimeMemoryUsage: { + return "runtime memory usage"; + } + } + return "unknown diagnostic"; +} + +std::string_view diagnostic_message(const Diagnostic& diagnostic) { + if (diagnostic.message.has_value()) { + return *diagnostic.message; + } + return diagnostic_default_message(diagnostic.code); +} + +std::string_view severity_name(Severity severity) { + switch (severity) { + case Severity::kInfo: { + return "info"; + } + case Severity::kWarning: { + return "warning"; + } + case Severity::kError: { + return "error"; + } + } + return "error"; +} + +} // namespace entrypoint_codegen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.h b/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.h new file mode 100644 index 0000000000..0b913c4d26 --- /dev/null +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.h @@ -0,0 +1,14 @@ +#pragma once + +#include "diagnostics/diagnostics.h" + +#include + +namespace entrypoint_codegen::diagnostics { + +std::string_view diagnostic_code_name(DiagnosticCode code); +std::string_view diagnostic_default_message(DiagnosticCode code); +std::string_view diagnostic_message(const Diagnostic& diagnostic); +std::string_view severity_name(Severity severity); + +} // namespace entrypoint_codegen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog_tests.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog_tests.cpp new file mode 100644 index 0000000000..7a86e4e775 --- /dev/null +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog_tests.cpp @@ -0,0 +1,94 @@ +#include "diagnostics/diagnostic_catalog.h" + +#include + +#include "facts/facts.h" + +#include +#include + +namespace { + +using entrypoint_codegen::diagnostics::Diagnostic; +using entrypoint_codegen::diagnostics::DiagnosticCode; +using entrypoint_codegen::diagnostics::Severity; +using entrypoint_codegen::facts::SourceFileId; +using entrypoint_codegen::facts::SourceLocation; + +TEST(DiagnosticCatalogTest, NamesDiagnosticCode) { + // Act + const std::string_view code = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kMissingNamePrefix); + + // Assert + EXPECT_EQ(code, "missing_name_prefix"); +} + +TEST(DiagnosticCatalogTest, NamesParameterRoleDiagnosticsWithDistinctCodes) { + // Act + const std::string_view unknown_role = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kUnknownParamRole); + const std::string_view invalid_order = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidTwoParamOrder); + const std::string_view invalid_second = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidSecondParamRole); + + // Assert + EXPECT_EQ(unknown_role, "unknown_param_role"); + EXPECT_EQ(invalid_order, "invalid_two_param_order"); + EXPECT_EQ(invalid_second, "invalid_second_param_role"); +} + +TEST(DiagnosticCatalogTest, ProvidesDefaultMessageForDiagnosticCode) { + // Act + const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_default_message(DiagnosticCode::kMissingNamePrefix); + + // Assert + EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); +} + +TEST(DiagnosticCatalogTest, UsesDiagnosticMessageWhenPresent) { + // Arrange + const SourceLocation location{.file = SourceFileId(0), .line = 1, .column = 1}; + const Diagnostic diagnostic{ + .code = DiagnosticCode::kClangDiagnostic, + .message = "clang says no", + .severity = Severity::kError, + .function = std::nullopt, + .location = location, + }; + + // Act + const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_message(diagnostic); + + // Assert + EXPECT_EQ(message, "clang says no"); +} + +TEST(DiagnosticCatalogTest, FallsBackToDefaultMessageWhenDiagnosticMessageIsAbsent) { + // Arrange + const SourceLocation location{.file = SourceFileId(0), .line = 1, .column = 1}; + const Diagnostic diagnostic{ + .code = DiagnosticCode::kMissingNamePrefix, + .message = std::nullopt, + .severity = Severity::kError, + .function = std::nullopt, + .location = location, + }; + + // Act + const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_message(diagnostic); + + // Assert + EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); +} + +TEST(DiagnosticCatalogTest, NamesSeverityValues) { + // Act + const std::string_view info = entrypoint_codegen::diagnostics::severity_name(Severity::kInfo); + const std::string_view warning = entrypoint_codegen::diagnostics::severity_name(Severity::kWarning); + const std::string_view error = entrypoint_codegen::diagnostics::severity_name(Severity::kError); + + // Assert + EXPECT_EQ(info, "info"); + EXPECT_EQ(warning, "warning"); + EXPECT_EQ(error, "error"); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp new file mode 100644 index 0000000000..605a4439e2 --- /dev/null +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp @@ -0,0 +1,25 @@ +#include "diagnostics/diagnostics.h" + +#include + +namespace entrypoint_codegen::diagnostics { + +DiagnosticSet::DiagnosticSet(std::pmr::memory_resource* memory_resource) : diagnostics_(memory_resource) {} + +void DiagnosticSet::add(Diagnostic diagnostic) { + diagnostics_.push_back(std::move(diagnostic)); +} + +std::span DiagnosticSet::diagnostics() const noexcept { + return diagnostics_; +} + +uint32_t DiagnosticSet::count() const noexcept { + return static_cast(diagnostics_.size()); +} + +bool DiagnosticSet::empty() const noexcept { + return diagnostics_.empty(); +} + +} // namespace entrypoint_codegen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h new file mode 100644 index 0000000000..3190d7abea --- /dev/null +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h @@ -0,0 +1,60 @@ +#pragma once + +#include "facts/facts.h" + +#include +#include +#include +#include +#include +#include + +namespace entrypoint_codegen::diagnostics { + +enum class Severity : uint8_t { + kInfo, + kWarning, + kError, +}; + +enum class DiagnosticCode : uint8_t { + kClangDiagnostic, + kUnsupportedReturnType, + kUnsupportedParamCount, + kUnsupportedParamType, + kUnknownParamRole, + kInvalidTwoParamOrder, + kInvalidSecondParamRole, + kMissingArgumentsLayout, + kMissingResultLayout, + kUnexpectedArgumentsLayout, + kUnexpectedResultLayout, + kMissingNamePrefix, + kMissingCLinkage, + kMissingEntrypointAttribute, + kRuntimeMemoryUsage, +}; + +struct Diagnostic { + DiagnosticCode code; + std::optional message; + Severity severity; + std::optional function; + std::optional location; +}; + +class DiagnosticSet { + public: + explicit DiagnosticSet(std::pmr::memory_resource* memory_resource = std::pmr::get_default_resource()); + + void add(Diagnostic diagnostic); + + [[nodiscard]] std::span diagnostics() const noexcept; + [[nodiscard]] uint32_t count() const noexcept; + [[nodiscard]] bool empty() const noexcept; + + private: + std::pmr::vector diagnostics_; +}; + +} // namespace entrypoint_codegen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp new file mode 100644 index 0000000000..e4972b5d82 --- /dev/null +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp @@ -0,0 +1,49 @@ +#include "diagnostics/diagnostics.h" + +#include + +#include + +namespace { + +using entrypoint_codegen::diagnostics::Diagnostic; +using entrypoint_codegen::diagnostics::DiagnosticCode; +using entrypoint_codegen::diagnostics::DiagnosticSet; +using entrypoint_codegen::diagnostics::Severity; +using entrypoint_codegen::facts::SourceFileId; +using entrypoint_codegen::facts::SourceLocation; + +class DiagnosticSetTest : public testing::Test { + protected: + DiagnosticSet diagnostics_; +}; + +TEST_F(DiagnosticSetTest, StartsEmpty) { + EXPECT_TRUE(diagnostics_.empty()); + EXPECT_EQ(diagnostics_.count(), 0); +} + +TEST_F(DiagnosticSetTest, StoresDiagnosticsAndCountsThem) { + // Arrange + const SourceLocation location{.file = SourceFileId(0), .line = 3, .column = 5}; + + // Act + diagnostics_.add(Diagnostic{ + .code = DiagnosticCode::kMissingNamePrefix, + .message = std::nullopt, + .severity = Severity::kError, + .function = std::nullopt, + .location = location, + }); + const auto stored = diagnostics_.diagnostics(); + + // Assert + EXPECT_EQ(diagnostics_.count(), 1); + ASSERT_EQ(stored.size(), 1); + EXPECT_EQ(stored[0].code, DiagnosticCode::kMissingNamePrefix); + ASSERT_TRUE(stored[0].location.has_value()); + EXPECT_EQ(stored[0].location->line, location.line); + EXPECT_EQ(stored[0].location->column, location.column); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/emit/diagnostic_text.cpp b/pp/tools/entrypoint_codegen/emit/diagnostic_text.cpp deleted file mode 100644 index dd4a3ed57e..0000000000 --- a/pp/tools/entrypoint_codegen/emit/diagnostic_text.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "emit/diagnostic_text.h" - -namespace entrypoint_codegen::emit { - -std::string_view diagnostic_code_name(facts::DiagnosticCode code) { - switch (code) { - case facts::DiagnosticCode::kClangDiagnostic: { - return "clang_diagnostic"; - } - case facts::DiagnosticCode::kUnsupportedReturnType: { - return "unsupported_return_type"; - } - case facts::DiagnosticCode::kUnsupportedParamCount: { - return "unsupported_param_count"; - } - case facts::DiagnosticCode::kUnsupportedParamType: { - return "unsupported_param_type"; - } - case facts::DiagnosticCode::kUnknownParamRole: - case facts::DiagnosticCode::kInvalidTwoParamOrder: - case facts::DiagnosticCode::kInvalidSecondParamRole: { - return "unknown_param_role"; - } - case facts::DiagnosticCode::kMissingArgumentsLayout: { - return "missing_arguments_layout"; - } - case facts::DiagnosticCode::kMissingResultLayout: { - return "missing_result_layout"; - } - case facts::DiagnosticCode::kUnexpectedArgumentsLayout: { - return "unexpected_arguments_layout"; - } - case facts::DiagnosticCode::kUnexpectedResultLayout: { - return "unexpected_result_layout"; - } - case facts::DiagnosticCode::kMissingNamePrefix: { - return "missing_name_prefix"; - } - case facts::DiagnosticCode::kMissingCLinkage: { - return "missing_c_linkage"; - } - case facts::DiagnosticCode::kMissingEntrypointAttribute: { - return "missing_entrypoint_attribute"; - } - case facts::DiagnosticCode::kRuntimeMemoryUsage: { - return "runtime_memory_usage"; - } - } - return "unknown_diagnostic"; -} - -std::string_view diagnostic_default_message(facts::DiagnosticCode code) { - switch (code) { - case facts::DiagnosticCode::kClangDiagnostic: { - return "Clang diagnostic"; - } - case facts::DiagnosticCode::kUnsupportedReturnType: { - return "FastCGo entrypoint must return void"; - } - case facts::DiagnosticCode::kUnsupportedParamCount: { - return "FastCGo entrypoint supports 0, 1, or 2 parameters"; - } - case facts::DiagnosticCode::kUnsupportedParamType: { - return "FastCGo entrypoint parameters must be void*"; - } - case facts::DiagnosticCode::kUnknownParamRole: { - return "FastCGo parameter must be named args or res"; - } - case facts::DiagnosticCode::kInvalidTwoParamOrder: { - return "FastCGo two-parameter form must be args, res"; - } - case facts::DiagnosticCode::kInvalidSecondParamRole: { - return "FastCGo second parameter must be res"; - } - case facts::DiagnosticCode::kMissingArgumentsLayout: { - return "args parameter requires local Arguments layout"; - } - case facts::DiagnosticCode::kMissingResultLayout: { - return "res parameter requires local Result layout"; - } - case facts::DiagnosticCode::kUnexpectedArgumentsLayout: { - return "Arguments layout exists but args parameter is absent"; - } - case facts::DiagnosticCode::kUnexpectedResultLayout: { - return "Result layout exists but res parameter is absent"; - } - case facts::DiagnosticCode::kMissingNamePrefix: { - return "entrypoint function must use prompp_ prefix"; - } - case facts::DiagnosticCode::kMissingCLinkage: { - return "entrypoint function must be declared extern \"C\""; - } - case facts::DiagnosticCode::kMissingEntrypointAttribute: { - return "entrypoint function requires CGo or FastCGo annotation"; - } - case facts::DiagnosticCode::kRuntimeMemoryUsage: { - return "runtime memory usage"; - } - } - return "unknown diagnostic"; -} - -std::string_view diagnostic_message(const facts::EntrypointFacts& facts, const facts::Diagnostic& diagnostic) { - if (diagnostic.message.has_value()) { - return facts.string(*diagnostic.message); - } - return diagnostic_default_message(diagnostic.code); -} - -} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/diagnostic_text.h b/pp/tools/entrypoint_codegen/emit/diagnostic_text.h deleted file mode 100644 index 26f1ad10ea..0000000000 --- a/pp/tools/entrypoint_codegen/emit/diagnostic_text.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "facts/entrypoint_facts.h" -#include "facts/facts.h" - -#include - -namespace entrypoint_codegen::emit { - -std::string_view diagnostic_code_name(facts::DiagnosticCode code); -std::string_view diagnostic_default_message(facts::DiagnosticCode code); -std::string_view diagnostic_message(const facts::EntrypointFacts& facts, const facts::Diagnostic& diagnostic); - -} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/diagnostic_text_tests.cpp b/pp/tools/entrypoint_codegen/emit/diagnostic_text_tests.cpp deleted file mode 100644 index 98186aa99d..0000000000 --- a/pp/tools/entrypoint_codegen/emit/diagnostic_text_tests.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "emit/diagnostic_text.h" - -#include - -#include - -namespace { - -using entrypoint_codegen::facts::Diagnostic; -using entrypoint_codegen::facts::DiagnosticCode; -using entrypoint_codegen::facts::EntrypointFacts; -using entrypoint_codegen::facts::Severity; -using entrypoint_codegen::facts::SourceLocation; - -SourceLocation add_source_file(EntrypointFacts& facts) { - return SourceLocation{ - .file = facts.add_source_file("entrypoint.cpp"), - .line = 1, - .column = 1, - }; -} - -TEST(DiagnosticTextTest, ReturnsStaticMessageForValidationCode) { - // Arrange - EntrypointFacts facts; - const Diagnostic diagnostic{ - .code = DiagnosticCode::kMissingNamePrefix, - .message = std::nullopt, - .severity = Severity::kError, - .function = std::nullopt, - .location = add_source_file(facts), - }; - - // Act - const std::string_view code = entrypoint_codegen::emit::diagnostic_code_name(diagnostic.code); - const std::string_view message = entrypoint_codegen::emit::diagnostic_message(facts, diagnostic); - - // Assert - EXPECT_EQ(code, "missing_name_prefix"); - EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); -} - -TEST(DiagnosticTextTest, ReturnsDynamicMessageWhenPresent) { - // Arrange - EntrypointFacts facts; - const Diagnostic diagnostic{ - .code = DiagnosticCode::kClangDiagnostic, - .message = facts.add_string("clang says no"), - .severity = Severity::kError, - .function = std::nullopt, - .location = add_source_file(facts), - }; - - // Act - const std::string_view message = entrypoint_codegen::emit::diagnostic_message(facts, diagnostic); - - // Assert - EXPECT_EQ(message, "clang says no"); -} - -} // namespace diff --git a/pp/tools/entrypoint_codegen/emit/diagnostics.cpp b/pp/tools/entrypoint_codegen/emit/diagnostics.cpp index b20990e2bc..c4ec1512ff 100644 --- a/pp/tools/entrypoint_codegen/emit/diagnostics.cpp +++ b/pp/tools/entrypoint_codegen/emit/diagnostics.cpp @@ -1,37 +1,19 @@ #include "emit/diagnostics.h" -#include "emit/diagnostic_text.h" +#include "diagnostics/diagnostic_catalog.h" #include -#include namespace entrypoint_codegen::emit { -namespace { - -std::string_view severity_name(facts::Severity severity) { - switch (severity) { - case facts::Severity::kInfo: { - return "info"; - } - case facts::Severity::kWarning: { - return "warning"; +void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { + for (const diagnostics::Diagnostic& diagnostic : diagnostic_set.diagnostics()) { + if (diagnostic.location.has_value()) { + const facts::SourceLocation location = *diagnostic.location; + out << facts.string(facts.source_file(location.file).path) << ":" << location.line << ":" << location.column << ": "; } - case facts::Severity::kError: { - return "error"; - } - } - return "error"; -} - -} // namespace - -void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts) { - for (const facts::Diagnostic& diagnostic : facts.diagnostics()) { - const facts::SourceLocation location = diagnostic.location; - out << facts.string(facts.source_file(location.file).path) << ":" << location.line << ":" << location.column - << ": " << severity_name(diagnostic.severity) << ": " << diagnostic_message(facts, diagnostic) - << " [" << diagnostic_code_name(diagnostic.code) << "]\n"; + out << diagnostics::severity_name(diagnostic.severity) << ": " << diagnostics::diagnostic_message(diagnostic) << " [" + << diagnostics::diagnostic_code_name(diagnostic.code) << "]\n"; } } diff --git a/pp/tools/entrypoint_codegen/emit/diagnostics.h b/pp/tools/entrypoint_codegen/emit/diagnostics.h index 288c3820b7..fdfd9129be 100644 --- a/pp/tools/entrypoint_codegen/emit/diagnostics.h +++ b/pp/tools/entrypoint_codegen/emit/diagnostics.h @@ -1,11 +1,12 @@ #pragma once +#include "diagnostics/diagnostics.h" #include "facts/entrypoint_facts.h" #include namespace entrypoint_codegen::emit { -void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts); +void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set); } // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp b/pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp index 9e94580454..db690d1b51 100644 --- a/pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp +++ b/pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp @@ -2,39 +2,60 @@ #include +#include "diagnostics/diagnostics.h" + #include #include namespace { -using entrypoint_codegen::facts::Diagnostic; -using entrypoint_codegen::facts::DiagnosticCode; +using entrypoint_codegen::diagnostics::Diagnostic; +using entrypoint_codegen::diagnostics::DiagnosticCode; +using entrypoint_codegen::diagnostics::DiagnosticSet; +using entrypoint_codegen::diagnostics::Severity; using entrypoint_codegen::facts::EntrypointFacts; -using entrypoint_codegen::facts::Severity; using entrypoint_codegen::facts::SourceLocation; -TEST(EmitDiagnosticsTest, WritesCompilerStyleDiagnosticLine) { +class EmitDiagnosticsTest : public testing::Test { + protected: + EntrypointFacts facts_; + DiagnosticSet diagnostics_; + std::ostringstream output_; +}; + +TEST_F(EmitDiagnosticsTest, WritesCompilerStyleDiagnosticLine) { // Arrange - EntrypointFacts facts; - const SourceLocation location{ - .file = facts.add_source_file("entrypoint.cpp"), - .line = 7, - .column = 9, - }; - facts.add_diagnostic(Diagnostic{ + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 9}; + diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kMissingNamePrefix, .message = std::nullopt, .severity = Severity::kError, .function = std::nullopt, .location = location, }); - std::ostringstream output; // Act - entrypoint_codegen::emit::write_diagnostics(output, facts); + entrypoint_codegen::emit::write_diagnostics(output_, facts_, diagnostics_); + + // Assert + EXPECT_EQ(output_.str(), "entrypoint.cpp:7:9: error: entrypoint function must use prompp_ prefix [missing_name_prefix]\n"); +} + +TEST_F(EmitDiagnosticsTest, WritesLocationlessDiagnosticLine) { + // Arrange + diagnostics_.add(Diagnostic{ + .code = DiagnosticCode::kRuntimeMemoryUsage, + .message = std::nullopt, + .severity = Severity::kInfo, + .function = std::nullopt, + .location = std::nullopt, + }); + + // Act + entrypoint_codegen::emit::write_diagnostics(output_, facts_, diagnostics_); // Assert - EXPECT_EQ(output.str(), "entrypoint.cpp:7:9: error: entrypoint function must use prompp_ prefix [missing_name_prefix]\n"); + EXPECT_EQ(output_.str(), "info: runtime memory usage [runtime_memory_usage]\n"); } } // namespace diff --git a/pp/tools/entrypoint_codegen/emit/json.cpp b/pp/tools/entrypoint_codegen/emit/json.cpp index 50e4aec299..72c68a402f 100644 --- a/pp/tools/entrypoint_codegen/emit/json.cpp +++ b/pp/tools/entrypoint_codegen/emit/json.cpp @@ -1,6 +1,6 @@ #include "emit/json.h" -#include "emit/diagnostic_text.h" +#include "diagnostics/diagnostic_catalog.h" #include #include @@ -12,9 +12,11 @@ namespace entrypoint_codegen::emit { namespace { std::string json_escape(std::string_view input) { + constexpr char kHexDigits[] = "0123456789abcdef"; + std::string out; out.reserve(input.size() + 8); - for (const char ch : input) { + for (const unsigned char ch : input) { switch (ch) { case '\\': { out += "\\\\"; @@ -37,7 +39,13 @@ std::string json_escape(std::string_view input) { break; } default: { - out.push_back(ch); + if (ch < 0x20) { + out += "\\u00"; + out.push_back(kHexDigits[ch >> 4]); + out.push_back(kHexDigits[ch & 0x0F]); + break; + } + out.push_back(static_cast(ch)); } } } @@ -86,21 +94,6 @@ std::string_view layout_kind_name(facts::LayoutKind kind) { return "unknown"; } -std::string_view severity_name(facts::Severity severity) { - switch (severity) { - case facts::Severity::kInfo: { - return "info"; - } - case facts::Severity::kWarning: { - return "warning"; - } - case facts::Severity::kError: { - return "error"; - } - } - return "error"; -} - void write_location(std::ostream& out, const facts::EntrypointFacts& facts, facts::SourceLocation location) { out << "{" << "\"file\": \"" << json_escape(facts.string(facts.source_file(location.file).path)) << "\", " @@ -203,7 +196,7 @@ void write_functions(std::ostream& out, const facts::EntrypointFacts& facts) { out << " ]"; } -void write_diagnostic_function(std::ostream& out, const facts::EntrypointFacts& facts, const facts::Diagnostic& diagnostic) { +void write_diagnostic_function(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::Diagnostic& diagnostic) { if (diagnostic.function.has_value()) { out << "\"" << json_escape(facts.string(facts.function(*diagnostic.function).name)) << "\""; return; @@ -211,25 +204,29 @@ void write_diagnostic_function(std::ostream& out, const facts::EntrypointFacts& out << "null"; } -void write_diagnostic(std::ostream& out, const facts::EntrypointFacts& facts, const facts::Diagnostic& diagnostic) { - const std::string_view message = diagnostic_message(facts, diagnostic); +void write_diagnostic(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::Diagnostic& diagnostic) { + const std::string_view message = diagnostics::diagnostic_message(diagnostic); out << " {" - << "\"code\": \"" << json_escape(diagnostic_code_name(diagnostic.code)) << "\", " + << "\"code\": \"" << json_escape(diagnostics::diagnostic_code_name(diagnostic.code)) << "\", " << "\"message\": \"" << json_escape(message) << "\", " - << "\"severity\": \"" << severity_name(diagnostic.severity) << "\", " + << "\"severity\": \"" << diagnostics::severity_name(diagnostic.severity) << "\", " << "\"function\": "; write_diagnostic_function(out, facts, diagnostic); out << ", \"location\": "; - write_location(out, facts, diagnostic.location); + if (diagnostic.location.has_value()) { + write_location(out, facts, *diagnostic.location); + } else { + out << "null"; + } out << "}"; } -void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts) { +void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { out << " \"diagnostics\": [\n"; - const auto diagnostics = facts.diagnostics(); - for (size_t i = 0; i < diagnostics.size(); ++i) { - write_diagnostic(out, facts, diagnostics[i]); - if (i + 1 != diagnostics.size()) { + const auto diagnostic_values = diagnostic_set.diagnostics(); + for (size_t i = 0; i < diagnostic_values.size(); ++i) { + write_diagnostic(out, facts, diagnostic_values[i]); + if (i + 1 != diagnostic_values.size()) { out << ","; } out << "\n"; @@ -239,7 +236,7 @@ void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts) { } // namespace -void write_json(std::ostream& out, const facts::EntrypointFacts& facts) { +void write_json(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { if (!out) { throw std::runtime_error("output stream is not writable"); } @@ -249,7 +246,7 @@ void write_json(std::ostream& out, const facts::EntrypointFacts& facts) { out << ",\n"; write_functions(out, facts); out << ",\n"; - write_diagnostics(out, facts); + write_diagnostics(out, facts, diagnostic_set); out << "\n"; out << "}\n"; } diff --git a/pp/tools/entrypoint_codegen/emit/json.h b/pp/tools/entrypoint_codegen/emit/json.h index 3ad4877e4d..e173caf1c2 100644 --- a/pp/tools/entrypoint_codegen/emit/json.h +++ b/pp/tools/entrypoint_codegen/emit/json.h @@ -1,11 +1,12 @@ #pragma once +#include "diagnostics/diagnostics.h" #include "facts/entrypoint_facts.h" #include namespace entrypoint_codegen::emit { -void write_json(std::ostream& out, const facts::EntrypointFacts& facts); +void write_json(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set); } // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/json_tests.cpp b/pp/tools/entrypoint_codegen/emit/json_tests.cpp index 1a5d5432e9..b99124249e 100644 --- a/pp/tools/entrypoint_codegen/emit/json_tests.cpp +++ b/pp/tools/entrypoint_codegen/emit/json_tests.cpp @@ -2,42 +2,173 @@ #include +#include "diagnostics/diagnostics.h" + #include #include #include +#include namespace { -using entrypoint_codegen::facts::Diagnostic; -using entrypoint_codegen::facts::DiagnosticCode; +using entrypoint_codegen::diagnostics::Diagnostic; +using entrypoint_codegen::diagnostics::DiagnosticCode; +using entrypoint_codegen::diagnostics::DiagnosticSet; +using entrypoint_codegen::diagnostics::Severity; +using entrypoint_codegen::facts::BridgeKind; using entrypoint_codegen::facts::EntrypointFacts; -using entrypoint_codegen::facts::Severity; +using entrypoint_codegen::facts::FunctionDecl; using entrypoint_codegen::facts::SourceLocation; -TEST(EmitJsonTest, EscapesStringFieldsInOutput) { +bool has_unescaped_control_character_in_string(std::string_view json) { + bool in_string = false; + bool escaped = false; + for (const unsigned char ch : json) { + if (!in_string) { + if (ch == '"') { + in_string = true; + } + continue; + } + + if (escaped) { + escaped = false; + continue; + } + if (ch == '\\') { + escaped = true; + continue; + } + if (ch == '"') { + in_string = false; + continue; + } + if (ch < 0x20) { + return true; + } + } + return false; +} + +class EmitJsonTest : public testing::Test { + protected: + EntrypointFacts facts_; + DiagnosticSet diagnostics_; + std::ostringstream output_; +}; + +TEST_F(EmitJsonTest, WritesFunctionFacts) { // Arrange - EntrypointFacts facts; - const SourceLocation location{ - .file = facts.add_source_file("entry\"point.cpp"), - .line = 2, - .column = 4, - }; - facts.add_diagnostic(Diagnostic{ + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 2, .column = 4}; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_store"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::emit::write_json(output_, facts_, diagnostics_); + const std::string json = output_.str(); + const bool has_function_name = json.find("\"name\": \"prompp_store\"") != std::string::npos; + const bool has_bridge_kind = json.find("\"bridge_kind\": \"cgo\"") != std::string::npos; + const bool has_c_linkage = json.find("\"has_c_linkage\": true") != std::string::npos; + + // Assert + EXPECT_TRUE(has_function_name); + EXPECT_TRUE(has_bridge_kind); + EXPECT_TRUE(has_c_linkage); +} + +TEST_F(EmitJsonTest, EscapesStringFieldsInOutput) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entry\"point.cpp"), .line = 2, .column = 4}; + diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kClangDiagnostic, - .message = facts.add_string("line\nmessage"), + .message = "line\nmessage", .severity = Severity::kError, .function = std::nullopt, .location = location, }); - std::ostringstream output; // Act - entrypoint_codegen::emit::write_json(output, facts); + entrypoint_codegen::emit::write_json(output_, facts_, diagnostics_); + const std::string json = output_.str(); + const bool has_escaped_path = json.find("\"path\": \"entry\\\"point.cpp\"") != std::string::npos; + const bool has_escaped_message = json.find("\"message\": \"line\\nmessage\"") != std::string::npos; + + // Assert + EXPECT_TRUE(has_escaped_path); + EXPECT_TRUE(has_escaped_message); +} + +TEST_F(EmitJsonTest, EscapesNonWhitespaceControlCharactersInStringFields) { + // Arrange + std::string message = "before"; + message.push_back('\x01'); + message.push_back('\b'); + message.push_back('\f'); + message += "after"; + diagnostics_.add(Diagnostic{ + .code = DiagnosticCode::kClangDiagnostic, + .message = message, + .severity = Severity::kError, + .function = std::nullopt, + .location = std::nullopt, + }); + + // Act + entrypoint_codegen::emit::write_json(output_, facts_, diagnostics_); + const std::string json = output_.str(); + + // Assert + EXPECT_FALSE(has_unescaped_control_character_in_string(json)); + EXPECT_NE(json.find("\\u0001"), std::string::npos); + EXPECT_NE(json.find("\\u0008"), std::string::npos); + EXPECT_NE(json.find("\\u000c"), std::string::npos); +} + +TEST_F(EmitJsonTest, WritesDiagnosticLocationObjectWhenPresent) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 9}; + diagnostics_.add(Diagnostic{ + .code = DiagnosticCode::kMissingNamePrefix, + .message = std::nullopt, + .severity = Severity::kError, + .function = std::nullopt, + .location = location, + }); + + // Act + entrypoint_codegen::emit::write_json(output_, facts_, diagnostics_); + const std::string json = output_.str(); + const bool has_location = json.find("\"location\": {\"file\": \"entrypoint.cpp\", \"line\": 7, \"column\": 9}") != std::string::npos; + + // Assert + EXPECT_TRUE(has_location); +} + +TEST_F(EmitJsonTest, WritesNullDiagnosticLocationWhenAbsent) { + // Arrange + diagnostics_.add(Diagnostic{ + .code = DiagnosticCode::kRuntimeMemoryUsage, + .message = std::nullopt, + .severity = Severity::kInfo, + .function = std::nullopt, + .location = std::nullopt, + }); + + // Act + entrypoint_codegen::emit::write_json(output_, facts_, diagnostics_); + const std::string json = output_.str(); + const bool has_null_location = json.find("\"location\": null") != std::string::npos; // Assert - const std::string json = output.str(); - EXPECT_NE(json.find("\"path\": \"entry\\\"point.cpp\""), std::string::npos); - EXPECT_NE(json.find("\"message\": \"line\\nmessage\""), std::string::npos); + EXPECT_TRUE(has_null_location); } } // namespace diff --git a/pp/tools/entrypoint_codegen/facts/entrypoint_facts.cpp b/pp/tools/entrypoint_codegen/facts/entrypoint_facts.cpp index 0de55f79ac..8d0b15ce39 100644 --- a/pp/tools/entrypoint_codegen/facts/entrypoint_facts.cpp +++ b/pp/tools/entrypoint_codegen/facts/entrypoint_facts.cpp @@ -29,8 +29,7 @@ class EntrypointFacts::Impl { functions_(memory_resource), params_(memory_resource), layouts_(memory_resource), - fields_(memory_resource), - diagnostics_(memory_resource) {} + fields_(memory_resource) {} StringId add_string(std::string_view value) { return strings_.add(value); } @@ -75,8 +74,6 @@ class EntrypointFacts::Impl { return id; } - void add_diagnostic(Diagnostic diagnostic) { diagnostics_.push_back(diagnostic); } - [[nodiscard]] std::string_view string(StringId id) const { return strings_.get(id); } [[nodiscard]] const SourceFileDecl& source_file(SourceFileId id) const { @@ -108,10 +105,6 @@ class EntrypointFacts::Impl { [[nodiscard]] std::span fields(FieldRange range) const { return range_span(fields_, range.begin.get(), range.count); } - [[nodiscard]] std::span diagnostics() const { return diagnostics_; } - - [[nodiscard]] uint32_t diagnostic_count() const noexcept { return static_cast(diagnostics_.size()); } - private: StringTable strings_; @@ -120,13 +113,18 @@ class EntrypointFacts::Impl { std::pmr::vector params_; std::pmr::vector layouts_; std::pmr::vector fields_; - std::pmr::vector diagnostics_; }; EntrypointFacts::EntrypointFacts(std::pmr::memory_resource* memory_resource) : memory_resource_(memory_resource) { std::pmr::polymorphic_allocator allocator(memory_resource_); impl_ = allocator.allocate(1); - allocator.construct(impl_, memory_resource_); + try { + allocator.construct(impl_, memory_resource_); + } catch (...) { + allocator.deallocate(impl_, 1); + impl_ = nullptr; + throw; + } } EntrypointFacts::~EntrypointFacts() { @@ -181,10 +179,6 @@ FunctionId EntrypointFacts::add_function(FunctionDecl function) { return impl_->add_function(function); } -void EntrypointFacts::add_diagnostic(Diagnostic diagnostic) { - impl_->add_diagnostic(diagnostic); -} - std::string_view EntrypointFacts::string(StringId id) const { return impl_->string(id); } @@ -229,12 +223,4 @@ std::span EntrypointFacts::fields(FieldRange range) const { return impl_->fields(range); } -std::span EntrypointFacts::diagnostics() const { - return impl_->diagnostics(); -} - -uint32_t EntrypointFacts::diagnostic_count() const noexcept { - return impl_->diagnostic_count(); -} - } // namespace entrypoint_codegen::facts diff --git a/pp/tools/entrypoint_codegen/facts/entrypoint_facts.h b/pp/tools/entrypoint_codegen/facts/entrypoint_facts.h index d5bed8e819..c6da2b38d7 100644 --- a/pp/tools/entrypoint_codegen/facts/entrypoint_facts.h +++ b/pp/tools/entrypoint_codegen/facts/entrypoint_facts.h @@ -26,7 +26,6 @@ class EntrypointFacts { FieldRange add_fields(std::span fields); LayoutRange add_layouts(std::span layouts); FunctionId add_function(FunctionDecl function); - void add_diagnostic(Diagnostic diagnostic); [[nodiscard]] std::string_view string(StringId id) const; @@ -45,9 +44,6 @@ class EntrypointFacts { [[nodiscard]] std::span fields(LayoutId id) const; [[nodiscard]] std::span fields(FieldRange range) const; - [[nodiscard]] std::span diagnostics() const; - [[nodiscard]] uint32_t diagnostic_count() const noexcept; - private: class Impl; Impl* impl_ = nullptr; diff --git a/pp/tools/entrypoint_codegen/facts/entrypoint_facts_tests.cpp b/pp/tools/entrypoint_codegen/facts/entrypoint_facts_tests.cpp index cf14db3754..9cfe17d686 100644 --- a/pp/tools/entrypoint_codegen/facts/entrypoint_facts_tests.cpp +++ b/pp/tools/entrypoint_codegen/facts/entrypoint_facts_tests.cpp @@ -2,64 +2,108 @@ #include +#include #include namespace { using entrypoint_codegen::facts::BridgeKind; using entrypoint_codegen::facts::EntrypointFacts; +using entrypoint_codegen::facts::FieldDecl; using entrypoint_codegen::facts::FunctionDecl; -using entrypoint_codegen::facts::LayoutKind; using entrypoint_codegen::facts::LayoutDecl; +using entrypoint_codegen::facts::LayoutKind; using entrypoint_codegen::facts::ParamDecl; using entrypoint_codegen::facts::ParamRole; using entrypoint_codegen::facts::SourceLocation; -TEST(EntrypointFactsTest, ResolvesFunctionRangesToStoredRecords) { +class EntrypointFactsTest : public testing::Test { + protected: + EntrypointFacts facts_; +}; + +TEST_F(EntrypointFactsTest, StoresStringsAndSourceFiles) { + // Act + const auto string_id = facts_.add_string("prompp_fn"); + const auto source_file_id = facts_.add_source_file("entrypoint.cpp"); + const auto source_files = facts_.source_files(); + + // Assert + EXPECT_EQ(facts_.string(string_id), "prompp_fn"); + ASSERT_EQ(source_files.size(), 1); + EXPECT_EQ(facts_.string(facts_.source_file(source_file_id).path), "entrypoint.cpp"); +} + +TEST_F(EntrypointFactsTest, ResolvesContiguousRangesToStoredRecords) { // Arrange - EntrypointFacts facts; - const SourceLocation location{ - .file = facts.add_source_file("entrypoint.cpp"), - .line = 3, - .column = 5, + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 3, .column = 5}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, }; + const std::vector fields{ + FieldDecl{.name = facts_.add_string("series"), .type_spelling = facts_.add_string("int"), .location = location}, + }; + + // Act + const auto param_range = facts_.add_params(params); + const auto field_range = facts_.add_fields(fields); + const auto stored_params = facts_.params(param_range); + const auto stored_fields = facts_.fields(field_range); + + // Assert + ASSERT_EQ(stored_params.size(), 2); + EXPECT_EQ(facts_.string(stored_params[0].name), "args"); + EXPECT_EQ(stored_params[1].role, ParamRole::kRes); + ASSERT_EQ(stored_fields.size(), 1); + EXPECT_EQ(facts_.string(stored_fields[0].name), "series"); +} + +TEST_F(EntrypointFactsTest, ResolvesFunctionOwnedRanges) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 3, .column = 5}; const std::vector params{ - ParamDecl{ - .name = facts.add_string("args"), - .type_spelling = facts.add_string("void*"), - .role = ParamRole::kArgs, - .location = location, - }, + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, }; const std::vector layouts{ - LayoutDecl{ - .kind = LayoutKind::kArguments, - .fields = facts.add_fields({}), - .location = location, - }, + LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, }; - const auto param_range = facts.add_params(params); - const auto layout_range = facts.add_layouts(layouts); - const auto function_id = facts.add_function(FunctionDecl{ - .name = facts.add_string("prompp_fn"), - .return_type_spelling = facts.add_string("void"), - .documentation = facts.add_string(""), + const auto param_range = facts_.add_params(params); + const auto layout_range = facts_.add_layouts(layouts); + + // Act + const auto function_id = facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), .bridge_kind = BridgeKind::kFastCGo, .params = param_range, .layouts = layout_range, .location = location, .has_c_linkage = true, }); - - // Act - const auto stored_params = facts.params(function_id); - const auto stored_layouts = facts.layouts(function_id); + const auto functions = facts_.functions(); + const auto stored_params = facts_.params(function_id); + const auto stored_layouts = facts_.layouts(function_id); // Assert + ASSERT_EQ(functions.size(), 1); + EXPECT_EQ(facts_.string(facts_.function(function_id).name), "prompp_fn"); ASSERT_EQ(stored_params.size(), 1); - EXPECT_EQ(facts.string(stored_params[0].name), "args"); + EXPECT_EQ(facts_.string(stored_params[0].name), "args"); ASSERT_EQ(stored_layouts.size(), 1); EXPECT_EQ(stored_layouts[0].kind, LayoutKind::kArguments); } +TEST_F(EntrypointFactsTest, MoveTransfersStoredFacts) { + // Arrange + const auto string_id = facts_.add_string("prompp_fn"); + + // Act + EntrypointFacts moved = std::move(facts_); + + // Assert + EXPECT_EQ(moved.string(string_id), "prompp_fn"); +} + } // namespace diff --git a/pp/tools/entrypoint_codegen/facts/facts.h b/pp/tools/entrypoint_codegen/facts/facts.h index a34aae8a58..7265fb0c6f 100644 --- a/pp/tools/entrypoint_codegen/facts/facts.h +++ b/pp/tools/entrypoint_codegen/facts/facts.h @@ -3,7 +3,6 @@ #include "tagged_index.h" #include -#include namespace entrypoint_codegen::facts { @@ -46,30 +45,6 @@ enum class LayoutKind : uint8_t { kResult, }; -enum class Severity : uint8_t { - kInfo, - kWarning, - kError, -}; - -enum class DiagnosticCode : uint8_t { - kClangDiagnostic, - kUnsupportedReturnType, - kUnsupportedParamCount, - kUnsupportedParamType, - kUnknownParamRole, - kInvalidTwoParamOrder, - kInvalidSecondParamRole, - kMissingArgumentsLayout, - kMissingResultLayout, - kUnexpectedArgumentsLayout, - kUnexpectedResultLayout, - kMissingNamePrefix, - kMissingCLinkage, - kMissingEntrypointAttribute, - kRuntimeMemoryUsage, -}; - struct SourceLocation { SourceFileId file; uint32_t line; @@ -110,12 +85,4 @@ struct FunctionDecl { bool has_c_linkage; }; -struct Diagnostic { - DiagnosticCode code; - std::optional message; - Severity severity; - std::optional function; - SourceLocation location; -}; - -} // namespace entrypoint_codegen::facts \ No newline at end of file +} // namespace entrypoint_codegen::facts diff --git a/pp/tools/entrypoint_codegen/facts/string_table.cpp b/pp/tools/entrypoint_codegen/facts/string_table.cpp index 9ae0315393..2f27bce8f5 100644 --- a/pp/tools/entrypoint_codegen/facts/string_table.cpp +++ b/pp/tools/entrypoint_codegen/facts/string_table.cpp @@ -61,7 +61,13 @@ class StringTable::Impl { StringTable::StringTable(std::pmr::memory_resource* memory_resource) : memory_resource_(memory_resource) { std::pmr::polymorphic_allocator allocator(memory_resource_); impl_ = allocator.allocate(1); - allocator.construct(impl_, memory_resource_); + try { + allocator.construct(impl_, memory_resource_); + } catch (...) { + allocator.deallocate(impl_, 1); + impl_ = nullptr; + throw; + } } StringTable::~StringTable() { diff --git a/pp/tools/entrypoint_codegen/validate/validate.cpp b/pp/tools/entrypoint_codegen/validate/validate.cpp index 992c1d99ea..7bb38daa69 100644 --- a/pp/tools/entrypoint_codegen/validate/validate.cpp +++ b/pp/tools/entrypoint_codegen/validate/validate.cpp @@ -1,5 +1,7 @@ #include "validate/validate.h" +#include "contract/entrypoint_contract.h" + #include #include @@ -7,19 +9,14 @@ namespace entrypoint_codegen::validate { namespace { -bool is_void_pointer_type(std::string_view type) { - return type == "void *" || type == "void*"; -} - -bool starts_with(std::string_view value, std::string_view prefix) { - return value.substr(0, prefix.size()) == prefix; -} - -void add_diagnostic(facts::EntrypointFacts& facts, facts::DiagnosticCode code, facts::FunctionId function_id, facts::SourceLocation location) { - facts.add_diagnostic(facts::Diagnostic{ +void add_diagnostic(diagnostics::DiagnosticSet& diagnostic_set, + diagnostics::DiagnosticCode code, + facts::FunctionId function_id, + facts::SourceLocation location) { + diagnostic_set.add(diagnostics::Diagnostic{ .code = code, .message = std::nullopt, - .severity = facts::Severity::kError, + .severity = diagnostics::Severity::kError, .function = function_id, .location = location, }); @@ -34,32 +31,35 @@ bool has_layout(const facts::EntrypointFacts& facts, const facts::FunctionDecl& return false; } -void validate_fastcgo_function(facts::EntrypointFacts& facts, facts::FunctionId function_id, const facts::FunctionDecl& function) { +void validate_fastcgo_function(const facts::EntrypointFacts& facts, + diagnostics::DiagnosticSet& diagnostic_set, + facts::FunctionId function_id, + const facts::FunctionDecl& function) { if (facts.string(function.return_type_spelling) != "void") { - add_diagnostic(facts, facts::DiagnosticCode::kUnsupportedReturnType, function_id, function.location); + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedReturnType, function_id, function.location); } const auto params = facts.params(function.params); if (params.size() > 2) { - add_diagnostic(facts, facts::DiagnosticCode::kUnsupportedParamCount, function_id, function.location); + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedParamCount, function_id, function.location); } bool uses_args = false; bool uses_res = false; for (size_t i = 0; i < params.size(); ++i) { const facts::ParamDecl& param = params[i]; - if (!is_void_pointer_type(facts.string(param.type_spelling))) { - add_diagnostic(facts, facts::DiagnosticCode::kUnsupportedParamType, function_id, param.location); + if (!contract::is_void_pointer_type(facts.string(param.type_spelling))) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedParamType, function_id, param.location); } if (param.role == facts::ParamRole::kOther) { - add_diagnostic(facts, facts::DiagnosticCode::kUnknownParamRole, function_id, param.location); + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnknownParamRole, function_id, param.location); continue; } if (i == 0 && param.role == facts::ParamRole::kRes && params.size() == 2) { - add_diagnostic(facts, facts::DiagnosticCode::kInvalidTwoParamOrder, function_id, param.location); + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kInvalidTwoParamOrder, function_id, param.location); } if (i == 1 && param.role != facts::ParamRole::kRes) { - add_diagnostic(facts, facts::DiagnosticCode::kInvalidSecondParamRole, function_id, param.location); + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kInvalidSecondParamRole, function_id, param.location); } uses_args |= param.role == facts::ParamRole::kArgs; uses_res |= param.role == facts::ParamRole::kRes; @@ -68,39 +68,39 @@ void validate_fastcgo_function(facts::EntrypointFacts& facts, facts::FunctionId const bool has_arguments = has_layout(facts, function, facts::LayoutKind::kArguments); const bool has_result = has_layout(facts, function, facts::LayoutKind::kResult); if (uses_args && !has_arguments) { - add_diagnostic(facts, facts::DiagnosticCode::kMissingArgumentsLayout, function_id, function.location); + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingArgumentsLayout, function_id, function.location); } if (uses_res && !has_result) { - add_diagnostic(facts, facts::DiagnosticCode::kMissingResultLayout, function_id, function.location); + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingResultLayout, function_id, function.location); } if (!uses_args && has_arguments) { - add_diagnostic(facts, facts::DiagnosticCode::kUnexpectedArgumentsLayout, function_id, function.location); + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnexpectedArgumentsLayout, function_id, function.location); } if (!uses_res && has_result) { - add_diagnostic(facts, facts::DiagnosticCode::kUnexpectedResultLayout, function_id, function.location); + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnexpectedResultLayout, function_id, function.location); } } } // namespace -void validate_entrypoints(facts::EntrypointFacts& facts) { +void validate_entrypoints(const facts::EntrypointFacts& facts, diagnostics::DiagnosticSet& diagnostic_set) { const auto functions = facts.functions(); for (size_t i = 0; i < functions.size(); ++i) { const auto function_id = facts::FunctionId(static_cast(i)); const facts::FunctionDecl& function = functions[i]; const std::string_view name = facts.string(function.name); - if (!starts_with(name, "prompp_")) { - add_diagnostic(facts, facts::DiagnosticCode::kMissingNamePrefix, function_id, function.location); + if (!contract::is_entrypoint_function_name(name)) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingNamePrefix, function_id, function.location); } if (!function.has_c_linkage) { - add_diagnostic(facts, facts::DiagnosticCode::kMissingCLinkage, function_id, function.location); + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingCLinkage, function_id, function.location); } if (function.bridge_kind == facts::BridgeKind::kUnknown) { - add_diagnostic(facts, facts::DiagnosticCode::kMissingEntrypointAttribute, function_id, function.location); + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingEntrypointAttribute, function_id, function.location); continue; } if (function.bridge_kind == facts::BridgeKind::kFastCGo) { - validate_fastcgo_function(facts, function_id, function); + validate_fastcgo_function(facts, diagnostic_set, function_id, function); } } } diff --git a/pp/tools/entrypoint_codegen/validate/validate.h b/pp/tools/entrypoint_codegen/validate/validate.h index af8a04b2ef..115793e5e7 100644 --- a/pp/tools/entrypoint_codegen/validate/validate.h +++ b/pp/tools/entrypoint_codegen/validate/validate.h @@ -1,9 +1,10 @@ #pragma once +#include "diagnostics/diagnostics.h" #include "facts/entrypoint_facts.h" namespace entrypoint_codegen::validate { -void validate_entrypoints(facts::EntrypointFacts& facts); +void validate_entrypoints(const facts::EntrypointFacts& facts, diagnostics::DiagnosticSet& diagnostic_set); } // namespace entrypoint_codegen::validate diff --git a/pp/tools/entrypoint_codegen/validate/validate_tests.cpp b/pp/tools/entrypoint_codegen/validate/validate_tests.cpp index d1f895d910..b65c33ec4f 100644 --- a/pp/tools/entrypoint_codegen/validate/validate_tests.cpp +++ b/pp/tools/entrypoint_codegen/validate/validate_tests.cpp @@ -2,93 +2,428 @@ #include -#include +#include "diagnostics/diagnostics.h" + +#include #include namespace { +using entrypoint_codegen::diagnostics::DiagnosticCode; +using entrypoint_codegen::diagnostics::DiagnosticSet; +using entrypoint_codegen::diagnostics::Severity; using entrypoint_codegen::facts::BridgeKind; -using entrypoint_codegen::facts::DiagnosticCode; using entrypoint_codegen::facts::EntrypointFacts; using entrypoint_codegen::facts::FunctionDecl; using entrypoint_codegen::facts::LayoutDecl; +using entrypoint_codegen::facts::LayoutKind; using entrypoint_codegen::facts::ParamDecl; using entrypoint_codegen::facts::ParamRole; -using entrypoint_codegen::facts::Severity; using entrypoint_codegen::facts::SourceLocation; -SourceLocation add_source_file(EntrypointFacts& facts) { - return SourceLocation{ - .file = facts.add_source_file("entrypoint.cpp"), - .line = 7, - .column = 3, +class ValidateEntrypointsTest : public testing::Test { + protected: + EntrypointFacts facts_; + DiagnosticSet diagnostics_; +}; + +TEST_F(ValidateEntrypointsTest, AcceptsValidCgoFunction) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + EXPECT_TRUE(diagnostics.empty()); +} + +TEST_F(ValidateEntrypointsTest, AcceptsValidFastCgoFunction) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, }; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, + LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + EXPECT_TRUE(diagnostics.empty()); } -entrypoint_codegen::facts::ParamRange add_params(EntrypointFacts& facts, std::span params) { - return facts.add_params(params); +TEST_F(ValidateEntrypointsTest, ReportsMissingEntrypointPrefix) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const auto function_id = facts_.add_function(FunctionDecl{ + .name = facts_.add_string("other_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingNamePrefix); + EXPECT_EQ(diagnostics[0].severity, Severity::kError); + EXPECT_EQ(diagnostics[0].function, function_id); + ASSERT_TRUE(diagnostics[0].location.has_value()); + EXPECT_EQ(diagnostics[0].location->line, location.line); } -entrypoint_codegen::facts::LayoutRange add_layouts(EntrypointFacts& facts, std::span layouts) { - return facts.add_layouts(layouts); +TEST_F(ValidateEntrypointsTest, ReportsMissingCLinkage) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = false, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingCLinkage); } -FunctionDecl make_function(EntrypointFacts& facts, SourceLocation location, BridgeKind bridge_kind) { - return FunctionDecl{ - .name = facts.add_string("prompp_fn"), - .return_type_spelling = facts.add_string("void"), - .documentation = facts.add_string(""), - .bridge_kind = bridge_kind, - .params = add_params(facts, {}), - .layouts = add_layouts(facts, {}), +TEST_F(ValidateEntrypointsTest, ReportsMissingEntrypointAttribute) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kUnknown, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), .location = location, .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingEntrypointAttribute); +} + +TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoReturnType) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("int"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnsupportedReturnType); +} + +TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoParamCount) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, + }; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, + LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnsupportedParamCount); } -TEST(ValidateEntrypointsTest, AddsMissingEntrypointAttributeForUnannotatedFunction) { +TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoParamType) { // Arrange - EntrypointFacts facts; - const SourceLocation location = add_source_file(facts); - const auto function_id = facts.add_function(make_function(facts, location, BridgeKind::kUnknown)); + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("int*"), .role = ParamRole::kArgs, .location = location}, + }; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts); + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); // Assert - const auto diagnostics = facts.diagnostics(); ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingEntrypointAttribute); - EXPECT_EQ(diagnostics[0].severity, Severity::kError); - EXPECT_EQ(diagnostics[0].function, function_id); - EXPECT_EQ(diagnostics[0].location.line, location.line); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnsupportedParamType); } -TEST(ValidateEntrypointsTest, AddsMissingArgumentsLayoutWhenArgsParameterHasNoLayout) { +TEST_F(ValidateEntrypointsTest, ReportsUnknownFastCgoParamRole) { // Arrange - EntrypointFacts facts; - const SourceLocation location = add_source_file(facts); + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; const std::vector params{ - ParamDecl{ - .name = facts.add_string("args"), - .type_spelling = facts.add_string("void*"), - .role = ParamRole::kArgs, - .location = location, - }, + ParamDecl{.name = facts_.add_string("other"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kOther, .location = location}, }; - FunctionDecl function = make_function(facts, location, BridgeKind::kFastCGo); - function.params = add_params(facts, params); - const auto function_id = facts.add_function(function); + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnknownParamRole); +} + +TEST_F(ValidateEntrypointsTest, ReportsInvalidTwoParamOrder) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, + }; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kInvalidTwoParamOrder); +} + +TEST_F(ValidateEntrypointsTest, ReportsInvalidSecondParamRole) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, + }; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kInvalidSecondParamRole); +} + +TEST_F(ValidateEntrypointsTest, ReportsMissingArgumentsLayout) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts); + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); // Assert - const auto diagnostics = facts.diagnostics(); ASSERT_EQ(diagnostics.size(), 1); EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingArgumentsLayout); - EXPECT_EQ(diagnostics[0].function, function_id); - EXPECT_EQ(diagnostics[0].location.line, location.line); +} + +TEST_F(ValidateEntrypointsTest, ReportsMissingResultLayout) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingResultLayout); +} + +TEST_F(ValidateEntrypointsTest, ReportsUnexpectedArgumentsLayout) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnexpectedArgumentsLayout); +} + +TEST_F(ValidateEntrypointsTest, ReportsUnexpectedResultLayout) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnexpectedResultLayout); } } // namespace From 5217d79b23b46bd42b411eb06900a8e629ba820c Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Mon, 6 Jul 2026 09:51:23 +0000 Subject: [PATCH 13/31] more ref --- pp/tools/entrypoint_codegen/BUILD | 28 +++++ pp/tools/entrypoint_codegen/MODULE.bazel | 1 + pp/tools/entrypoint_codegen/MODULE.bazel.lock | 38 +++++-- pp/tools/entrypoint_codegen/app/argparse.cpp | 24 ++-- .../entrypoint_codegen/app/argparse_tests.cpp | 20 +++- pp/tools/entrypoint_codegen/app/main.cpp | 16 +-- .../app/memory_tracking.cpp | 35 ++++++ .../entrypoint_codegen/app/memory_tracking.h | 28 +++++ .../app/memory_tracking_tests.cpp | 34 ++++++ .../entrypoint_codegen/app/memory_usage.h | 13 +++ pp/tools/entrypoint_codegen/app/options.h | 28 ++--- pp/tools/entrypoint_codegen/app/run.cpp | 103 +++--------------- pp/tools/entrypoint_codegen/app/run.h | 8 ++ pp/tools/entrypoint_codegen/app/run_tests.cpp | 23 ++-- .../entrypoint_codegen/app/runtime_debug.cpp | 2 +- .../entrypoint_codegen/app/runtime_debug.h | 11 +- .../app/runtime_debug_tests.cpp | 13 +-- .../diagnostics/diagnostics.cpp | 26 +++++ .../diagnostics/diagnostics.h | 11 ++ .../diagnostics/diagnostics_tests.cpp | 35 ++++++ 20 files changed, 334 insertions(+), 163 deletions(-) create mode 100644 pp/tools/entrypoint_codegen/app/memory_tracking.cpp create mode 100644 pp/tools/entrypoint_codegen/app/memory_tracking.h create mode 100644 pp/tools/entrypoint_codegen/app/memory_tracking_tests.cpp create mode 100644 pp/tools/entrypoint_codegen/app/memory_usage.h diff --git a/pp/tools/entrypoint_codegen/BUILD b/pp/tools/entrypoint_codegen/BUILD index 626d429ff8..e586110b38 100644 --- a/pp/tools/entrypoint_codegen/BUILD +++ b/pp/tools/entrypoint_codegen/BUILD @@ -192,6 +192,12 @@ cc_test( ], ) +cc_library( + name = "app_memory_usage", + hdrs = ["app/memory_usage.h"], + copts = ENTRYPOINT_CODEGEN_COPTS, +) + cc_library( name = "app_options", hdrs = ["app/options.h"], @@ -212,12 +218,31 @@ cc_library( ], ) +cc_library( + name = "app_memory_tracking", + srcs = ["app/memory_tracking.cpp"], + hdrs = ["app/memory_tracking.h"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [":app_memory_usage"], +) + +cc_test( + name = "memory_tracking_test", + srcs = ["app/memory_tracking_tests.cpp"], + copts = ENTRYPOINT_CODEGEN_COPTS, + deps = [ + ":app_memory_tracking", + "@gtest//:gtest_main", + ], +) + cc_library( name = "app_runtime_debug", srcs = ["app/runtime_debug.cpp"], hdrs = ["app/runtime_debug.h"], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ + ":app_memory_usage", ":diagnostics", ], ) @@ -230,6 +255,8 @@ cc_library( ], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ + ":app_memory_usage", + ":app_memory_tracking", ":app_options", ":app_runtime_debug", ":clang_adapter", @@ -256,6 +283,7 @@ cc_test( srcs = ["app/runtime_debug_tests.cpp"], copts = ENTRYPOINT_CODEGEN_COPTS, deps = [ + ":app_memory_usage", ":app_runtime_debug", ":diagnostics", "@gtest//:gtest_main", diff --git a/pp/tools/entrypoint_codegen/MODULE.bazel b/pp/tools/entrypoint_codegen/MODULE.bazel index e6dbca3b67..47faafd3bb 100644 --- a/pp/tools/entrypoint_codegen/MODULE.bazel +++ b/pp/tools/entrypoint_codegen/MODULE.bazel @@ -4,6 +4,7 @@ bazel_dep(name = "rules_cc", version = "0.2.16") bazel_dep( name = "googletest", + dev_dependency = True, repo_name = "gtest", version = "1.11.0", ) diff --git a/pp/tools/entrypoint_codegen/MODULE.bazel.lock b/pp/tools/entrypoint_codegen/MODULE.bazel.lock index 8f577d057c..16d03fcf26 100644 --- a/pp/tools/entrypoint_codegen/MODULE.bazel.lock +++ b/pp/tools/entrypoint_codegen/MODULE.bazel.lock @@ -4,40 +4,56 @@ "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", - "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/source.json": "7e3a9adf473e9af076ae485ed649d5641ad50ec5c11718103f34de03170d94ad", + "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/source.json": "14892cc698e02ffedf4967546e6bedb7245015906888d3465fcf27c90a26da10", "https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel": "50341a62efbc483e8a2a6aec30994a58749bd7b885e18dd96aa8c33031e558ef", "https://bcr.bazel.build/modules/apple_support/1.5.0/source.json": "eb98a7627c0bc486b57f598ad8da50f6625d974c8f723e9ea71bd39f709c9862", "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", - "https://bcr.bazel.build/modules/bazel_features/1.11.0/source.json": "c9320aa53cd1c441d24bd6b716da087ad7e4ff0d9742a9884587596edfe53015", + "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d", + "https://bcr.bazel.build/modules/bazel_features/1.28.0/source.json": "16a3fc5b4483cb307643791f5a4b7365fa98d2e70da7c378cdbde55f0c0b32cf", "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", - "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/source.json": "082ed5f9837901fada8c68c2f3ddc958bb22b6d654f71dd73f3df30d45d4b749", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953", "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", - "https://bcr.bazel.build/modules/googletest/1.11.0/source.json": "c73d9ef4268c91bd0c1cd88f1f9dfa08e814b1dbe89b5f594a9f08ba0244d206", + "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", + "https://bcr.bazel.build/modules/googletest/1.14.0/source.json": "2478949479000fdd7de9a3d0107ba2c85bb5f961c3ecb1aa448f52549ce310b5", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", + "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", + "https://bcr.bazel.build/modules/platforms/0.0.10/source.json": "f22828ff4cf021a6b577f1bf6341cb9dcd7965092a439f64fc1bb3b7a5ae4bd5", "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", + "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", - "https://bcr.bazel.build/modules/platforms/0.0.9/source.json": "cd74d854bf16a9e002fb2ca7b1a421f4403cda29f824a765acd3a8c56f8d43e6", "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", - "https://bcr.bazel.build/modules/protobuf/21.7/source.json": "bbe500720421e582ff2d18b0802464205138c06056f443184de39fbb8187b09b", + "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", + "https://bcr.bazel.build/modules/protobuf/27.0/source.json": "1acf3d080c728d42f423fde5422fd0a1a24f44c15908124ce12363a253384193", "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", + "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", - "https://bcr.bazel.build/modules/rules_cc/0.0.9/source.json": "1f1ba6fea244b616de4a554a0f4983c91a9301640c8fe0dd1d410254115c8430", + "https://bcr.bazel.build/modules/rules_cc/0.2.16/MODULE.bazel": "9242fa89f950c6ef7702801ab53922e99c69b02310c39fb6e62b2bd30df2a1d4", + "https://bcr.bazel.build/modules/rules_cc/0.2.16/source.json": "d03d5cde49376d87e14ec14b666c56075e5e3926930327fd5d0484a1ff2ac1cc", "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", + "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", "https://bcr.bazel.build/modules/rules_java/7.6.5/MODULE.bazel": "481164be5e02e4cab6e77a36927683263be56b7e36fef918b458d7a8a1ebadb1", "https://bcr.bazel.build/modules/rules_java/7.6.5/source.json": "a805b889531d1690e3c72a7a7e47a870d00323186a9904b36af83aa3d053ee8d", "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", - "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/source.json": "a075731e1b46bc8425098512d038d416e966ab19684a10a34f4741295642fc35", + "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", + "https://bcr.bazel.build/modules/rules_jvm_external/5.1/source.json": "5abb45cc9beb27b77aec6a65a11855ef2b55d95dfdc358e9f312b78ae0ba32d5", "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", "https://bcr.bazel.build/modules/rules_license/0.0.7/source.json": "355cc5737a0f294e560d52b1b7a6492d4fff2caf0bef1a315df5a298fca2d34a", @@ -51,9 +67,9 @@ "https://bcr.bazel.build/modules/rules_python/0.22.1/source.json": "57226905e783bae7c37c2dd662be078728e48fa28ee4324a7eabcafb5a43d014", "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", - "https://bcr.bazel.build/modules/stardoc/0.5.1/source.json": "a96f95e02123320aa015b956f29c00cb818fa891ef823d55148e1a362caacf29", + "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", + "https://bcr.bazel.build/modules/stardoc/0.5.3/source.json": "cd53fe968dc8cd98197c052db3db6d82562960c87b61e7a90ee96f8e4e0dda97", "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", - "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/source.json": "f1ef7d3f9e0e26d4b23d1c39b5f5de71f584dd7d1b4ef83d9bbba6ec7a6a6459", "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79", @@ -109,7 +125,7 @@ "@@platforms//host:extension.bzl%host_platform": { "general": { "bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=", - "usagesDigest": "pCYpDQmqMbmiiPI1p2Kd3VLm5T48rRAht5WdW0X2GlA=", + "usagesDigest": "hgylFkgWSg0ulUwWZzEM1aIftlUnbmw2ynWLdEfHnZc=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, diff --git a/pp/tools/entrypoint_codegen/app/argparse.cpp b/pp/tools/entrypoint_codegen/app/argparse.cpp index 127ffe2b33..142e4487ff 100644 --- a/pp/tools/entrypoint_codegen/app/argparse.cpp +++ b/pp/tools/entrypoint_codegen/app/argparse.cpp @@ -89,7 +89,7 @@ void write_help(std::ostream& out) { CliOptions parse_arguments(int argc, char** argv) { std::vector inputs; CliOptions options; - options.run_options.output_path = std::filesystem::current_path() / "entrypoint_facts.json"; + options.run_options.output.output_path = std::filesystem::current_path() / "entrypoint_facts.json"; for (int i = 1; i < argc; ++i) { const std::string arg = argv[i]; @@ -98,39 +98,39 @@ CliOptions parse_arguments(int argc, char** argv) { return options; } if (arg.rfind("--output=", 0) == 0) { - options.run_options.output_path = arg.substr(std::string("--output=").size()); + options.run_options.output.output_path = arg.substr(std::string("--output=").size()); continue; } if (arg.rfind("--output-dir=", 0) == 0) { - options.run_options.output_path = std::filesystem::path(arg.substr(std::string("--output-dir=").size())) / "entrypoint_facts.json"; + options.run_options.output.output_path = std::filesystem::path(arg.substr(std::string("--output-dir=").size())) / "entrypoint_facts.json"; continue; } if (arg.rfind("--mode=", 0) == 0) { - options.run_options.output_mode = parse_output_mode(arg.substr(std::string("--mode=").size())); + options.run_options.output.output_mode = parse_output_mode(arg.substr(std::string("--mode=").size())); continue; } if (arg.rfind("--format=", 0) == 0) { - options.run_options.output_mode = parse_output_mode(arg.substr(std::string("--format=").size())); + options.run_options.output.output_mode = parse_output_mode(arg.substr(std::string("--format=").size())); continue; } if (arg == "--no-output") { - options.run_options.output_mode = OutputMode::kCheck; + options.run_options.output.output_mode = OutputMode::kCheck; continue; } if (arg == "--runtime-debug") { - options.run_options.runtime_debug = true; + options.run_options.runtime.debug_diagnostics = true; continue; } if (arg.rfind("--clang-arg=", 0) == 0) { - options.run_options.clang_args.push_back(arg.substr(std::string("--clang-arg=").size())); + options.run_options.analysis.clang_args.push_back(arg.substr(std::string("--clang-arg=").size())); continue; } if (arg.rfind("--extra-arg=", 0) == 0) { - options.run_options.clang_args.push_back(arg.substr(std::string("--extra-arg=").size())); + options.run_options.analysis.clang_args.push_back(arg.substr(std::string("--extra-arg=").size())); continue; } if (arg.rfind("--clang-args-file=", 0) == 0) { - append_clang_args_file(arg.substr(std::string("--clang-args-file=").size()), options.run_options.clang_args); + append_clang_args_file(arg.substr(std::string("--clang-args-file=").size()), options.run_options.analysis.clang_args); continue; } if (arg.rfind("--source=", 0) == 0) { @@ -143,14 +143,14 @@ CliOptions parse_arguments(int argc, char** argv) { } if (arg == "--") { for (++i; i < argc; ++i) { - options.run_options.clang_args.emplace_back(argv[i]); + options.run_options.analysis.clang_args.emplace_back(argv[i]); } break; } inputs.emplace_back(arg); } - options.run_options.source_files = collect_input_files(inputs); + options.run_options.analysis.source_files = collect_input_files(inputs); return options; } diff --git a/pp/tools/entrypoint_codegen/app/argparse_tests.cpp b/pp/tools/entrypoint_codegen/app/argparse_tests.cpp index 550f6b23c3..e5d9272e5a 100644 --- a/pp/tools/entrypoint_codegen/app/argparse_tests.cpp +++ b/pp/tools/entrypoint_codegen/app/argparse_tests.cpp @@ -47,7 +47,7 @@ TEST(ArgparseTest, ReturnsHelpForHelpFlag) { // Assert EXPECT_TRUE(options.help); - EXPECT_TRUE(options.run_options.source_files.empty()); + EXPECT_TRUE(options.run_options.analysis.source_files.empty()); } TEST(ArgparseTest, CollectsExistingCppInputFiles) { @@ -57,7 +57,7 @@ TEST(ArgparseTest, CollectsExistingCppInputFiles) { // Act const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", source_file.string()}); - const auto source_files = options.run_options.source_files; + const auto source_files = options.run_options.analysis.source_files; // Assert ASSERT_EQ(source_files.size(), 1); @@ -74,7 +74,7 @@ TEST(ArgparseTest, CollectsCppInputFilesRecursivelyFromDirectory) { // Act const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", root.string()}); - const auto source_files = options.run_options.source_files; + const auto source_files = options.run_options.analysis.source_files; // Assert ASSERT_EQ(source_files.size(), 1); @@ -98,7 +98,7 @@ TEST(ArgparseTest, ParsesOutputDirectoryAsFactsFilePath) { const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--output-dir=" + output_dir.string()}); // Assert - EXPECT_EQ(options.run_options.output_path, output_dir / "entrypoint_facts.json"); + EXPECT_EQ(options.run_options.output.output_path, output_dir / "entrypoint_facts.json"); } TEST(ArgparseTest, ParsesCheckModeAlias) { @@ -106,13 +106,13 @@ TEST(ArgparseTest, ParsesCheckModeAlias) { const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--no-output"}); // Assert - EXPECT_EQ(options.run_options.output_mode, entrypoint_codegen::app::OutputMode::kCheck); + EXPECT_EQ(options.run_options.output.output_mode, entrypoint_codegen::app::OutputMode::kCheck); } TEST(ArgparseTest, CollectsClangArgsFromFlagAndSeparator) { // Act const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--clang-arg=-I.", "--", "-std=c++2b"}); - const auto clang_args = options.run_options.clang_args; + const auto clang_args = options.run_options.analysis.clang_args; // Assert ASSERT_EQ(clang_args.size(), 2); @@ -124,4 +124,12 @@ TEST(ArgparseTest, RejectsUnknownOutputMode) { EXPECT_THROW(parse_args({"entrypoint_codegen", "--mode=xml"}), std::runtime_error); } +TEST(ArgparseTest, ParsesRuntimeDebugPolicy) { + // Act + const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--runtime-debug"}); + + // Assert + EXPECT_TRUE(options.run_options.runtime.debug_diagnostics); +} + } // namespace diff --git a/pp/tools/entrypoint_codegen/app/main.cpp b/pp/tools/entrypoint_codegen/app/main.cpp index c8248700ec..374672986d 100644 --- a/pp/tools/entrypoint_codegen/app/main.cpp +++ b/pp/tools/entrypoint_codegen/app/main.cpp @@ -20,12 +20,12 @@ int main(int argc, char** argv) { return 0; } - if (cli_options.run_options.source_files.empty()) { + if (cli_options.run_options.analysis.source_files.empty()) { app::write_help(std::cout); return 1; } - cli_options.run_options.diagnostics_output = &std::cout; + cli_options.run_options.output.diagnostics_output = &std::cout; app::RunReport report; try { @@ -35,17 +35,17 @@ int main(int argc, char** argv) { return 1; } - if (cli_options.run_options.output_mode == app::OutputMode::kJson) { - std::cout << "Wrote " << cli_options.run_options.output_path << "\n"; + if (cli_options.run_options.output.output_mode == app::OutputMode::kJson) { + std::cout << "Wrote " << cli_options.run_options.output.output_path << "\n"; } if (report.decision == app::ExitDecision::kAnalysisFailed) { - std::cerr << "entrypoint_codegen found " << report.error_count << " error diagnostics"; - if (report.warning_count != 0 || report.info_count != 0) { - std::cerr << " (" << report.warning_count << " warnings, " << report.info_count << " info)"; + std::cerr << "entrypoint_codegen found " << report.diagnostics.errors << " error diagnostics"; + if (report.diagnostics.warnings != 0 || report.diagnostics.infos != 0) { + std::cerr << " (" << report.diagnostics.warnings << " warnings, " << report.diagnostics.infos << " info)"; } std::cerr << "\n"; return 2; } return 0; -} \ No newline at end of file +} diff --git a/pp/tools/entrypoint_codegen/app/memory_tracking.cpp b/pp/tools/entrypoint_codegen/app/memory_tracking.cpp new file mode 100644 index 0000000000..3c08781b61 --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/memory_tracking.cpp @@ -0,0 +1,35 @@ +#include "app/memory_tracking.h" + +namespace entrypoint_codegen::app { + +TrackingMemoryResource::TrackingMemoryResource(std::pmr::memory_resource* upstream) : upstream_(upstream) {} + +MemoryUsageSnapshot TrackingMemoryResource::snapshot() const noexcept { + return MemoryUsageSnapshot{ + .allocated_bytes = allocated_bytes_, + .deallocated_bytes = deallocated_bytes_, + .peak_live_bytes = peak_live_bytes_, + }; +} + +void* TrackingMemoryResource::do_allocate(size_t bytes, size_t alignment) { + void* result = upstream_->allocate(bytes, alignment); + allocated_bytes_ += bytes; + live_bytes_ += bytes; + if (live_bytes_ > peak_live_bytes_) { + peak_live_bytes_ = live_bytes_; + } + return result; +} + +void TrackingMemoryResource::do_deallocate(void* pointer, size_t bytes, size_t alignment) { + upstream_->deallocate(pointer, bytes, alignment); + deallocated_bytes_ += bytes; + live_bytes_ -= bytes; +} + +bool TrackingMemoryResource::do_is_equal(const std::pmr::memory_resource& other) const noexcept { + return this == &other; +} + +} // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/memory_tracking.h b/pp/tools/entrypoint_codegen/app/memory_tracking.h new file mode 100644 index 0000000000..a4f4783619 --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/memory_tracking.h @@ -0,0 +1,28 @@ +#pragma once + +#include "app/memory_usage.h" + +#include +#include + +namespace entrypoint_codegen::app { + +class TrackingMemoryResource : public std::pmr::memory_resource { + public: + explicit TrackingMemoryResource(std::pmr::memory_resource* upstream = std::pmr::get_default_resource()); + + [[nodiscard]] MemoryUsageSnapshot snapshot() const noexcept; + + private: + void* do_allocate(size_t bytes, size_t alignment) override; + void do_deallocate(void* pointer, size_t bytes, size_t alignment) override; + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override; + + std::pmr::memory_resource* upstream_; + size_t allocated_bytes_ = 0; + size_t deallocated_bytes_ = 0; + size_t live_bytes_ = 0; + size_t peak_live_bytes_ = 0; +}; + +} // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/memory_tracking_tests.cpp b/pp/tools/entrypoint_codegen/app/memory_tracking_tests.cpp new file mode 100644 index 0000000000..9f54717795 --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/memory_tracking_tests.cpp @@ -0,0 +1,34 @@ +#include "app/memory_tracking.h" + +#include + +#include + +namespace { + +TEST(TrackingMemoryResourceTest, SnapshotReportsAllocatedDeallocatedAndPeakLiveBytes) { + // Arrange + entrypoint_codegen::app::TrackingMemoryResource memory_resource; + + // Act + void* first = memory_resource.allocate(8, alignof(std::max_align_t)); + void* second = memory_resource.allocate(4, alignof(std::max_align_t)); + const entrypoint_codegen::app::MemoryUsageSnapshot after_allocations = memory_resource.snapshot(); + memory_resource.deallocate(first, 8, alignof(std::max_align_t)); + const entrypoint_codegen::app::MemoryUsageSnapshot after_first_deallocate = memory_resource.snapshot(); + memory_resource.deallocate(second, 4, alignof(std::max_align_t)); + const entrypoint_codegen::app::MemoryUsageSnapshot after_all_deallocated = memory_resource.snapshot(); + + // Assert + EXPECT_EQ(after_allocations.allocated_bytes, 12); + EXPECT_EQ(after_allocations.deallocated_bytes, 0); + EXPECT_EQ(after_allocations.peak_live_bytes, 12); + EXPECT_EQ(after_first_deallocate.allocated_bytes, 12); + EXPECT_EQ(after_first_deallocate.deallocated_bytes, 8); + EXPECT_EQ(after_first_deallocate.peak_live_bytes, 12); + EXPECT_EQ(after_all_deallocated.allocated_bytes, 12); + EXPECT_EQ(after_all_deallocated.deallocated_bytes, 12); + EXPECT_EQ(after_all_deallocated.peak_live_bytes, 12); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/app/memory_usage.h b/pp/tools/entrypoint_codegen/app/memory_usage.h new file mode 100644 index 0000000000..6950c8a3ef --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/memory_usage.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace entrypoint_codegen::app { + +struct MemoryUsageSnapshot { + size_t allocated_bytes = 0; + size_t deallocated_bytes = 0; + size_t peak_live_bytes = 0; +}; + +} // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/options.h b/pp/tools/entrypoint_codegen/app/options.h index 4caa8b8408..8788f0d5c0 100644 --- a/pp/tools/entrypoint_codegen/app/options.h +++ b/pp/tools/entrypoint_codegen/app/options.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -15,29 +14,30 @@ enum class OutputMode : uint8_t { kCheck, }; -struct RunOptions { +struct AnalysisOptions { std::vector source_files; std::vector clang_args; +}; + +struct OutputOptions { std::filesystem::path output_path; OutputMode output_mode = OutputMode::kJson; - bool runtime_debug = false; std::ostream* diagnostics_output = nullptr; }; +struct RuntimeOptions { + bool debug_diagnostics = false; +}; + +struct RunOptions { + AnalysisOptions analysis; + OutputOptions output; + RuntimeOptions runtime; +}; + enum class ExitDecision : uint8_t { kSuccess, kAnalysisFailed, }; -struct RunReport { - ExitDecision decision; - uint32_t diagnostic_count; - uint32_t error_count; - uint32_t warning_count; - uint32_t info_count; - size_t app_allocated_bytes; - size_t app_deallocated_bytes; - size_t app_peak_live_bytes; -}; - } // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/run.cpp b/pp/tools/entrypoint_codegen/app/run.cpp index 9e7c656c97..67dea4078e 100644 --- a/pp/tools/entrypoint_codegen/app/run.cpp +++ b/pp/tools/entrypoint_codegen/app/run.cpp @@ -1,5 +1,6 @@ #include "app/run.h" +#include "app/memory_tracking.h" #include "app/runtime_debug.h" #include "clang_adapter/parse.h" #include "diagnostics/diagnostics.h" @@ -10,77 +11,13 @@ #include #include #include -#include #include namespace entrypoint_codegen::app { namespace { -class tracking_memory_resource : public std::pmr::memory_resource { - public: - explicit tracking_memory_resource(std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) : upstream_(upstream) {} - - [[nodiscard]] size_t allocated_bytes() const noexcept { return allocated_bytes_; } - [[nodiscard]] size_t deallocated_bytes() const noexcept { return deallocated_bytes_; } - [[nodiscard]] size_t peak_live_bytes() const noexcept { return peak_live_bytes_; } - - private: - void* do_allocate(size_t bytes, size_t alignment) override { - void* result = upstream_->allocate(bytes, alignment); - allocated_bytes_ += bytes; - live_bytes_ += bytes; - if (live_bytes_ > peak_live_bytes_) { - peak_live_bytes_ = live_bytes_; - } - return result; - } - - void do_deallocate(void* pointer, size_t bytes, size_t alignment) override { - upstream_->deallocate(pointer, bytes, alignment); - deallocated_bytes_ += bytes; - live_bytes_ -= bytes; - } - - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { return this == &other; } - - std::pmr::memory_resource* upstream_; - size_t allocated_bytes_ = 0; - size_t deallocated_bytes_ = 0; - size_t live_bytes_ = 0; - size_t peak_live_bytes_ = 0; -}; - -struct DiagnosticCounts { - uint32_t total = 0; - uint32_t errors = 0; - uint32_t warnings = 0; - uint32_t infos = 0; -}; - -DiagnosticCounts count_diagnostics(const diagnostics::DiagnosticSet& diagnostic_set) { - DiagnosticCounts counts; - for (const diagnostics::Diagnostic& diagnostic : diagnostic_set.diagnostics()) { - ++counts.total; - switch (diagnostic.severity) { - case diagnostics::Severity::kInfo: { - ++counts.infos; - break; - } - case diagnostics::Severity::kWarning: { - ++counts.warnings; - break; - } - case diagnostics::Severity::kError: { - ++counts.errors; - break; - } - } - } - return counts; -} - -void write_json_output(const RunOptions& options, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { +void write_json_output(const OutputOptions& options, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { if (options.output_path.has_parent_path()) { std::filesystem::create_directories(options.output_path.parent_path()); } @@ -92,7 +29,7 @@ void write_json_output(const RunOptions& options, const facts::EntrypointFacts& emit::write_json(output, facts, diagnostic_set); } -void write_lint_output(const RunOptions& options, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { +void write_lint_output(const OutputOptions& options, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { std::ostream& output = options.diagnostics_output == nullptr ? std::cout : *options.diagnostics_output; emit::write_diagnostics(output, facts, diagnostic_set); } @@ -100,33 +37,29 @@ void write_lint_output(const RunOptions& options, const facts::EntrypointFacts& } // namespace RunReport run(const RunOptions& options) { - tracking_memory_resource memory_resource; + TrackingMemoryResource memory_resource; diagnostics::DiagnosticSet diagnostic_set(&memory_resource); facts::EntrypointFacts facts = clang_adapter::parse_files( clang_adapter::ParseOptions{ - .source_files = options.source_files, - .clang_args = options.clang_args, + .source_files = options.analysis.source_files, + .clang_args = options.analysis.clang_args, .memory_resource = &memory_resource, }, diagnostic_set); validate::validate_entrypoints(facts, diagnostic_set); - if (options.runtime_debug) { - append_runtime_debug_diagnostics(diagnostic_set, RuntimeDebugSnapshot{ - .allocated_bytes = memory_resource.allocated_bytes(), - .deallocated_bytes = memory_resource.deallocated_bytes(), - .peak_live_bytes = memory_resource.peak_live_bytes(), - }); + if (options.runtime.debug_diagnostics) { + append_runtime_debug_diagnostics(diagnostic_set, memory_resource.snapshot()); } - switch (options.output_mode) { + switch (options.output.output_mode) { case OutputMode::kJson: { - write_json_output(options, facts, diagnostic_set); + write_json_output(options.output, facts, diagnostic_set); break; } case OutputMode::kLint: { - write_lint_output(options, facts, diagnostic_set); + write_lint_output(options.output, facts, diagnostic_set); break; } case OutputMode::kCheck: { @@ -134,16 +67,12 @@ RunReport run(const RunOptions& options) { } } - const DiagnosticCounts diagnostic_counts = count_diagnostics(diagnostic_set); + const diagnostics::SeverityCounts diagnostic_counts = diagnostics::count_by_severity(diagnostic_set); + const MemoryUsageSnapshot memory_usage = memory_resource.snapshot(); return RunReport{ - .decision = diagnostic_counts.errors == 0 ? ExitDecision::kSuccess : ExitDecision::kAnalysisFailed, - .diagnostic_count = diagnostic_counts.total, - .error_count = diagnostic_counts.errors, - .warning_count = diagnostic_counts.warnings, - .info_count = diagnostic_counts.infos, - .app_allocated_bytes = memory_resource.allocated_bytes(), - .app_deallocated_bytes = memory_resource.deallocated_bytes(), - .app_peak_live_bytes = memory_resource.peak_live_bytes(), + .decision = diagnostic_counts.has_errors() ? ExitDecision::kAnalysisFailed : ExitDecision::kSuccess, + .diagnostics = diagnostic_counts, + .memory_usage = memory_usage, }; } diff --git a/pp/tools/entrypoint_codegen/app/run.h b/pp/tools/entrypoint_codegen/app/run.h index cd67916670..c31412baeb 100644 --- a/pp/tools/entrypoint_codegen/app/run.h +++ b/pp/tools/entrypoint_codegen/app/run.h @@ -1,9 +1,17 @@ #pragma once +#include "app/memory_usage.h" #include "app/options.h" +#include "diagnostics/diagnostics.h" namespace entrypoint_codegen::app { +struct RunReport { + ExitDecision decision; + diagnostics::SeverityCounts diagnostics; + MemoryUsageSnapshot memory_usage; +}; + RunReport run(const RunOptions& options); } // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/run_tests.cpp b/pp/tools/entrypoint_codegen/app/run_tests.cpp index 56a1f923ba..73a218160e 100644 --- a/pp/tools/entrypoint_codegen/app/run_tests.cpp +++ b/pp/tools/entrypoint_codegen/app/run_tests.cpp @@ -26,10 +26,17 @@ std::filesystem::path write_source_file(std::string_view name, std::string_view entrypoint_codegen::app::RunOptions check_options_for(std::filesystem::path source_file) { return entrypoint_codegen::app::RunOptions{ - .source_files = {std::move(source_file)}, - .clang_args = {"-std=c++2b"}, - .output_path = {}, - .output_mode = entrypoint_codegen::app::OutputMode::kCheck, + .analysis = + entrypoint_codegen::app::AnalysisOptions{ + .source_files = {std::move(source_file)}, + .clang_args = {"-std=c++2b"}, + }, + .output = + entrypoint_codegen::app::OutputOptions{ + .output_path = {}, + .output_mode = entrypoint_codegen::app::OutputMode::kCheck, + }, + .runtime = entrypoint_codegen::app::RuntimeOptions{}, }; } @@ -45,8 +52,8 @@ TEST(RunTest, CheckModeSucceedsForValidAnnotatedEntrypoint) { // Assert EXPECT_EQ(report.decision, entrypoint_codegen::app::ExitDecision::kSuccess); - EXPECT_EQ(report.error_count, 0); - EXPECT_EQ(report.diagnostic_count, 0); + EXPECT_EQ(report.diagnostics.errors, 0); + EXPECT_EQ(report.diagnostics.total, 0); } TEST(RunTest, CheckModeFailsWhenValidationReportsErrors) { @@ -61,8 +68,8 @@ TEST(RunTest, CheckModeFailsWhenValidationReportsErrors) { // Assert EXPECT_EQ(report.decision, entrypoint_codegen::app::ExitDecision::kAnalysisFailed); - EXPECT_EQ(report.error_count, 1); - EXPECT_EQ(report.diagnostic_count, 1); + EXPECT_EQ(report.diagnostics.errors, 1); + EXPECT_EQ(report.diagnostics.total, 1); } } // namespace diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug.cpp index e46d7ab5e0..18a3b94933 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug.cpp +++ b/pp/tools/entrypoint_codegen/app/runtime_debug.cpp @@ -5,7 +5,7 @@ namespace entrypoint_codegen::app { -void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, RuntimeDebugSnapshot snapshot) { +void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, MemoryUsageSnapshot snapshot) { const std::string message = "App PMR allocations: allocated=" + std::to_string(snapshot.allocated_bytes) + " deallocated=" + std::to_string(snapshot.deallocated_bytes) + " peak_live=" + std::to_string(snapshot.peak_live_bytes) + " bytes"; diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.h b/pp/tools/entrypoint_codegen/app/runtime_debug.h index 5519490cb2..aa1513bd0a 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug.h +++ b/pp/tools/entrypoint_codegen/app/runtime_debug.h @@ -1,17 +1,10 @@ #pragma once +#include "app/memory_usage.h" #include "diagnostics/diagnostics.h" -#include - namespace entrypoint_codegen::app { -struct RuntimeDebugSnapshot { - size_t allocated_bytes; - size_t deallocated_bytes; - size_t peak_live_bytes; -}; - -void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, RuntimeDebugSnapshot snapshot); +void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, MemoryUsageSnapshot snapshot); } // namespace entrypoint_codegen::app diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp index 9f5106b28b..061e6d14b1 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp +++ b/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp @@ -8,7 +8,7 @@ namespace { -using entrypoint_codegen::app::RuntimeDebugSnapshot; +using entrypoint_codegen::app::MemoryUsageSnapshot; using entrypoint_codegen::diagnostics::DiagnosticCode; using entrypoint_codegen::diagnostics::DiagnosticSet; using entrypoint_codegen::diagnostics::Severity; @@ -20,12 +20,11 @@ class RuntimeDebugTest : public testing::Test { TEST_F(RuntimeDebugTest, AppendsMemoryUsageDiagnostic) { // Act - entrypoint_codegen::app::append_runtime_debug_diagnostics(diagnostics_, - RuntimeDebugSnapshot{ - .allocated_bytes = 11, - .deallocated_bytes = 7, - .peak_live_bytes = 9, - }); + entrypoint_codegen::app::append_runtime_debug_diagnostics(diagnostics_, MemoryUsageSnapshot{ + .allocated_bytes = 11, + .deallocated_bytes = 7, + .peak_live_bytes = 9, + }); const auto diagnostics = diagnostics_.diagnostics(); // Assert diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp index 605a4439e2..88ba71074d 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp @@ -4,6 +4,10 @@ namespace entrypoint_codegen::diagnostics { +bool SeverityCounts::has_errors() const noexcept { + return errors != 0; +} + DiagnosticSet::DiagnosticSet(std::pmr::memory_resource* memory_resource) : diagnostics_(memory_resource) {} void DiagnosticSet::add(Diagnostic diagnostic) { @@ -22,4 +26,26 @@ bool DiagnosticSet::empty() const noexcept { return diagnostics_.empty(); } +SeverityCounts count_by_severity(const DiagnosticSet& diagnostic_set) { + SeverityCounts counts; + for (const Diagnostic& diagnostic : diagnostic_set.diagnostics()) { + ++counts.total; + switch (diagnostic.severity) { + case Severity::kInfo: { + ++counts.infos; + break; + } + case Severity::kWarning: { + ++counts.warnings; + break; + } + case Severity::kError: { + ++counts.errors; + break; + } + } + } + return counts; +} + } // namespace entrypoint_codegen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h index 3190d7abea..6123b79e91 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h @@ -43,6 +43,15 @@ struct Diagnostic { std::optional location; }; +struct SeverityCounts { + uint32_t total = 0; + uint32_t errors = 0; + uint32_t warnings = 0; + uint32_t infos = 0; + + [[nodiscard]] bool has_errors() const noexcept; +}; + class DiagnosticSet { public: explicit DiagnosticSet(std::pmr::memory_resource* memory_resource = std::pmr::get_default_resource()); @@ -57,4 +66,6 @@ class DiagnosticSet { std::pmr::vector diagnostics_; }; +[[nodiscard]] SeverityCounts count_by_severity(const DiagnosticSet& diagnostic_set); + } // namespace entrypoint_codegen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp index e4972b5d82..986d85a1b8 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp @@ -46,4 +46,39 @@ TEST_F(DiagnosticSetTest, StoresDiagnosticsAndCountsThem) { EXPECT_EQ(stored[0].location->column, location.column); } +TEST_F(DiagnosticSetTest, CountsDiagnosticsBySeverity) { + // Arrange + diagnostics_.add(Diagnostic{ + .code = DiagnosticCode::kRuntimeMemoryUsage, + .message = std::nullopt, + .severity = Severity::kInfo, + .function = std::nullopt, + .location = std::nullopt, + }); + diagnostics_.add(Diagnostic{ + .code = DiagnosticCode::kClangDiagnostic, + .message = std::nullopt, + .severity = Severity::kWarning, + .function = std::nullopt, + .location = std::nullopt, + }); + diagnostics_.add(Diagnostic{ + .code = DiagnosticCode::kMissingNamePrefix, + .message = std::nullopt, + .severity = Severity::kError, + .function = std::nullopt, + .location = std::nullopt, + }); + + // Act + const entrypoint_codegen::diagnostics::SeverityCounts counts = entrypoint_codegen::diagnostics::count_by_severity(diagnostics_); + + // Assert + EXPECT_EQ(counts.total, 3); + EXPECT_EQ(counts.errors, 1); + EXPECT_EQ(counts.warnings, 1); + EXPECT_EQ(counts.infos, 1); + EXPECT_TRUE(counts.has_errors()); +} + } // namespace From fe734e576e3bd7d4427c17d392c689f762a4d7ef Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Mon, 6 Jul 2026 09:52:09 +0000 Subject: [PATCH 14/31] remove build system --- pp/tools/entrypoint_codegen/.bazelignore | 4 - pp/tools/entrypoint_codegen/.bazelrc | 1 - pp/tools/entrypoint_codegen/.clangd | 5 - pp/tools/entrypoint_codegen/BUILD | 312 ------------------ pp/tools/entrypoint_codegen/MODULE.bazel | 13 - pp/tools/entrypoint_codegen/MODULE.bazel.lock | 143 -------- pp/tools/entrypoint_codegen/build_defs.bzl | 8 - .../libclang_repository.bzl | 133 -------- 8 files changed, 619 deletions(-) delete mode 100644 pp/tools/entrypoint_codegen/.bazelignore delete mode 100644 pp/tools/entrypoint_codegen/.bazelrc delete mode 100644 pp/tools/entrypoint_codegen/.clangd delete mode 100644 pp/tools/entrypoint_codegen/BUILD delete mode 100644 pp/tools/entrypoint_codegen/MODULE.bazel delete mode 100644 pp/tools/entrypoint_codegen/MODULE.bazel.lock delete mode 100644 pp/tools/entrypoint_codegen/build_defs.bzl delete mode 100644 pp/tools/entrypoint_codegen/libclang_repository.bzl diff --git a/pp/tools/entrypoint_codegen/.bazelignore b/pp/tools/entrypoint_codegen/.bazelignore deleted file mode 100644 index 41c9094078..0000000000 --- a/pp/tools/entrypoint_codegen/.bazelignore +++ /dev/null @@ -1,4 +0,0 @@ -bazel-bin -bazel-entrypoint_codegen -bazel-out -bazel-testlogs diff --git a/pp/tools/entrypoint_codegen/.bazelrc b/pp/tools/entrypoint_codegen/.bazelrc deleted file mode 100644 index c1abb89e92..0000000000 --- a/pp/tools/entrypoint_codegen/.bazelrc +++ /dev/null @@ -1 +0,0 @@ -common --noenable_workspace diff --git a/pp/tools/entrypoint_codegen/.clangd b/pp/tools/entrypoint_codegen/.clangd deleted file mode 100644 index 6b8a7e66b9..0000000000 --- a/pp/tools/entrypoint_codegen/.clangd +++ /dev/null @@ -1,5 +0,0 @@ -CompileFlags: - Add: - - "-std=c++2b" - - "-I/workspaces/prompp/pp/tools/entrypoint_codegen" - - "-isystem/usr/lib/llvm-21/include" diff --git a/pp/tools/entrypoint_codegen/BUILD b/pp/tools/entrypoint_codegen/BUILD deleted file mode 100644 index e586110b38..0000000000 --- a/pp/tools/entrypoint_codegen/BUILD +++ /dev/null @@ -1,312 +0,0 @@ -load(":build_defs.bzl", "entrypoint_codegen_copts") - -package(default_visibility = ["//visibility:private"]) - -ENTRYPOINT_CODEGEN_COPTS = entrypoint_codegen_copts() - -cc_library( - name = "facts", - srcs = [ - "facts/entrypoint_facts.cpp", - "facts/string_table.cpp", - ], - hdrs = [ - "facts/entrypoint_facts.h", - "facts/facts.h", - "facts/string_table.h", - "facts/tagged_index.h", - ], - copts = ENTRYPOINT_CODEGEN_COPTS, -) - -cc_test( - name = "entrypoint_facts_test", - srcs = ["facts/entrypoint_facts_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":facts", - "@gtest//:gtest_main", - ], -) - -cc_library( - name = "diagnostics", - srcs = ["diagnostics/diagnostics.cpp"], - hdrs = ["diagnostics/diagnostics.h"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [":facts"], -) - -cc_test( - name = "diagnostics_test", - srcs = ["diagnostics/diagnostics_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":diagnostics", - ":facts", - "@gtest//:gtest_main", - ], -) - -cc_library( - name = "diagnostic_catalog", - srcs = ["diagnostics/diagnostic_catalog.cpp"], - hdrs = ["diagnostics/diagnostic_catalog.h"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [":diagnostics"], -) - -cc_test( - name = "diagnostic_catalog_test", - srcs = ["diagnostics/diagnostic_catalog_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":diagnostic_catalog", - ":diagnostics", - ":facts", - "@gtest//:gtest_main", - ], -) - -cc_library( - name = "entrypoint_contract", - srcs = ["contract/entrypoint_contract.cpp"], - hdrs = ["contract/entrypoint_contract.h"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":facts", - ], -) - -cc_test( - name = "entrypoint_contract_test", - srcs = ["contract/entrypoint_contract_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":entrypoint_contract", - ":facts", - "@gtest//:gtest_main", - ], -) - -cc_library( - name = "clang_adapter", - srcs = [ - "clang_adapter/aggregate_source.cpp", - "clang_adapter/aggregate_source.h", - "clang_adapter/clang_runtime.h", - "clang_adapter/clang_runtime.cpp", - "clang_adapter/parse.cpp", - ], - hdrs = ["clang_adapter/parse.h"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":diagnostics", - ":entrypoint_contract", - ":facts", - "@system_libclang//:libclang", - ], -) - -cc_test( - name = "clang_adapter_test", - srcs = ["clang_adapter/parse_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":clang_adapter", - ":diagnostics", - ":facts", - "@gtest//:gtest_main", - ], -) - -cc_library( - name = "validate", - srcs = ["validate/validate.cpp"], - hdrs = ["validate/validate.h"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":diagnostics", - ":entrypoint_contract", - ":facts", - ], -) - -cc_test( - name = "validate_test", - srcs = ["validate/validate_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":diagnostics", - ":facts", - ":validate", - "@gtest//:gtest_main", - ], -) - -cc_library( - name = "emit_json", - srcs = ["emit/json.cpp"], - hdrs = ["emit/json.h"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":diagnostic_catalog", - ":diagnostics", - ":facts", - ], -) - -cc_test( - name = "emit_json_test", - srcs = ["emit/json_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":diagnostics", - ":emit_json", - ":facts", - "@gtest//:gtest_main", - ], -) - -cc_library( - name = "emit_diagnostics", - srcs = ["emit/diagnostics.cpp"], - hdrs = ["emit/diagnostics.h"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":diagnostic_catalog", - ":diagnostics", - ":facts", - ], -) - -cc_test( - name = "emit_diagnostics_test", - srcs = ["emit/diagnostics_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":diagnostics", - ":emit_diagnostics", - ":facts", - "@gtest//:gtest_main", - ], -) - -cc_library( - name = "app_memory_usage", - hdrs = ["app/memory_usage.h"], - copts = ENTRYPOINT_CODEGEN_COPTS, -) - -cc_library( - name = "app_options", - hdrs = ["app/options.h"], - copts = ENTRYPOINT_CODEGEN_COPTS, -) - -cc_library( - name = "app_argparse", - srcs = [ - "app/argparse.cpp", - ], - hdrs = [ - "app/argparse.h", - ], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":app_options", - ], -) - -cc_library( - name = "app_memory_tracking", - srcs = ["app/memory_tracking.cpp"], - hdrs = ["app/memory_tracking.h"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [":app_memory_usage"], -) - -cc_test( - name = "memory_tracking_test", - srcs = ["app/memory_tracking_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":app_memory_tracking", - "@gtest//:gtest_main", - ], -) - -cc_library( - name = "app_runtime_debug", - srcs = ["app/runtime_debug.cpp"], - hdrs = ["app/runtime_debug.h"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":app_memory_usage", - ":diagnostics", - ], -) - -cc_library( - name = "app_run", - srcs = ["app/run.cpp"], - hdrs = [ - "app/run.h", - ], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":app_memory_usage", - ":app_memory_tracking", - ":app_options", - ":app_runtime_debug", - ":clang_adapter", - ":diagnostics", - ":emit_diagnostics", - ":emit_json", - ":facts", - ":validate", - ], -) - -cc_test( - name = "argparse_test", - srcs = ["app/argparse_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":app_argparse", - "@gtest//:gtest_main", - ], -) - -cc_test( - name = "runtime_debug_test", - srcs = ["app/runtime_debug_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":app_memory_usage", - ":app_runtime_debug", - ":diagnostics", - "@gtest//:gtest_main", - ], -) - -cc_test( - name = "run_test", - srcs = ["app/run_tests.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - deps = [ - ":app_run", - "@gtest//:gtest_main", - ], -) - -cc_binary( - name = "entrypoint_codegen", - srcs = ["app/main.cpp"], - copts = ENTRYPOINT_CODEGEN_COPTS, - visibility = ["//visibility:public"], - deps = [ - ":app_argparse", - ":app_run", - ], -) diff --git a/pp/tools/entrypoint_codegen/MODULE.bazel b/pp/tools/entrypoint_codegen/MODULE.bazel deleted file mode 100644 index 47faafd3bb..0000000000 --- a/pp/tools/entrypoint_codegen/MODULE.bazel +++ /dev/null @@ -1,13 +0,0 @@ -module(name = "entrypoint_codegen") - -bazel_dep(name = "rules_cc", version = "0.2.16") - -bazel_dep( - name = "googletest", - dev_dependency = True, - repo_name = "gtest", - version = "1.11.0", -) - -libclang = use_extension("//:libclang_repository.bzl", "libclang_extension") -use_repo(libclang, "system_libclang") diff --git a/pp/tools/entrypoint_codegen/MODULE.bazel.lock b/pp/tools/entrypoint_codegen/MODULE.bazel.lock deleted file mode 100644 index 16d03fcf26..0000000000 --- a/pp/tools/entrypoint_codegen/MODULE.bazel.lock +++ /dev/null @@ -1,143 +0,0 @@ -{ - "lockFileVersion": 11, - "registryFileHashes": { - "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", - "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", - "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", - "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", - "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", - "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/source.json": "14892cc698e02ffedf4967546e6bedb7245015906888d3465fcf27c90a26da10", - "https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel": "50341a62efbc483e8a2a6aec30994a58749bd7b885e18dd96aa8c33031e558ef", - "https://bcr.bazel.build/modules/apple_support/1.5.0/source.json": "eb98a7627c0bc486b57f598ad8da50f6625d974c8f723e9ea71bd39f709c9862", - "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", - "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d", - "https://bcr.bazel.build/modules/bazel_features/1.28.0/source.json": "16a3fc5b4483cb307643791f5a4b7365fa98d2e70da7c378cdbde55f0c0b32cf", - "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", - "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", - "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", - "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", - "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", - "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", - "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", - "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953", - "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", - "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", - "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", - "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", - "https://bcr.bazel.build/modules/googletest/1.14.0/source.json": "2478949479000fdd7de9a3d0107ba2c85bb5f961c3ecb1aa448f52549ce310b5", - "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", - "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", - "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", - "https://bcr.bazel.build/modules/platforms/0.0.10/source.json": "f22828ff4cf021a6b577f1bf6341cb9dcd7965092a439f64fc1bb3b7a5ae4bd5", - "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", - "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", - "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", - "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", - "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", - "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", - "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", - "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", - "https://bcr.bazel.build/modules/protobuf/27.0/source.json": "1acf3d080c728d42f423fde5422fd0a1a24f44c15908124ce12363a253384193", - "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", - "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", - "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", - "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", - "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", - "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", - "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", - "https://bcr.bazel.build/modules/rules_cc/0.2.16/MODULE.bazel": "9242fa89f950c6ef7702801ab53922e99c69b02310c39fb6e62b2bd30df2a1d4", - "https://bcr.bazel.build/modules/rules_cc/0.2.16/source.json": "d03d5cde49376d87e14ec14b666c56075e5e3926930327fd5d0484a1ff2ac1cc", - "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", - "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", - "https://bcr.bazel.build/modules/rules_java/7.6.5/MODULE.bazel": "481164be5e02e4cab6e77a36927683263be56b7e36fef918b458d7a8a1ebadb1", - "https://bcr.bazel.build/modules/rules_java/7.6.5/source.json": "a805b889531d1690e3c72a7a7e47a870d00323186a9904b36af83aa3d053ee8d", - "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", - "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", - "https://bcr.bazel.build/modules/rules_jvm_external/5.1/source.json": "5abb45cc9beb27b77aec6a65a11855ef2b55d95dfdc358e9f312b78ae0ba32d5", - "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", - "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", - "https://bcr.bazel.build/modules/rules_license/0.0.7/source.json": "355cc5737a0f294e560d52b1b7a6492d4fff2caf0bef1a315df5a298fca2d34a", - "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", - "https://bcr.bazel.build/modules/rules_pkg/0.7.0/source.json": "c2557066e0c0342223ba592510ad3d812d4963b9024831f7f66fd0584dd8c66c", - "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", - "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", - "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/source.json": "d57902c052424dfda0e71646cb12668d39c4620ee0544294d9d941e7d12bc3a9", - "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", - "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel": "26114f0c0b5e93018c0c066d6673f1a2c3737c7e90af95eff30cfee38d0bbac7", - "https://bcr.bazel.build/modules/rules_python/0.22.1/source.json": "57226905e783bae7c37c2dd662be078728e48fa28ee4324a7eabcafb5a43d014", - "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", - "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", - "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", - "https://bcr.bazel.build/modules/stardoc/0.5.3/source.json": "cd53fe968dc8cd98197c052db3db6d82562960c87b61e7a90ee96f8e4e0dda97", - "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", - "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", - "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d" - }, - "selectedYankedVersions": {}, - "moduleExtensions": { - "//:libclang_repository.bzl%libclang_extension": { - "general": { - "bzlTransitiveDigest": "QhHM+hE3cYM+CNga0q6YabsxuePowVG61SE5NQ+/3Xk=", - "usagesDigest": "9+KLxb6yQ4k6Qd6N9mM+l30Tc/TYMc0kY0YZDcH4CEo=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "system_libclang": { - "bzlFile": "@@//:libclang_repository.bzl", - "ruleClassName": "libclang_repository", - "attributes": {} - } - }, - "recordedRepoMappingEntries": [] - } - }, - "@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": { - "general": { - "bzlTransitiveDigest": "PjIds3feoYE8SGbbIq2SFTZy3zmxeO2tQevJZNDo7iY=", - "usagesDigest": "+hz7IHWN6A1oVJJWNDB6yZRG+RYhF76wAYItpAeIUIg=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "local_config_apple_cc_toolchains": { - "bzlFile": "@@apple_support~//crosstool:setup.bzl", - "ruleClassName": "_apple_cc_autoconf_toolchains", - "attributes": {} - }, - "local_config_apple_cc": { - "bzlFile": "@@apple_support~//crosstool:setup.bzl", - "ruleClassName": "_apple_cc_autoconf", - "attributes": {} - } - }, - "recordedRepoMappingEntries": [ - [ - "apple_support~", - "bazel_tools", - "bazel_tools" - ] - ] - } - }, - "@@platforms//host:extension.bzl%host_platform": { - "general": { - "bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=", - "usagesDigest": "hgylFkgWSg0ulUwWZzEM1aIftlUnbmw2ynWLdEfHnZc=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "host_platform": { - "bzlFile": "@@platforms//host:extension.bzl", - "ruleClassName": "host_platform_repo", - "attributes": {} - } - }, - "recordedRepoMappingEntries": [] - } - } - } -} diff --git a/pp/tools/entrypoint_codegen/build_defs.bzl b/pp/tools/entrypoint_codegen/build_defs.bzl deleted file mode 100644 index f9ff3a8584..0000000000 --- a/pp/tools/entrypoint_codegen/build_defs.bzl +++ /dev/null @@ -1,8 +0,0 @@ -def entrypoint_codegen_copts(): - include_root = native.package_name() - if include_root == "": - include_root = "." - return [ - "-std=c++2b", - "-I" + include_root, - ] diff --git a/pp/tools/entrypoint_codegen/libclang_repository.bzl b/pp/tools/entrypoint_codegen/libclang_repository.bzl deleted file mode 100644 index 69168abf7b..0000000000 --- a/pp/tools/entrypoint_codegen/libclang_repository.bzl +++ /dev/null @@ -1,133 +0,0 @@ -def _first_non_empty(values): - for value in values: - if value: - return value - return "" - -def _libclang_path(repository_ctx, attr_name, env_name, llvm_root, suffix): - attr_value = getattr(repository_ctx.attr, attr_name) - env_value = repository_ctx.os.environ.get(env_name, "") - if attr_value: - return attr_value - if env_value: - return env_value - if llvm_root: - return llvm_root + suffix - return "" - -def _libclang_repository_impl(repository_ctx): - llvm_root = _first_non_empty([ - repository_ctx.attr.llvm_root, - repository_ctx.os.environ.get("LIBCLANG_LLVM_ROOT", ""), - "/usr/lib/llvm-21", - ]) - include_dir = _libclang_path(repository_ctx, "include_dir", "LIBCLANG_INCLUDE_DIR", llvm_root, "/include") - lib_dir = _libclang_path(repository_ctx, "lib_dir", "LIBCLANG_LIB_DIR", llvm_root, "/lib") - library_name = _first_non_empty([ - repository_ctx.attr.library_name, - repository_ctx.os.environ.get("LIBCLANG_LIBRARY", ""), - "clang", - ]) - - if not include_dir: - fail("libclang include directory is not configured") - if not lib_dir: - fail("libclang library directory is not configured") - - include_path = repository_ctx.path(include_dir) - lib_path = repository_ctx.path(lib_dir) - if not include_path.exists: - fail("libclang include directory does not exist: " + include_dir) - if not lib_path.exists: - fail("libclang library directory does not exist: " + lib_dir) - - copy_result = repository_ctx.execute([ - "cp", - "-R", - include_dir, - "include", - ]) - if copy_result.return_code != 0: - fail("failed to copy libclang headers: " + copy_result.stderr) - - linkopts = [ - "-L" + lib_dir, - "-l" + library_name, - ] - if repository_ctx.attr.rpath: - linkopts.append("-Wl,-rpath," + lib_dir) - - repository_ctx.file( - "BUILD.bazel", - """package(default_visibility = ["//visibility:public"]) - -cc_library( - name = "libclang", - hdrs = glob(["include/**/*.h"]), - includes = ["include"], - linkopts = {linkopts}, -) - -alias( - name = "headers", - actual = ":libclang", -) -""".format(linkopts = repr(linkopts)), - ) - -libclang_repository = repository_rule( - implementation = _libclang_repository_impl, - attrs = { - "include_dir": attr.string(), - "lib_dir": attr.string(), - "library_name": attr.string(default = "clang"), - "llvm_root": attr.string(), - "rpath": attr.bool(default = True), - }, - environ = [ - "LIBCLANG_INCLUDE_DIR", - "LIBCLANG_LIB_DIR", - "LIBCLANG_LIBRARY", - "LIBCLANG_LLVM_ROOT", - ], -) - -_libclang_config = tag_class( - attrs = { - "include_dir": attr.string(), - "lib_dir": attr.string(), - "library_name": attr.string(default = "clang"), - "llvm_root": attr.string(), - "rpath": attr.bool(default = True), - }, -) - -def _libclang_extension_impl(module_ctx): - configs = [] - for module in module_ctx.modules: - for config in module.tags.configure: - configs.append(config) - - if len(configs) > 1: - fail("libclang_extension accepts at most one configure(...) tag") - - if configs: - config = configs[0] - libclang_repository( - name = "system_libclang", - include_dir = config.include_dir, - lib_dir = config.lib_dir, - library_name = config.library_name, - llvm_root = config.llvm_root, - rpath = config.rpath, - ) - return - - libclang_repository(name = "system_libclang") - -libclang_extension = module_extension( - implementation = _libclang_extension_impl, - tag_classes = { - "configure": _libclang_config, - }, -) From 05ac74782ea7e49b5f17583759d3ec34957ae7d4 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Mon, 6 Jul 2026 14:28:31 +0000 Subject: [PATCH 15/31] new build system --- pp/BUILD | 24 +++ pp/MODULE.bazel | 10 +- pp/bazel/rules/entrypoint_codegen.bzl | 130 +++++++++++++++ pp/tools/entrypoint_codegen/.bazelrc | 3 + pp/tools/entrypoint_codegen/BUILD.bazel | 58 +++++++ pp/tools/entrypoint_codegen/MODULE.bazel | 16 ++ pp/tools/entrypoint_codegen/README.md | 155 ++++++++++++++++++ pp/tools/entrypoint_codegen/app/main.cpp | 2 +- .../libclang_repository.bzl | 155 ++++++++++++++++++ 9 files changed, 551 insertions(+), 2 deletions(-) create mode 100644 pp/bazel/rules/entrypoint_codegen.bzl create mode 100644 pp/tools/entrypoint_codegen/.bazelrc create mode 100644 pp/tools/entrypoint_codegen/BUILD.bazel create mode 100644 pp/tools/entrypoint_codegen/MODULE.bazel create mode 100644 pp/tools/entrypoint_codegen/README.md create mode 100644 pp/tools/entrypoint_codegen/libclang_repository.bzl diff --git a/pp/BUILD b/pp/BUILD index bb57de71dc..7fe2bc7dd9 100644 --- a/pp/BUILD +++ b/pp/BUILD @@ -1,4 +1,5 @@ load("//:bazel/rules/cc_static_library.bzl", "cc_static_library") +load("//:bazel/rules/entrypoint_codegen.bzl", "entrypoint_fact_checking") package(default_visibility = ["//visibility:public"]) @@ -208,6 +209,29 @@ cc_library( }), ) +entrypoint_fact_checking( + name = "entrypoint_fact_checking", + inputs = glob([ + "bare_bones/**/*.h", + "entrypoint/**/*.h", + "entrypoint/**/*.hpp", + "head/**/*.h", + "metrics/**/*.h", + "primitives/**/*.h", + "prometheus/**/*.h", + "series_data/**/*.h", + "series_index/**/*.h", + "wal/**/*.h", + ]), + srcs = glob( + include = ["entrypoint/**/*.cpp"], + exclude = [ + "entrypoint/init/**", + "entrypoint/**/*_tests.cpp", + ], + ), +) + cc_static_library( name = "entrypoint_aio", deps = [ diff --git a/pp/MODULE.bazel b/pp/MODULE.bazel index 5c323ec422..da7e8e2d87 100644 --- a/pp/MODULE.bazel +++ b/pp/MODULE.bazel @@ -13,9 +13,17 @@ module( # --- Bazel rule sets (from BCR) ----------------------------------------------- bazel_dep(name = "bazel_skylib", version = "1.9.0") bazel_dep(name = "platforms", version = "0.0.11") -bazel_dep(name = "rules_cc", version = "0.1.1") +bazel_dep(name = "rules_cc", version = "0.2.16") bazel_dep(name = "rules_foreign_cc", version = "0.15.1") +# Local standalone component. It can be built independently from +# tools/entrypoint_codegen, and pp consumes it as an external module. +bazel_dep(name = "entrypoint_codegen", version = "0", dev_dependency = True) +local_path_override( + module_name = "entrypoint_codegen", + path = "tools/entrypoint_codegen", +) + # --- Tools not published to BCR (use git_override) ---------------------------- # bazel_clang_tidy is only used by `make tidy` (clang-tidy aspect); it never # enters the production entrypoint build, so it is a dev-only dependency. This diff --git a/pp/bazel/rules/entrypoint_codegen.bzl b/pp/bazel/rules/entrypoint_codegen.bzl new file mode 100644 index 0000000000..5adfbd3cf4 --- /dev/null +++ b/pp/bazel/rules/entrypoint_codegen.bzl @@ -0,0 +1,130 @@ +def _path_args(flag, paths): + return [flag + path for path in paths] + +def _entrypoint_fact_checking_impl(ctx): + output = ctx.actions.declare_file(ctx.label.name + ".json") + log = ctx.actions.declare_file(ctx.label.name + ".log") + + clang_args = [] + clang_args.extend(_path_args("-I", ctx.attr.includes)) + clang_args.extend(_path_args("-iquote", ctx.attr.quote_includes)) + clang_args.extend(_path_args("-isystem", ctx.attr.system_includes)) + + json_args = ctx.actions.args() + json_args.add("--mode=json") + json_args.add("--output=" + output.path) + json_args.add_all(ctx.files.srcs) + json_args.add("--") + json_args.add_all(ctx.attr.clang_args) + json_args.add_all(clang_args) + + lint_args = ctx.actions.args() + lint_args.add("--mode=lint") + lint_args.add_all(ctx.files.srcs) + lint_args.add("--") + lint_args.add_all(ctx.attr.clang_args) + lint_args.add_all(clang_args) + + inputs = depset(ctx.files.srcs + ctx.files.inputs) + + ctx.actions.run_shell( + inputs = inputs, + tools = [ctx.executable.tool], + outputs = [ + output, + log, + ], + arguments = [ + ctx.executable.tool.path, + log.path, + output.path, + json_args, + "--entrypoint_fact_checking_lint_args--", + lint_args, + ], + command = """ +tool="$1" +log="$2" +output="$3" +shift 3 + +json_args=() +while [ "$#" -gt 0 ] && [ "$1" != "--entrypoint_fact_checking_lint_args--" ]; do + json_args+=("$1") + shift +done + +if [ "$#" -gt 0 ]; then + shift +fi + +lint_args=("$@") + +set +e +"${tool}" "${json_args[@]}" >"${log}" 2>&1 +status=$? + +{ + echo + echo "entrypoint_fact_checking diagnostics:" +} >>"${log}" +"${tool}" "${lint_args[@]}" >>"${log}" 2>&1 +lint_status=$? + +if [ "${status}" -eq 0 ]; then + status="${lint_status}" +fi + +if [ -s "${log}" ]; then + sed 's/^/entrypoint_fact_checking: /' "${log}" >&2 +fi + +if [ ! -s "${output}" ]; then + echo "ERROR: entrypoint_fact_checking did not produce ${output}; see ${log}" >&2 + if [ "${status}" -ne 0 ]; then + exit "${status}" + fi + exit 1 +fi + +if [ "${status}" -ne 0 ]; then + echo "WARNING: entrypoint_fact_checking reported diagnostics; see ${output} and ${log}" >&2 +fi +exit 0 +""", + mnemonic = "EntrypointFactChecking", + progress_message = "Checking entrypoint facts for %{label}", + ) + + return [ + DefaultInfo(files = depset([ + output, + log, + ])), + ] + +entrypoint_fact_checking = rule( + implementation = _entrypoint_fact_checking_impl, + attrs = { + "clang_args": attr.string_list( + default = ["-std=c++2b"], + ), + "includes": attr.string_list( + default = ["."], + ), + "inputs": attr.label_list( + allow_files = True, + ), + "quote_includes": attr.string_list(), + "system_includes": attr.string_list(), + "srcs": attr.label_list( + allow_files = [".cc", ".cpp", ".cxx"], + mandatory = True, + ), + "tool": attr.label( + default = Label("@entrypoint_codegen//:entrypoint_codegen"), + executable = True, + cfg = "exec", + ), + }, +) diff --git a/pp/tools/entrypoint_codegen/.bazelrc b/pp/tools/entrypoint_codegen/.bazelrc new file mode 100644 index 0000000000..b8a57ea8c5 --- /dev/null +++ b/pp/tools/entrypoint_codegen/.bazelrc @@ -0,0 +1,3 @@ +common --noenable_workspace + +build --noreuse_sandbox_directories diff --git a/pp/tools/entrypoint_codegen/BUILD.bazel b/pp/tools/entrypoint_codegen/BUILD.bazel new file mode 100644 index 0000000000..4a710bee3f --- /dev/null +++ b/pp/tools/entrypoint_codegen/BUILD.bazel @@ -0,0 +1,58 @@ +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "entrypoint_codegen_lib", + srcs = glob( + include = [ + "app/*.cpp", + "clang_adapter/*.cpp", + "contract/*.cpp", + "diagnostics/*.cpp", + "emit/*.cpp", + "facts/*.cpp", + "validate/*.cpp", + ], + exclude = [ + "app/main.cpp", + "**/*_tests.cpp", + ], + ), + hdrs = glob([ + "app/*.h", + "clang_adapter/*.h", + "contract/*.h", + "diagnostics/*.h", + "emit/*.h", + "facts/*.h", + "validate/*.h", + ]), + copts = [ + "-std=c++2b", + ], + deps = [ + "@system_libclang//:libclang", + ], +) + +cc_binary( + name = "entrypoint_codegen", + srcs = ["app/main.cpp"], + copts = [ + "-std=c++2b", + ], + deps = [ + ":entrypoint_codegen_lib", + ], +) + +cc_test( + name = "entrypoint_codegen_test", + srcs = glob(["**/*_tests.cpp"]), + copts = [ + "-std=c++2b", + ], + deps = [ + ":entrypoint_codegen_lib", + "@googletest//:gtest_main", + ], +) diff --git a/pp/tools/entrypoint_codegen/MODULE.bazel b/pp/tools/entrypoint_codegen/MODULE.bazel new file mode 100644 index 0000000000..e5a826bb2b --- /dev/null +++ b/pp/tools/entrypoint_codegen/MODULE.bazel @@ -0,0 +1,16 @@ +module( + name = "entrypoint_codegen", + version = "0", +) + +bazel_dep(name = "platforms", version = "0.0.11") +bazel_dep(name = "rules_cc", version = "0.2.16") + +bazel_dep(name = "googletest", version = "1.15.2") + +libclang = use_extension("//:libclang_repository.bzl", "libclang_extension") +libclang.configure( + expected_version = "21.1.8", + llvm_root = "/usr/lib/llvm-21", +) +use_repo(libclang, "system_libclang") diff --git a/pp/tools/entrypoint_codegen/README.md b/pp/tools/entrypoint_codegen/README.md new file mode 100644 index 0000000000..36ad4eb8b4 --- /dev/null +++ b/pp/tools/entrypoint_codegen/README.md @@ -0,0 +1,155 @@ +# entrypoint_codegen + +`entrypoint_codegen` is a libclang-based scanner for Prom++ entrypoint +translation units. It can run as a standalone Bazel module from this directory, +and pp also exposes a lightweight report target at `//:entrypoint_fact_checking`. + +The pp report target is intentionally not wired into `//:entrypoint` builds yet. +It is a manual diagnostic/reporting surface, not a build gate. + +## What It Extracts + +- `prompp_*` entrypoint function definitions. +- Bridge kind from `annotate("prompp.entrypoint.cgo")` or + `annotate("prompp.entrypoint.fastcgo")`. +- Function return type, linkage, parameter count, parameter names and parameter + types. +- Local layout declarations: + - `struct Arguments { ... }` + - `struct Result { ... }` + - `using Arguments = struct { ... };` + - `using Result = struct { ... };` +- Structured diagnostics: + - missing `extern "C"` linkage + - missing entrypoint annotation + - unsupported return type + - unsupported parameter count or parameter type + - `args`/`res` layout mismatches + - libclang parse diagnostics + +## Standalone Build + +From `tools/entrypoint_codegen`: + +```bash +bazel build //:entrypoint_codegen +``` + +The standalone module expects libclang to be provided by the local environment. +The default configuration in `MODULE.bazel` currently expects LLVM/libclang +`21.1.8` under `/usr/lib/llvm-21`: + +```starlark +libclang = use_extension("//:libclang_repository.bzl", "libclang_extension") +libclang.configure( + expected_version = "21.1.8", + llvm_root = "/usr/lib/llvm-21", +) +use_repo(libclang, "system_libclang") +``` + +You can override the location from the command line: + +```bash +bazel build \ + --repo_env=LIBCLANG_LLVM_ROOT=/usr/lib/llvm-21 \ + //:entrypoint_codegen +``` + +If headers and libraries are installed separately: + +```bash +bazel build \ + --repo_env=LIBCLANG_INCLUDE_DIR=/opt/llvm/include \ + --repo_env=LIBCLANG_LIB_DIR=/opt/llvm/lib \ + --repo_env=LIBCLANG_LIBRARY=clang \ + //:entrypoint_codegen +``` + +## Standalone Run + +Generate JSON: + +```bash +bazel run //:entrypoint_codegen -- \ + --output=/tmp/entrypoint_facts.json \ + /workspaces/prompp/pp/entrypoint/common.cpp \ + -- \ + -std=c++2b \ + -I/workspaces/prompp/pp +``` + +Print compiler-style diagnostics instead of JSON: + +```bash +bazel run //:entrypoint_codegen -- \ + --mode=lint \ + /workspaces/prompp/pp/entrypoint/common.cpp \ + -- \ + -std=c++2b \ + -I/workspaces/prompp/pp +``` + +Run as check-only: + +```bash +bazel run //:entrypoint_codegen -- \ + --mode=check \ + /workspaces/prompp/pp/entrypoint/common.cpp \ + -- \ + -std=c++2b \ + -I/workspaces/prompp/pp +``` + +Inputs may be files or directories. Directory inputs are scanned recursively for +`.cpp`, `.cc`, and `.cxx` files. + +## pp Report Target + +From the pp workspace root: + +```bash +bazel build //:entrypoint_fact_checking +``` + +This produces: + +```text +bazel-bin/entrypoint_fact_checking.json +bazel-bin/entrypoint_fact_checking.log +``` + +The target is deliberately simple. Its `srcs` are the entrypoint `.cpp` files to +scan. Its `inputs` are local headers that Bazel should make available to the +action sandbox. It does not depend on `//:entrypoint`, and it does not enumerate +the full entrypoint dependency graph. + +Because the pp target is currently a report target, diagnostics do not fail the +Bazel action as long as JSON was produced. The diagnostics are available in both +the JSON output and the text log. + +## Output Modes + +- `--mode=json`: write structured facts and diagnostics to JSON. +- `--mode=lint`: print compiler-style diagnostics to stdout. +- `--mode=check`: run analysis without writing facts or diagnostics. + +Useful options: + +- `--output=PATH`: JSON output path. +- `--output-dir=PATH`: write `entrypoint_facts.json` in a directory. +- `--clang-arg=ARG` / `--extra-arg=ARG`: add one libclang parser argument. +- `--clang-args-file=PATH`: read parser arguments, one per line. +- `--runtime-debug`: append tool-owned runtime diagnostics. +- `--`: treat remaining arguments as libclang parser arguments. + +## Exit Codes + +For the standalone binary: + +- `0`: no error diagnostics were found. +- `1`: invalid invocation, infrastructure failure, or at least one error + diagnostic was found. + +The pp `//:entrypoint_fact_checking` target wraps the standalone binary and +keeps diagnostics report-only for now. diff --git a/pp/tools/entrypoint_codegen/app/main.cpp b/pp/tools/entrypoint_codegen/app/main.cpp index 374672986d..577b9aa4b5 100644 --- a/pp/tools/entrypoint_codegen/app/main.cpp +++ b/pp/tools/entrypoint_codegen/app/main.cpp @@ -45,7 +45,7 @@ int main(int argc, char** argv) { std::cerr << " (" << report.diagnostics.warnings << " warnings, " << report.diagnostics.infos << " info)"; } std::cerr << "\n"; - return 2; + return 1; } return 0; } diff --git a/pp/tools/entrypoint_codegen/libclang_repository.bzl b/pp/tools/entrypoint_codegen/libclang_repository.bzl new file mode 100644 index 0000000000..18ad2b5753 --- /dev/null +++ b/pp/tools/entrypoint_codegen/libclang_repository.bzl @@ -0,0 +1,155 @@ +def _first_non_empty(values): + for value in values: + if value: + return value + return "" + +def _check_expected_version(repository_ctx, llvm_root): + expected_version = repository_ctx.attr.expected_version + if not expected_version: + return + + version_tool = _first_non_empty([ + repository_ctx.attr.version_tool, + llvm_root + "/bin/clang" if llvm_root else "", + ]) + if not version_tool or not repository_ctx.path(version_tool).exists: + fail("clang version tool is required to validate libclang version " + expected_version) + + version = repository_ctx.execute([ + version_tool, + "--version", + ]) + if version.return_code != 0: + fail("failed to query libclang LLVM version with " + version_tool + ": " + version.stderr) + + actual_version = version.stdout.splitlines()[0].strip() + if expected_version not in actual_version: + fail("expected libclang LLVM version " + expected_version + ", got " + actual_version + " from " + version_tool) + +def _system_libclang_repository_impl(repository_ctx): + llvm_root = _first_non_empty([ + repository_ctx.attr.llvm_root, + repository_ctx.getenv("LIBCLANG_LLVM_ROOT"), + ]) + + if repository_ctx.attr.llvm_root_label: + llvm_root = str(repository_ctx.path(repository_ctx.attr.llvm_root_label).dirname) + + include_dir = _first_non_empty([ + repository_ctx.attr.include_dir, + repository_ctx.getenv("LIBCLANG_INCLUDE_DIR"), + llvm_root + "/include" if llvm_root else "", + ]) + lib_dir = _first_non_empty([ + repository_ctx.attr.lib_dir, + repository_ctx.getenv("LIBCLANG_LIB_DIR"), + llvm_root + "/lib" if llvm_root else "", + ]) + library = _first_non_empty([ + repository_ctx.attr.library, + repository_ctx.getenv("LIBCLANG_LIBRARY"), + "clang", + ]) + + if not include_dir or not repository_ctx.path(include_dir).exists: + fail("libclang include directory does not exist. Set LIBCLANG_INCLUDE_DIR or LIBCLANG_LLVM_ROOT.") + if not lib_dir or not repository_ctx.path(lib_dir).exists: + fail("libclang library directory does not exist. Set LIBCLANG_LIB_DIR or LIBCLANG_LLVM_ROOT.") + + _check_expected_version(repository_ctx, llvm_root) + + copy_headers = repository_ctx.execute([ + "/bin/cp", + "-R", + include_dir, + "include", + ]) + if copy_headers.return_code != 0: + fail("failed to copy libclang headers: " + copy_headers.stderr) + + repository_ctx.symlink(lib_dir, "lib") + repository_ctx.file( + "BUILD.bazel", + """ +cc_library( + name = "libclang", + hdrs = glob(["include/**/*.h"]), + includes = ["include"], + linkopts = [ + "-L{lib_dir}", + "-l{library}", + "-Wl,-rpath,{lib_dir}", + ], + visibility = ["//visibility:public"], +) +""".format( + lib_dir = lib_dir, + library = library, + ), + ) + +_system_libclang_repository = repository_rule( + implementation = _system_libclang_repository_impl, + attrs = { + "expected_version": attr.string(), + "include_dir": attr.string(), + "lib_dir": attr.string(), + "library": attr.string(), + "llvm_root_label": attr.label(), + "llvm_root": attr.string(), + "version_tool": attr.string(), + }, + environ = [ + "LIBCLANG_INCLUDE_DIR", + "LIBCLANG_LIB_DIR", + "LIBCLANG_LIBRARY", + "LIBCLANG_LLVM_ROOT", + ], +) + +_libclang_configure = tag_class(attrs = { + "expected_version": attr.string(), + "include_dir": attr.string(), + "lib_dir": attr.string(), + "library": attr.string(), + "llvm_root_label": attr.label(), + "llvm_root": attr.string(), + "version_tool": attr.string(), +}) + +def _libclang_extension_impl(module_ctx): + config = None + + for module in module_ctx.modules: + for configure in module.tags.configure: + next_config = struct( + expected_version = configure.expected_version, + include_dir = configure.include_dir, + lib_dir = configure.lib_dir, + library = configure.library, + llvm_root = configure.llvm_root, + llvm_root_label = configure.llvm_root_label, + version_tool = configure.version_tool, + ) + if config == None or module.is_root: + config = next_config + + if config == None: + fail("libclang_extension requires a configure tag from the root module") + + _system_libclang_repository( + name = "system_libclang", + expected_version = config.expected_version, + include_dir = config.include_dir, + lib_dir = config.lib_dir, + library = config.library, + llvm_root = config.llvm_root, + llvm_root_label = config.llvm_root_label, + version_tool = config.version_tool, + ) + +libclang_extension = module_extension( + implementation = _libclang_extension_impl, + tag_classes = {"configure": _libclang_configure}, +) From 2581d385fd818ae06353e7b1f24448c28a60c300 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Tue, 7 Jul 2026 10:14:50 +0000 Subject: [PATCH 16/31] ignore tools for //... target --- pp/.bazelignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pp/.bazelignore b/pp/.bazelignore index f83c314637..c42d9b5007 100644 --- a/pp/.bazelignore +++ b/pp/.bazelignore @@ -5,3 +5,7 @@ bazel-pp bazel-out bazel-testlogs bazel-external + +# Standalone Bzlmod module consumed through @entrypoint_codegen via local_path_override. +# Keep it out of root //... package discovery. +tools/entrypoint_codegen \ No newline at end of file From 11c4ed9eab762aeb786ba9de06022e3e347ed1b1 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Wed, 8 Jul 2026 11:21:15 +0000 Subject: [PATCH 17/31] rewiew fixes --- pp/entrypoint/bridge/go_constants.cpp | 27 ++++++ pp/entrypoint/types/go_constants_tests.cpp | 33 ------- pp/entrypoint/types/querier_tests.cpp | 102 ++++----------------- 3 files changed, 46 insertions(+), 116 deletions(-) create mode 100644 pp/entrypoint/bridge/go_constants.cpp delete mode 100644 pp/entrypoint/types/go_constants_tests.cpp diff --git a/pp/entrypoint/bridge/go_constants.cpp b/pp/entrypoint/bridge/go_constants.cpp new file mode 100644 index 0000000000..613fd08695 --- /dev/null +++ b/pp/entrypoint/bridge/go_constants.cpp @@ -0,0 +1,27 @@ +#include + +#include "bare_bones/vector.h" +#include "entrypoint/types/go_constants.h" +#include "entrypoint/types/serialized_data.h" +#include "metrics/storage.h" +#include "prometheus/relabeler.h" +#include "wal/output_decoder.h" +#include "wal/segment_samples_storage.h" + +namespace { + +static_assert(sizeof(std::vector) == Sizeof_StdVector); +static_assert(sizeof(BareBones::Vector) == Sizeof_BareBonesVector); +static_assert(sizeof(roaring::Roaring) == Sizeof_RoaringBitset); + +static_assert(sizeof(PromPP::Prometheus::Relabel::InnerSeries) == Sizeof_InnerSeries); + +static_assert(sizeof(entrypoint::types::SerializedDataIterator) == Sizeof_SerializedDataIterator); + +static_assert(sizeof(metrics::Storage::Iterator) == Sizeof_MetricsIterator); + +static_assert(sizeof(PromPP::WAL::SegmentSamplesStorage) == Sizeof_SegmentSamplesStorage); +static_assert(sizeof(PromPP::WAL::ProtobufEncoder) == Sizeof_RemoteWriteMessageEncoder); +static_assert(sizeof(PromPP::WAL::SegmentSamplesStorageList::Iterator) == Sizeof_SegmentSamplesStorageListIterator); + +} // namespace diff --git a/pp/entrypoint/types/go_constants_tests.cpp b/pp/entrypoint/types/go_constants_tests.cpp deleted file mode 100644 index 73d81dc88b..0000000000 --- a/pp/entrypoint/types/go_constants_tests.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include - -#include - -#include "bare_bones/vector.h" -#include "entrypoint/types/go_constants.h" -#include "entrypoint/types/serialized_data.h" -#include "metrics/storage.h" -#include "prometheus/relabeler.h" -#include "wal/output_decoder.h" -#include "wal/segment_samples_storage.h" - -namespace { - -TEST(GoConstantsTest, CompileTimeSizesMatchConstants) { - static_assert(sizeof(std::vector) == Sizeof_StdVector); - static_assert(sizeof(BareBones::Vector) == Sizeof_BareBonesVector); - static_assert(sizeof(roaring::Roaring) == Sizeof_RoaringBitset); - - static_assert(sizeof(PromPP::Prometheus::Relabel::InnerSeries) == Sizeof_InnerSeries); - - static_assert(sizeof(entrypoint::types::SerializedDataIterator) == Sizeof_SerializedDataIterator); - - static_assert(sizeof(metrics::Storage::Iterator) == Sizeof_MetricsIterator); - - static_assert(sizeof(PromPP::WAL::SegmentSamplesStorage) == Sizeof_SegmentSamplesStorage); - static_assert(sizeof(PromPP::WAL::ProtobufEncoder) == Sizeof_RemoteWriteMessageEncoder); - static_assert(sizeof(PromPP::WAL::SegmentSamplesStorageList::Iterator) == Sizeof_SegmentSamplesStorageListIterator); - - SUCCEED(); -} - -} // namespace diff --git a/pp/entrypoint/types/querier_tests.cpp b/pp/entrypoint/types/querier_tests.cpp index 8ee230f346..8afaaa7aa4 100644 --- a/pp/entrypoint/types/querier_tests.cpp +++ b/pp/entrypoint/types/querier_tests.cpp @@ -1,9 +1,7 @@ #include #include -#include #include -#include #include #include @@ -30,37 +28,12 @@ using series_data::unloading::Unloader; using InstantQuerierWrapper = entrypoint::types::InstantQuerierWithArgumentsWrapper, std::span>; using RangeQuery = series_data::querier::Query>; -template -class UninitializedMemory { - public: - UninitializedMemory() { std::ranges::fill(storage_, kDefaultValue); } - - ~UninitializedMemory() { - if (!has_default_value()) { - std::destroy_at(ptr()); - } - } - - [[nodiscard]] T* ptr() noexcept { return reinterpret_cast(storage_); } - [[nodiscard]] const T* ptr() const noexcept { return reinterpret_cast(storage_); } - [[nodiscard]] T& value() noexcept { return *ptr(); } - [[nodiscard]] const T& value() const noexcept { return *ptr(); } - [[nodiscard]] bool has_default_value() const noexcept { - return std::ranges::all_of(storage_, [](std::byte byte) { return byte == kDefaultValue; }); - } - - private: - static constexpr auto kDefaultValue = std::byte{0x5a}; - - alignas(T) std::byte storage_[sizeof(T)]; -}; - -class RangeQuerierUninitializedMemoryFixture : public testing::Test { +class RangeQuerierWrapperFixture : public testing::Test { protected: DataStorage storage_; Encoder<> encoder_{storage_}; BareBones::ShrinkedToFitOStringStream unloaded_chunks_; - UninitializedMemory serialized_data_memory_; + entrypoint::types::SerializedDataPtr serialized_data_; RangeQuery query_for(LabelSetID label_set_id, int64_t min, int64_t max) { Slice label_set_ids; @@ -68,7 +41,13 @@ class RangeQuerierUninitializedMemoryFixture : public testing::Test { return RangeQuery{.time_interval{.min = min, .max = max}, .label_set_ids = std::move(label_set_ids)}; } - [[nodiscard]] entrypoint::types::SerializedDataPtr* serialized_data_ptr() noexcept { return serialized_data_memory_.ptr(); } + [[nodiscard]] SampleList decode_chunk(uint32_t chunk_id) const { + SampleList decoded; + std::ranges::copy(serialized_data_->iterator(chunk_id), DecodeIteratorSentinel{}, std::back_inserter(decoded)); + return decoded; + } + + [[nodiscard]] entrypoint::types::SerializedDataPtr* serialized_data_ptr() noexcept { return &serialized_data_; } void unload_open_chunks() { Unloader unloader{storage_}; @@ -84,23 +63,22 @@ class RangeQuerierUninitializedMemoryFixture : public testing::Test { } }; -TEST_F(RangeQuerierUninitializedMemoryFixture, QueryWritesSerializedDataToPreparedMemory) { +TEST_F(RangeQuerierWrapperFixture, QueryWritesSerializedDataToPreparedMemory) { // Arrange encoder_.encode(0, 1, 1.0); auto query = query_for(0, 1, 1); - const auto was_default_before_prepare = serialized_data_memory_.has_default_value(); + const auto was_null_before_prepare = serialized_data_ == nullptr; entrypoint::types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; // Act wrapper.query(); // Assert - EXPECT_TRUE(was_default_before_prepare); - EXPECT_FALSE(serialized_data_memory_.has_default_value()); - ASSERT_NE(nullptr, serialized_data_memory_.value().get()); + EXPECT_TRUE(was_null_before_prepare); + ASSERT_NE(nullptr, serialized_data_); } -TEST_F(RangeQuerierUninitializedMemoryFixture, QueryFinalizeWritesSerializedDataToPreparedMemory) { +TEST_F(RangeQuerierWrapperFixture, QueryFinalizeWritesSerializedDataToPreparedMemory) { // Arrange encoder_.encode(0, 1, 1.0); encoder_.encode(0, 2, 2.0); @@ -109,7 +87,7 @@ TEST_F(RangeQuerierUninitializedMemoryFixture, QueryFinalizeWritesSerializedData unload_open_chunks(); auto query = query_for(0, 1, 3); - const auto was_default_before_prepare = serialized_data_memory_.has_default_value(); + const auto was_null_before_prepare = serialized_data_ == nullptr; entrypoint::types::RangeQuerierWithArgumentsWrapperV2 wrapper{storage_, query, serialized_data_ptr()}; // Act @@ -120,9 +98,8 @@ TEST_F(RangeQuerierUninitializedMemoryFixture, QueryFinalizeWritesSerializedData // Assert ASSERT_TRUE(need_loading); - EXPECT_TRUE(was_default_before_prepare); - EXPECT_FALSE(serialized_data_memory_.has_default_value()); - ASSERT_NE(nullptr, serialized_data_memory_.value().get()); + EXPECT_TRUE(was_null_before_prepare); + ASSERT_NE(nullptr, serialized_data_); } class InstantQuerierWrapperFixture : public testing::Test { @@ -203,44 +180,6 @@ TEST_F(InstantQuerierWrapperFixture, QueryRequestsLoadingForUnloadedSeriesThenFi EXPECT_EQ((Sample{.timestamp = 3, .value = 3.0}), samples_[0]); } -class RangeQuerierWrapperFixture : public testing::Test { - protected: - DataStorage storage_; - Encoder<> encoder_{storage_}; - BareBones::ShrinkedToFitOStringStream unloaded_chunks_; - UninitializedMemory serialized_data_memory_; - entrypoint::types::SerializedDataPtr serialized_data_; - - RangeQuery query_for(LabelSetID label_set_id, int64_t min, int64_t max) { - Slice label_set_ids; - label_set_ids.push_back(label_set_id); - return RangeQuery{.time_interval{.min = min, .max = max}, .label_set_ids = std::move(label_set_ids)}; - } - - [[nodiscard]] SampleList decode_chunk(uint32_t chunk_id) const { - SampleList decoded; - std::ranges::copy(serialized_data_->iterator(chunk_id), DecodeIteratorSentinel{}, std::back_inserter(decoded)); - return decoded; - } - - [[nodiscard]] entrypoint::types::SerializedDataPtr* serialized_data_ptr() noexcept { return serialized_data_memory_.ptr(); } - - void take_serialized_data() { serialized_data_ = std::move(serialized_data_memory_.value()); } - - void unload_open_chunks() { - Unloader unloader{storage_}; - unloader.create_snapshot(unloaded_chunks_); - unloader.unload(); - } - - void load_unloaded_chunks(LabelSetID label_set_id) { - std::vector label_set_ids{label_set_id}; - Loader loader{storage_, label_set_ids, static_cast(label_set_ids.size())}; - loader.load_next(unloaded_chunks_.span()); - loader.load_finalize(); - } -}; - TEST_F(RangeQuerierWrapperFixture, QuerySerializesMatchingOpenChunk) { // Arrange encoder_.encode(0, 1, 1.0); @@ -254,7 +193,6 @@ TEST_F(RangeQuerierWrapperFixture, QuerySerializesMatchingOpenChunk) { // Act wrapper.query(); - take_serialized_data(); const auto decoded = decode_chunk(0); // Assert @@ -272,7 +210,6 @@ TEST_F(RangeQuerierWrapperFixture, QuerySerializesEmptyResultWhenSeriesDoesNotMa // Act wrapper.query(); - take_serialized_data(); // Assert ASSERT_FALSE(wrapper.need_loading()); @@ -296,16 +233,15 @@ TEST_F(RangeQuerierWrapperFixture, QueryDefersSerializationUntilUnloadedSeriesIs const auto need_loading = wrapper.need_loading(); const auto series_to_load_0 = wrapper.series_to_load().is_set(0); - const auto was_default_before_finalize = serialized_data_memory_.has_default_value(); + const auto was_null_before_finalize = serialized_data_ == nullptr; load_unloaded_chunks(0); wrapper.query_finalize(); - take_serialized_data(); // Assert ASSERT_TRUE(need_loading); EXPECT_TRUE(series_to_load_0); - EXPECT_TRUE(was_default_before_finalize); + EXPECT_TRUE(was_null_before_finalize); ASSERT_NE(nullptr, serialized_data_); ASSERT_EQ(1U, serialized_data_->get_chunks_view().size()); EXPECT_EQ((SampleList{{1, 1.0}, {2, 2.0}, {3, 3.0}}), decode_chunk(0)); From 8301db4e9cea3f7ad59abc0336c1f2fbb08bd56c Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Wed, 8 Jul 2026 12:34:04 +0000 Subject: [PATCH 18/31] ref --- pp/tools/entrypoint_codegen/BUILD.bazel | 2 - pp/tools/entrypoint_codegen/app/run.cpp | 11 +- .../contract/entrypoint_contract.cpp | 104 ++++++++++++++ .../contract/entrypoint_contract.h | 10 ++ .../entrypoint_contract_validation_tests.cpp} | 33 ++--- .../diagnostics/diagnostic_catalog.cpp | 129 ------------------ .../diagnostics/diagnostic_catalog.h | 14 -- .../diagnostics/diagnostic_catalog_tests.cpp | 94 ------------- .../diagnostics/diagnostics.cpp | 124 +++++++++++++++++ .../diagnostics/diagnostics.h | 5 + .../diagnostics/diagnostics_tests.cpp | 77 +++++++++++ .../entrypoint_codegen/emit/diagnostics.cpp | 20 --- .../entrypoint_codegen/emit/diagnostics.h | 12 -- .../emit/diagnostics_tests.cpp | 61 --------- pp/tools/entrypoint_codegen/emit/json.h | 12 -- .../emit/{json.cpp => report.cpp} | 38 ++++-- pp/tools/entrypoint_codegen/emit/report.h | 18 +++ .../emit/{json_tests.cpp => report_tests.cpp} | 59 ++++++-- .../entrypoint_codegen/validate/validate.cpp | 108 --------------- .../entrypoint_codegen/validate/validate.h | 10 -- 20 files changed, 437 insertions(+), 504 deletions(-) rename pp/tools/entrypoint_codegen/{validate/validate_tests.cpp => contract/entrypoint_contract_validation_tests.cpp} (93%) delete mode 100644 pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.cpp delete mode 100644 pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.h delete mode 100644 pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog_tests.cpp delete mode 100644 pp/tools/entrypoint_codegen/emit/diagnostics.cpp delete mode 100644 pp/tools/entrypoint_codegen/emit/diagnostics.h delete mode 100644 pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp delete mode 100644 pp/tools/entrypoint_codegen/emit/json.h rename pp/tools/entrypoint_codegen/emit/{json.cpp => report.cpp} (84%) create mode 100644 pp/tools/entrypoint_codegen/emit/report.h rename pp/tools/entrypoint_codegen/emit/{json_tests.cpp => report_tests.cpp} (66%) delete mode 100644 pp/tools/entrypoint_codegen/validate/validate.cpp delete mode 100644 pp/tools/entrypoint_codegen/validate/validate.h diff --git a/pp/tools/entrypoint_codegen/BUILD.bazel b/pp/tools/entrypoint_codegen/BUILD.bazel index 4a710bee3f..f3c44f5c2b 100644 --- a/pp/tools/entrypoint_codegen/BUILD.bazel +++ b/pp/tools/entrypoint_codegen/BUILD.bazel @@ -10,7 +10,6 @@ cc_library( "diagnostics/*.cpp", "emit/*.cpp", "facts/*.cpp", - "validate/*.cpp", ], exclude = [ "app/main.cpp", @@ -24,7 +23,6 @@ cc_library( "diagnostics/*.h", "emit/*.h", "facts/*.h", - "validate/*.h", ]), copts = [ "-std=c++2b", diff --git a/pp/tools/entrypoint_codegen/app/run.cpp b/pp/tools/entrypoint_codegen/app/run.cpp index 67dea4078e..d47db6a98b 100644 --- a/pp/tools/entrypoint_codegen/app/run.cpp +++ b/pp/tools/entrypoint_codegen/app/run.cpp @@ -3,10 +3,9 @@ #include "app/memory_tracking.h" #include "app/runtime_debug.h" #include "clang_adapter/parse.h" +#include "contract/entrypoint_contract.h" #include "diagnostics/diagnostics.h" -#include "emit/diagnostics.h" -#include "emit/json.h" -#include "validate/validate.h" +#include "emit/report.h" #include #include @@ -26,12 +25,12 @@ void write_json_output(const OutputOptions& options, const facts::EntrypointFact if (!output) { throw std::runtime_error("failed to open output file: " + options.output_path.string()); } - emit::write_json(output, facts, diagnostic_set); + emit::write_report(output, emit::ReportFormat::kJson, facts, diagnostic_set); } void write_lint_output(const OutputOptions& options, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { std::ostream& output = options.diagnostics_output == nullptr ? std::cout : *options.diagnostics_output; - emit::write_diagnostics(output, facts, diagnostic_set); + emit::write_report(output, emit::ReportFormat::kCompilerDiagnostics, facts, diagnostic_set); } } // namespace @@ -47,7 +46,7 @@ RunReport run(const RunOptions& options) { }, diagnostic_set); - validate::validate_entrypoints(facts, diagnostic_set); + contract::validate_entrypoints(facts, diagnostic_set); if (options.runtime.debug_diagnostics) { append_runtime_debug_diagnostics(diagnostic_set, memory_resource.snapshot()); diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp index 9a9fc27fee..149ca32a77 100644 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp @@ -1,7 +1,89 @@ #include "contract/entrypoint_contract.h" +#include "diagnostics/diagnostics.h" +#include "facts/entrypoint_facts.h" + +#include +#include + namespace entrypoint_codegen::contract { +namespace { + +void add_diagnostic(diagnostics::DiagnosticSet& diagnostic_set, + diagnostics::DiagnosticCode code, + facts::FunctionId function_id, + facts::SourceLocation location) { + diagnostic_set.add(diagnostics::Diagnostic{ + .code = code, + .message = std::nullopt, + .severity = diagnostics::Severity::kError, + .function = function_id, + .location = location, + }); +} + +bool has_layout(const facts::EntrypointFacts& facts, const facts::FunctionDecl& function, facts::LayoutKind kind) { + for (const facts::LayoutDecl& layout : facts.layouts(function.layouts)) { + if (layout.kind == kind) { + return true; + } + } + return false; +} + +void validate_fastcgo_function(const facts::EntrypointFacts& facts, + diagnostics::DiagnosticSet& diagnostic_set, + facts::FunctionId function_id, + const facts::FunctionDecl& function) { + if (facts.string(function.return_type_spelling) != "void") { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedReturnType, function_id, function.location); + } + + const auto params = facts.params(function.params); + if (params.size() > 2) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedParamCount, function_id, function.location); + } + + bool uses_args = false; + bool uses_res = false; + for (size_t i = 0; i < params.size(); ++i) { + const facts::ParamDecl& param = params[i]; + if (!is_void_pointer_type(facts.string(param.type_spelling))) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedParamType, function_id, param.location); + } + if (param.role == facts::ParamRole::kOther) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnknownParamRole, function_id, param.location); + continue; + } + if (i == 0 && param.role == facts::ParamRole::kRes && params.size() == 2) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kInvalidTwoParamOrder, function_id, param.location); + } + if (i == 1 && param.role != facts::ParamRole::kRes) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kInvalidSecondParamRole, function_id, param.location); + } + uses_args |= param.role == facts::ParamRole::kArgs; + uses_res |= param.role == facts::ParamRole::kRes; + } + + const bool has_arguments = has_layout(facts, function, facts::LayoutKind::kArguments); + const bool has_result = has_layout(facts, function, facts::LayoutKind::kResult); + if (uses_args && !has_arguments) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingArgumentsLayout, function_id, function.location); + } + if (uses_res && !has_result) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingResultLayout, function_id, function.location); + } + if (!uses_args && has_arguments) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnexpectedArgumentsLayout, function_id, function.location); + } + if (!uses_res && has_result) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnexpectedResultLayout, function_id, function.location); + } +} + +} // namespace + bool starts_with(std::string_view value, std::string_view prefix) { return value.substr(0, prefix.size()) == prefix; } @@ -44,4 +126,26 @@ std::optional layout_kind_for_name(std::string_view name) { return std::nullopt; } +void validate_entrypoints(const facts::EntrypointFacts& facts, diagnostics::DiagnosticSet& diagnostic_set) { + const auto functions = facts.functions(); + for (size_t i = 0; i < functions.size(); ++i) { + const auto function_id = facts::FunctionId(static_cast(i)); + const facts::FunctionDecl& function = functions[i]; + const std::string_view name = facts.string(function.name); + if (!is_entrypoint_function_name(name)) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingNamePrefix, function_id, function.location); + } + if (!function.has_c_linkage) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingCLinkage, function_id, function.location); + } + if (function.bridge_kind == facts::BridgeKind::kUnknown) { + add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingEntrypointAttribute, function_id, function.location); + continue; + } + if (function.bridge_kind == facts::BridgeKind::kFastCGo) { + validate_fastcgo_function(facts, diagnostic_set, function_id, function); + } + } +} + } // namespace entrypoint_codegen::contract diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h index e63101db0f..fe4a4451ec 100644 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h @@ -5,6 +5,14 @@ #include #include +namespace entrypoint_codegen::diagnostics { +class DiagnosticSet; +} + +namespace entrypoint_codegen::facts { +class EntrypointFacts; +} + namespace entrypoint_codegen::contract { constexpr std::string_view kFunctionNamePrefix = "prompp_"; @@ -23,4 +31,6 @@ constexpr std::string_view kResParamName = "res"; [[nodiscard]] facts::ParamRole param_role_for_name(std::string_view name); [[nodiscard]] std::optional layout_kind_for_name(std::string_view name); +void validate_entrypoints(const facts::EntrypointFacts& facts, diagnostics::DiagnosticSet& diagnostic_set); + } // namespace entrypoint_codegen::contract diff --git a/pp/tools/entrypoint_codegen/validate/validate_tests.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_validation_tests.cpp similarity index 93% rename from pp/tools/entrypoint_codegen/validate/validate_tests.cpp rename to pp/tools/entrypoint_codegen/contract/entrypoint_contract_validation_tests.cpp index b65c33ec4f..4c1b32ab84 100644 --- a/pp/tools/entrypoint_codegen/validate/validate_tests.cpp +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_validation_tests.cpp @@ -1,8 +1,9 @@ -#include "validate/validate.h" +#include "contract/entrypoint_contract.h" #include #include "diagnostics/diagnostics.h" +#include "facts/entrypoint_facts.h" #include #include @@ -42,7 +43,7 @@ TEST_F(ValidateEntrypointsTest, AcceptsValidCgoFunction) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -72,7 +73,7 @@ TEST_F(ValidateEntrypointsTest, AcceptsValidFastCgoFunction) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -94,7 +95,7 @@ TEST_F(ValidateEntrypointsTest, ReportsMissingEntrypointPrefix) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -121,7 +122,7 @@ TEST_F(ValidateEntrypointsTest, ReportsMissingCLinkage) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -144,7 +145,7 @@ TEST_F(ValidateEntrypointsTest, ReportsMissingEntrypointAttribute) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -167,7 +168,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoReturnType) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -199,7 +200,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoParamCount) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -228,7 +229,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoParamType) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -254,7 +255,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnknownFastCgoParamRole) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -284,7 +285,7 @@ TEST_F(ValidateEntrypointsTest, ReportsInvalidTwoParamOrder) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -314,7 +315,7 @@ TEST_F(ValidateEntrypointsTest, ReportsInvalidSecondParamRole) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -340,7 +341,7 @@ TEST_F(ValidateEntrypointsTest, ReportsMissingArgumentsLayout) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -366,7 +367,7 @@ TEST_F(ValidateEntrypointsTest, ReportsMissingResultLayout) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -392,7 +393,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnexpectedArgumentsLayout) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -418,7 +419,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnexpectedResultLayout) { }); // Act - entrypoint_codegen::validate::validate_entrypoints(facts_, diagnostics_); + entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.cpp deleted file mode 100644 index c372219db1..0000000000 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include "diagnostics/diagnostic_catalog.h" - -namespace entrypoint_codegen::diagnostics { - -std::string_view diagnostic_code_name(DiagnosticCode code) { - switch (code) { - case DiagnosticCode::kClangDiagnostic: { - return "clang_diagnostic"; - } - case DiagnosticCode::kUnsupportedReturnType: { - return "unsupported_return_type"; - } - case DiagnosticCode::kUnsupportedParamCount: { - return "unsupported_param_count"; - } - case DiagnosticCode::kUnsupportedParamType: { - return "unsupported_param_type"; - } - case DiagnosticCode::kUnknownParamRole: { - return "unknown_param_role"; - } - case DiagnosticCode::kInvalidTwoParamOrder: { - return "invalid_two_param_order"; - } - case DiagnosticCode::kInvalidSecondParamRole: { - return "invalid_second_param_role"; - } - case DiagnosticCode::kMissingArgumentsLayout: { - return "missing_arguments_layout"; - } - case DiagnosticCode::kMissingResultLayout: { - return "missing_result_layout"; - } - case DiagnosticCode::kUnexpectedArgumentsLayout: { - return "unexpected_arguments_layout"; - } - case DiagnosticCode::kUnexpectedResultLayout: { - return "unexpected_result_layout"; - } - case DiagnosticCode::kMissingNamePrefix: { - return "missing_name_prefix"; - } - case DiagnosticCode::kMissingCLinkage: { - return "missing_c_linkage"; - } - case DiagnosticCode::kMissingEntrypointAttribute: { - return "missing_entrypoint_attribute"; - } - case DiagnosticCode::kRuntimeMemoryUsage: { - return "runtime_memory_usage"; - } - } - return "unknown_diagnostic"; -} - -std::string_view diagnostic_default_message(DiagnosticCode code) { - switch (code) { - case DiagnosticCode::kClangDiagnostic: { - return "Clang diagnostic"; - } - case DiagnosticCode::kUnsupportedReturnType: { - return "FastCGo entrypoint must return void"; - } - case DiagnosticCode::kUnsupportedParamCount: { - return "FastCGo entrypoint supports 0, 1, or 2 parameters"; - } - case DiagnosticCode::kUnsupportedParamType: { - return "FastCGo entrypoint parameters must be void*"; - } - case DiagnosticCode::kUnknownParamRole: { - return "FastCGo parameter must be named args or res"; - } - case DiagnosticCode::kInvalidTwoParamOrder: { - return "FastCGo two-parameter form must be args, res"; - } - case DiagnosticCode::kInvalidSecondParamRole: { - return "FastCGo second parameter must be res"; - } - case DiagnosticCode::kMissingArgumentsLayout: { - return "args parameter requires local Arguments layout"; - } - case DiagnosticCode::kMissingResultLayout: { - return "res parameter requires local Result layout"; - } - case DiagnosticCode::kUnexpectedArgumentsLayout: { - return "Arguments layout exists but args parameter is absent"; - } - case DiagnosticCode::kUnexpectedResultLayout: { - return "Result layout exists but res parameter is absent"; - } - case DiagnosticCode::kMissingNamePrefix: { - return "entrypoint function must use prompp_ prefix"; - } - case DiagnosticCode::kMissingCLinkage: { - return "entrypoint function must be declared extern \"C\""; - } - case DiagnosticCode::kMissingEntrypointAttribute: { - return "entrypoint function requires CGo or FastCGo annotation"; - } - case DiagnosticCode::kRuntimeMemoryUsage: { - return "runtime memory usage"; - } - } - return "unknown diagnostic"; -} - -std::string_view diagnostic_message(const Diagnostic& diagnostic) { - if (diagnostic.message.has_value()) { - return *diagnostic.message; - } - return diagnostic_default_message(diagnostic.code); -} - -std::string_view severity_name(Severity severity) { - switch (severity) { - case Severity::kInfo: { - return "info"; - } - case Severity::kWarning: { - return "warning"; - } - case Severity::kError: { - return "error"; - } - } - return "error"; -} - -} // namespace entrypoint_codegen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.h b/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.h deleted file mode 100644 index 0b913c4d26..0000000000 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "diagnostics/diagnostics.h" - -#include - -namespace entrypoint_codegen::diagnostics { - -std::string_view diagnostic_code_name(DiagnosticCode code); -std::string_view diagnostic_default_message(DiagnosticCode code); -std::string_view diagnostic_message(const Diagnostic& diagnostic); -std::string_view severity_name(Severity severity); - -} // namespace entrypoint_codegen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog_tests.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog_tests.cpp deleted file mode 100644 index 7a86e4e775..0000000000 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostic_catalog_tests.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "diagnostics/diagnostic_catalog.h" - -#include - -#include "facts/facts.h" - -#include -#include - -namespace { - -using entrypoint_codegen::diagnostics::Diagnostic; -using entrypoint_codegen::diagnostics::DiagnosticCode; -using entrypoint_codegen::diagnostics::Severity; -using entrypoint_codegen::facts::SourceFileId; -using entrypoint_codegen::facts::SourceLocation; - -TEST(DiagnosticCatalogTest, NamesDiagnosticCode) { - // Act - const std::string_view code = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kMissingNamePrefix); - - // Assert - EXPECT_EQ(code, "missing_name_prefix"); -} - -TEST(DiagnosticCatalogTest, NamesParameterRoleDiagnosticsWithDistinctCodes) { - // Act - const std::string_view unknown_role = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kUnknownParamRole); - const std::string_view invalid_order = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidTwoParamOrder); - const std::string_view invalid_second = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidSecondParamRole); - - // Assert - EXPECT_EQ(unknown_role, "unknown_param_role"); - EXPECT_EQ(invalid_order, "invalid_two_param_order"); - EXPECT_EQ(invalid_second, "invalid_second_param_role"); -} - -TEST(DiagnosticCatalogTest, ProvidesDefaultMessageForDiagnosticCode) { - // Act - const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_default_message(DiagnosticCode::kMissingNamePrefix); - - // Assert - EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); -} - -TEST(DiagnosticCatalogTest, UsesDiagnosticMessageWhenPresent) { - // Arrange - const SourceLocation location{.file = SourceFileId(0), .line = 1, .column = 1}; - const Diagnostic diagnostic{ - .code = DiagnosticCode::kClangDiagnostic, - .message = "clang says no", - .severity = Severity::kError, - .function = std::nullopt, - .location = location, - }; - - // Act - const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_message(diagnostic); - - // Assert - EXPECT_EQ(message, "clang says no"); -} - -TEST(DiagnosticCatalogTest, FallsBackToDefaultMessageWhenDiagnosticMessageIsAbsent) { - // Arrange - const SourceLocation location{.file = SourceFileId(0), .line = 1, .column = 1}; - const Diagnostic diagnostic{ - .code = DiagnosticCode::kMissingNamePrefix, - .message = std::nullopt, - .severity = Severity::kError, - .function = std::nullopt, - .location = location, - }; - - // Act - const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_message(diagnostic); - - // Assert - EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); -} - -TEST(DiagnosticCatalogTest, NamesSeverityValues) { - // Act - const std::string_view info = entrypoint_codegen::diagnostics::severity_name(Severity::kInfo); - const std::string_view warning = entrypoint_codegen::diagnostics::severity_name(Severity::kWarning); - const std::string_view error = entrypoint_codegen::diagnostics::severity_name(Severity::kError); - - // Assert - EXPECT_EQ(info, "info"); - EXPECT_EQ(warning, "warning"); - EXPECT_EQ(error, "error"); -} - -} // namespace diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp index 88ba71074d..21faa39764 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp @@ -48,4 +48,128 @@ SeverityCounts count_by_severity(const DiagnosticSet& diagnostic_set) { return counts; } +std::string_view diagnostic_code_name(DiagnosticCode code) { + switch (code) { + case DiagnosticCode::kClangDiagnostic: { + return "clang_diagnostic"; + } + case DiagnosticCode::kUnsupportedReturnType: { + return "unsupported_return_type"; + } + case DiagnosticCode::kUnsupportedParamCount: { + return "unsupported_param_count"; + } + case DiagnosticCode::kUnsupportedParamType: { + return "unsupported_param_type"; + } + case DiagnosticCode::kUnknownParamRole: { + return "unknown_param_role"; + } + case DiagnosticCode::kInvalidTwoParamOrder: { + return "invalid_two_param_order"; + } + case DiagnosticCode::kInvalidSecondParamRole: { + return "invalid_second_param_role"; + } + case DiagnosticCode::kMissingArgumentsLayout: { + return "missing_arguments_layout"; + } + case DiagnosticCode::kMissingResultLayout: { + return "missing_result_layout"; + } + case DiagnosticCode::kUnexpectedArgumentsLayout: { + return "unexpected_arguments_layout"; + } + case DiagnosticCode::kUnexpectedResultLayout: { + return "unexpected_result_layout"; + } + case DiagnosticCode::kMissingNamePrefix: { + return "missing_name_prefix"; + } + case DiagnosticCode::kMissingCLinkage: { + return "missing_c_linkage"; + } + case DiagnosticCode::kMissingEntrypointAttribute: { + return "missing_entrypoint_attribute"; + } + case DiagnosticCode::kRuntimeMemoryUsage: { + return "runtime_memory_usage"; + } + } + return "unknown_diagnostic"; +} + +std::string_view diagnostic_default_message(DiagnosticCode code) { + switch (code) { + case DiagnosticCode::kClangDiagnostic: { + return "Clang diagnostic"; + } + case DiagnosticCode::kUnsupportedReturnType: { + return "FastCGo entrypoint must return void"; + } + case DiagnosticCode::kUnsupportedParamCount: { + return "FastCGo entrypoint supports 0, 1, or 2 parameters"; + } + case DiagnosticCode::kUnsupportedParamType: { + return "FastCGo entrypoint parameters must be void*"; + } + case DiagnosticCode::kUnknownParamRole: { + return "FastCGo parameter must be named args or res"; + } + case DiagnosticCode::kInvalidTwoParamOrder: { + return "FastCGo two-parameter form must be args, res"; + } + case DiagnosticCode::kInvalidSecondParamRole: { + return "FastCGo second parameter must be res"; + } + case DiagnosticCode::kMissingArgumentsLayout: { + return "args parameter requires local Arguments layout"; + } + case DiagnosticCode::kMissingResultLayout: { + return "res parameter requires local Result layout"; + } + case DiagnosticCode::kUnexpectedArgumentsLayout: { + return "Arguments layout exists but args parameter is absent"; + } + case DiagnosticCode::kUnexpectedResultLayout: { + return "Result layout exists but res parameter is absent"; + } + case DiagnosticCode::kMissingNamePrefix: { + return "entrypoint function must use prompp_ prefix"; + } + case DiagnosticCode::kMissingCLinkage: { + return "entrypoint function must be declared extern \"C\""; + } + case DiagnosticCode::kMissingEntrypointAttribute: { + return "entrypoint function requires CGo or FastCGo annotation"; + } + case DiagnosticCode::kRuntimeMemoryUsage: { + return "runtime memory usage"; + } + } + return "unknown diagnostic"; +} + +std::string_view diagnostic_message(const Diagnostic& diagnostic) { + if (diagnostic.message.has_value()) { + return *diagnostic.message; + } + return diagnostic_default_message(diagnostic.code); +} + +std::string_view severity_name(Severity severity) { + switch (severity) { + case Severity::kInfo: { + return "info"; + } + case Severity::kWarning: { + return "warning"; + } + case Severity::kError: { + return "error"; + } + } + return "error"; +} + } // namespace entrypoint_codegen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h index 6123b79e91..6742a87495 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h @@ -7,6 +7,7 @@ #include #include #include +#include #include namespace entrypoint_codegen::diagnostics { @@ -67,5 +68,9 @@ class DiagnosticSet { }; [[nodiscard]] SeverityCounts count_by_severity(const DiagnosticSet& diagnostic_set); +[[nodiscard]] std::string_view diagnostic_code_name(DiagnosticCode code); +[[nodiscard]] std::string_view diagnostic_default_message(DiagnosticCode code); +[[nodiscard]] std::string_view diagnostic_message(const Diagnostic& diagnostic); +[[nodiscard]] std::string_view severity_name(Severity severity); } // namespace entrypoint_codegen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp index 986d85a1b8..33f0a9ea3f 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp @@ -3,6 +3,7 @@ #include #include +#include namespace { @@ -81,4 +82,80 @@ TEST_F(DiagnosticSetTest, CountsDiagnosticsBySeverity) { EXPECT_TRUE(counts.has_errors()); } +TEST(DiagnosticsTest, NamesDiagnosticCode) { + // Act + const std::string_view code = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kMissingNamePrefix); + + // Assert + EXPECT_EQ(code, "missing_name_prefix"); +} + +TEST(DiagnosticsTest, NamesParameterRoleDiagnosticsWithDistinctCodes) { + // Act + const std::string_view unknown_role = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kUnknownParamRole); + const std::string_view invalid_order = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidTwoParamOrder); + const std::string_view invalid_second = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidSecondParamRole); + + // Assert + EXPECT_EQ(unknown_role, "unknown_param_role"); + EXPECT_EQ(invalid_order, "invalid_two_param_order"); + EXPECT_EQ(invalid_second, "invalid_second_param_role"); +} + +TEST(DiagnosticsTest, ProvidesDefaultMessageForDiagnosticCode) { + // Act + const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_default_message(DiagnosticCode::kMissingNamePrefix); + + // Assert + EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); +} + +TEST(DiagnosticsTest, UsesDiagnosticMessageWhenPresent) { + // Arrange + const SourceLocation location{.file = SourceFileId(0), .line = 1, .column = 1}; + const Diagnostic diagnostic{ + .code = DiagnosticCode::kClangDiagnostic, + .message = "clang says no", + .severity = Severity::kError, + .function = std::nullopt, + .location = location, + }; + + // Act + const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_message(diagnostic); + + // Assert + EXPECT_EQ(message, "clang says no"); +} + +TEST(DiagnosticsTest, FallsBackToDefaultMessageWhenDiagnosticMessageIsAbsent) { + // Arrange + const SourceLocation location{.file = SourceFileId(0), .line = 1, .column = 1}; + const Diagnostic diagnostic{ + .code = DiagnosticCode::kMissingNamePrefix, + .message = std::nullopt, + .severity = Severity::kError, + .function = std::nullopt, + .location = location, + }; + + // Act + const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_message(diagnostic); + + // Assert + EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); +} + +TEST(DiagnosticsTest, NamesSeverityValues) { + // Act + const std::string_view info = entrypoint_codegen::diagnostics::severity_name(Severity::kInfo); + const std::string_view warning = entrypoint_codegen::diagnostics::severity_name(Severity::kWarning); + const std::string_view error = entrypoint_codegen::diagnostics::severity_name(Severity::kError); + + // Assert + EXPECT_EQ(info, "info"); + EXPECT_EQ(warning, "warning"); + EXPECT_EQ(error, "error"); +} + } // namespace diff --git a/pp/tools/entrypoint_codegen/emit/diagnostics.cpp b/pp/tools/entrypoint_codegen/emit/diagnostics.cpp deleted file mode 100644 index c4ec1512ff..0000000000 --- a/pp/tools/entrypoint_codegen/emit/diagnostics.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "emit/diagnostics.h" - -#include "diagnostics/diagnostic_catalog.h" - -#include - -namespace entrypoint_codegen::emit { - -void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { - for (const diagnostics::Diagnostic& diagnostic : diagnostic_set.diagnostics()) { - if (diagnostic.location.has_value()) { - const facts::SourceLocation location = *diagnostic.location; - out << facts.string(facts.source_file(location.file).path) << ":" << location.line << ":" << location.column << ": "; - } - out << diagnostics::severity_name(diagnostic.severity) << ": " << diagnostics::diagnostic_message(diagnostic) << " [" - << diagnostics::diagnostic_code_name(diagnostic.code) << "]\n"; - } -} - -} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/diagnostics.h b/pp/tools/entrypoint_codegen/emit/diagnostics.h deleted file mode 100644 index fdfd9129be..0000000000 --- a/pp/tools/entrypoint_codegen/emit/diagnostics.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "diagnostics/diagnostics.h" -#include "facts/entrypoint_facts.h" - -#include - -namespace entrypoint_codegen::emit { - -void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set); - -} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp b/pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp deleted file mode 100644 index db690d1b51..0000000000 --- a/pp/tools/entrypoint_codegen/emit/diagnostics_tests.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "emit/diagnostics.h" - -#include - -#include "diagnostics/diagnostics.h" - -#include -#include - -namespace { - -using entrypoint_codegen::diagnostics::Diagnostic; -using entrypoint_codegen::diagnostics::DiagnosticCode; -using entrypoint_codegen::diagnostics::DiagnosticSet; -using entrypoint_codegen::diagnostics::Severity; -using entrypoint_codegen::facts::EntrypointFacts; -using entrypoint_codegen::facts::SourceLocation; - -class EmitDiagnosticsTest : public testing::Test { - protected: - EntrypointFacts facts_; - DiagnosticSet diagnostics_; - std::ostringstream output_; -}; - -TEST_F(EmitDiagnosticsTest, WritesCompilerStyleDiagnosticLine) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 9}; - diagnostics_.add(Diagnostic{ - .code = DiagnosticCode::kMissingNamePrefix, - .message = std::nullopt, - .severity = Severity::kError, - .function = std::nullopt, - .location = location, - }); - - // Act - entrypoint_codegen::emit::write_diagnostics(output_, facts_, diagnostics_); - - // Assert - EXPECT_EQ(output_.str(), "entrypoint.cpp:7:9: error: entrypoint function must use prompp_ prefix [missing_name_prefix]\n"); -} - -TEST_F(EmitDiagnosticsTest, WritesLocationlessDiagnosticLine) { - // Arrange - diagnostics_.add(Diagnostic{ - .code = DiagnosticCode::kRuntimeMemoryUsage, - .message = std::nullopt, - .severity = Severity::kInfo, - .function = std::nullopt, - .location = std::nullopt, - }); - - // Act - entrypoint_codegen::emit::write_diagnostics(output_, facts_, diagnostics_); - - // Assert - EXPECT_EQ(output_.str(), "info: runtime memory usage [runtime_memory_usage]\n"); -} - -} // namespace diff --git a/pp/tools/entrypoint_codegen/emit/json.h b/pp/tools/entrypoint_codegen/emit/json.h deleted file mode 100644 index e173caf1c2..0000000000 --- a/pp/tools/entrypoint_codegen/emit/json.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "diagnostics/diagnostics.h" -#include "facts/entrypoint_facts.h" - -#include - -namespace entrypoint_codegen::emit { - -void write_json(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set); - -} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/json.cpp b/pp/tools/entrypoint_codegen/emit/report.cpp similarity index 84% rename from pp/tools/entrypoint_codegen/emit/json.cpp rename to pp/tools/entrypoint_codegen/emit/report.cpp index 72c68a402f..f541e0b75c 100644 --- a/pp/tools/entrypoint_codegen/emit/json.cpp +++ b/pp/tools/entrypoint_codegen/emit/report.cpp @@ -1,6 +1,4 @@ -#include "emit/json.h" - -#include "diagnostics/diagnostic_catalog.h" +#include "emit/report.h" #include #include @@ -221,7 +219,7 @@ void write_diagnostic(std::ostream& out, const facts::EntrypointFacts& facts, co out << "}"; } -void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { +void write_json_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { out << " \"diagnostics\": [\n"; const auto diagnostic_values = diagnostic_set.diagnostics(); for (size_t i = 0; i < diagnostic_values.size(); ++i) { @@ -234,9 +232,7 @@ void write_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts, c out << " ]"; } -} // namespace - -void write_json(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { +void write_json_report(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { if (!out) { throw std::runtime_error("output stream is not writable"); } @@ -246,9 +242,35 @@ void write_json(std::ostream& out, const facts::EntrypointFacts& facts, const di out << ",\n"; write_functions(out, facts); out << ",\n"; - write_diagnostics(out, facts, diagnostic_set); + write_json_diagnostics(out, facts, diagnostic_set); out << "\n"; out << "}\n"; } +void write_compiler_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { + for (const diagnostics::Diagnostic& diagnostic : diagnostic_set.diagnostics()) { + if (diagnostic.location.has_value()) { + const facts::SourceLocation location = *diagnostic.location; + out << facts.string(facts.source_file(location.file).path) << ":" << location.line << ":" << location.column << ": "; + } + out << diagnostics::severity_name(diagnostic.severity) << ": " << diagnostics::diagnostic_message(diagnostic) << " [" + << diagnostics::diagnostic_code_name(diagnostic.code) << "]\n"; + } +} + +} // namespace + +void write_report(std::ostream& out, ReportFormat format, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { + switch (format) { + case ReportFormat::kJson: { + write_json_report(out, facts, diagnostic_set); + break; + } + case ReportFormat::kCompilerDiagnostics: { + write_compiler_diagnostics(out, facts, diagnostic_set); + break; + } + } +} + } // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/report.h b/pp/tools/entrypoint_codegen/emit/report.h new file mode 100644 index 0000000000..4cbf1a78ad --- /dev/null +++ b/pp/tools/entrypoint_codegen/emit/report.h @@ -0,0 +1,18 @@ +#pragma once + +#include "diagnostics/diagnostics.h" +#include "facts/entrypoint_facts.h" + +#include +#include + +namespace entrypoint_codegen::emit { + +enum class ReportFormat : uint8_t { + kJson, + kCompilerDiagnostics, +}; + +void write_report(std::ostream& out, ReportFormat format, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set); + +} // namespace entrypoint_codegen::emit diff --git a/pp/tools/entrypoint_codegen/emit/json_tests.cpp b/pp/tools/entrypoint_codegen/emit/report_tests.cpp similarity index 66% rename from pp/tools/entrypoint_codegen/emit/json_tests.cpp rename to pp/tools/entrypoint_codegen/emit/report_tests.cpp index b99124249e..67093004ef 100644 --- a/pp/tools/entrypoint_codegen/emit/json_tests.cpp +++ b/pp/tools/entrypoint_codegen/emit/report_tests.cpp @@ -1,4 +1,4 @@ -#include "emit/json.h" +#include "emit/report.h" #include @@ -50,14 +50,14 @@ bool has_unescaped_control_character_in_string(std::string_view json) { return false; } -class EmitJsonTest : public testing::Test { +class EmitReportTest : public testing::Test { protected: EntrypointFacts facts_; DiagnosticSet diagnostics_; std::ostringstream output_; }; -TEST_F(EmitJsonTest, WritesFunctionFacts) { +TEST_F(EmitReportTest, WritesJsonFunctionFacts) { // Arrange const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 2, .column = 4}; facts_.add_function(FunctionDecl{ @@ -72,7 +72,7 @@ TEST_F(EmitJsonTest, WritesFunctionFacts) { }); // Act - entrypoint_codegen::emit::write_json(output_, facts_, diagnostics_); + entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kJson, facts_, diagnostics_); const std::string json = output_.str(); const bool has_function_name = json.find("\"name\": \"prompp_store\"") != std::string::npos; const bool has_bridge_kind = json.find("\"bridge_kind\": \"cgo\"") != std::string::npos; @@ -84,7 +84,7 @@ TEST_F(EmitJsonTest, WritesFunctionFacts) { EXPECT_TRUE(has_c_linkage); } -TEST_F(EmitJsonTest, EscapesStringFieldsInOutput) { +TEST_F(EmitReportTest, EscapesJsonStringFieldsInOutput) { // Arrange const SourceLocation location{.file = facts_.add_source_file("entry\"point.cpp"), .line = 2, .column = 4}; diagnostics_.add(Diagnostic{ @@ -96,7 +96,7 @@ TEST_F(EmitJsonTest, EscapesStringFieldsInOutput) { }); // Act - entrypoint_codegen::emit::write_json(output_, facts_, diagnostics_); + entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kJson, facts_, diagnostics_); const std::string json = output_.str(); const bool has_escaped_path = json.find("\"path\": \"entry\\\"point.cpp\"") != std::string::npos; const bool has_escaped_message = json.find("\"message\": \"line\\nmessage\"") != std::string::npos; @@ -106,7 +106,7 @@ TEST_F(EmitJsonTest, EscapesStringFieldsInOutput) { EXPECT_TRUE(has_escaped_message); } -TEST_F(EmitJsonTest, EscapesNonWhitespaceControlCharactersInStringFields) { +TEST_F(EmitReportTest, EscapesNonWhitespaceControlCharactersInJsonStringFields) { // Arrange std::string message = "before"; message.push_back('\x01'); @@ -122,7 +122,7 @@ TEST_F(EmitJsonTest, EscapesNonWhitespaceControlCharactersInStringFields) { }); // Act - entrypoint_codegen::emit::write_json(output_, facts_, diagnostics_); + entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kJson, facts_, diagnostics_); const std::string json = output_.str(); // Assert @@ -132,7 +132,7 @@ TEST_F(EmitJsonTest, EscapesNonWhitespaceControlCharactersInStringFields) { EXPECT_NE(json.find("\\u000c"), std::string::npos); } -TEST_F(EmitJsonTest, WritesDiagnosticLocationObjectWhenPresent) { +TEST_F(EmitReportTest, WritesJsonDiagnosticLocationObjectWhenPresent) { // Arrange const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 9}; diagnostics_.add(Diagnostic{ @@ -144,7 +144,7 @@ TEST_F(EmitJsonTest, WritesDiagnosticLocationObjectWhenPresent) { }); // Act - entrypoint_codegen::emit::write_json(output_, facts_, diagnostics_); + entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kJson, facts_, diagnostics_); const std::string json = output_.str(); const bool has_location = json.find("\"location\": {\"file\": \"entrypoint.cpp\", \"line\": 7, \"column\": 9}") != std::string::npos; @@ -152,7 +152,7 @@ TEST_F(EmitJsonTest, WritesDiagnosticLocationObjectWhenPresent) { EXPECT_TRUE(has_location); } -TEST_F(EmitJsonTest, WritesNullDiagnosticLocationWhenAbsent) { +TEST_F(EmitReportTest, WritesNullJsonDiagnosticLocationWhenAbsent) { // Arrange diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kRuntimeMemoryUsage, @@ -163,7 +163,7 @@ TEST_F(EmitJsonTest, WritesNullDiagnosticLocationWhenAbsent) { }); // Act - entrypoint_codegen::emit::write_json(output_, facts_, diagnostics_); + entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kJson, facts_, diagnostics_); const std::string json = output_.str(); const bool has_null_location = json.find("\"location\": null") != std::string::npos; @@ -171,4 +171,39 @@ TEST_F(EmitJsonTest, WritesNullDiagnosticLocationWhenAbsent) { EXPECT_TRUE(has_null_location); } +TEST_F(EmitReportTest, WritesCompilerStyleDiagnosticLine) { + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 9}; + diagnostics_.add(Diagnostic{ + .code = DiagnosticCode::kMissingNamePrefix, + .message = std::nullopt, + .severity = Severity::kError, + .function = std::nullopt, + .location = location, + }); + + // Act + entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kCompilerDiagnostics, facts_, diagnostics_); + + // Assert + EXPECT_EQ(output_.str(), "entrypoint.cpp:7:9: error: entrypoint function must use prompp_ prefix [missing_name_prefix]\n"); +} + +TEST_F(EmitReportTest, WritesLocationlessDiagnosticLine) { + // Arrange + diagnostics_.add(Diagnostic{ + .code = DiagnosticCode::kRuntimeMemoryUsage, + .message = std::nullopt, + .severity = Severity::kInfo, + .function = std::nullopt, + .location = std::nullopt, + }); + + // Act + entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kCompilerDiagnostics, facts_, diagnostics_); + + // Assert + EXPECT_EQ(output_.str(), "info: runtime memory usage [runtime_memory_usage]\n"); +} + } // namespace diff --git a/pp/tools/entrypoint_codegen/validate/validate.cpp b/pp/tools/entrypoint_codegen/validate/validate.cpp deleted file mode 100644 index 7bb38daa69..0000000000 --- a/pp/tools/entrypoint_codegen/validate/validate.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "validate/validate.h" - -#include "contract/entrypoint_contract.h" - -#include -#include - -namespace entrypoint_codegen::validate { - -namespace { - -void add_diagnostic(diagnostics::DiagnosticSet& diagnostic_set, - diagnostics::DiagnosticCode code, - facts::FunctionId function_id, - facts::SourceLocation location) { - diagnostic_set.add(diagnostics::Diagnostic{ - .code = code, - .message = std::nullopt, - .severity = diagnostics::Severity::kError, - .function = function_id, - .location = location, - }); -} - -bool has_layout(const facts::EntrypointFacts& facts, const facts::FunctionDecl& function, facts::LayoutKind kind) { - for (const facts::LayoutDecl& layout : facts.layouts(function.layouts)) { - if (layout.kind == kind) { - return true; - } - } - return false; -} - -void validate_fastcgo_function(const facts::EntrypointFacts& facts, - diagnostics::DiagnosticSet& diagnostic_set, - facts::FunctionId function_id, - const facts::FunctionDecl& function) { - if (facts.string(function.return_type_spelling) != "void") { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedReturnType, function_id, function.location); - } - - const auto params = facts.params(function.params); - if (params.size() > 2) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedParamCount, function_id, function.location); - } - - bool uses_args = false; - bool uses_res = false; - for (size_t i = 0; i < params.size(); ++i) { - const facts::ParamDecl& param = params[i]; - if (!contract::is_void_pointer_type(facts.string(param.type_spelling))) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedParamType, function_id, param.location); - } - if (param.role == facts::ParamRole::kOther) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnknownParamRole, function_id, param.location); - continue; - } - if (i == 0 && param.role == facts::ParamRole::kRes && params.size() == 2) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kInvalidTwoParamOrder, function_id, param.location); - } - if (i == 1 && param.role != facts::ParamRole::kRes) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kInvalidSecondParamRole, function_id, param.location); - } - uses_args |= param.role == facts::ParamRole::kArgs; - uses_res |= param.role == facts::ParamRole::kRes; - } - - const bool has_arguments = has_layout(facts, function, facts::LayoutKind::kArguments); - const bool has_result = has_layout(facts, function, facts::LayoutKind::kResult); - if (uses_args && !has_arguments) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingArgumentsLayout, function_id, function.location); - } - if (uses_res && !has_result) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingResultLayout, function_id, function.location); - } - if (!uses_args && has_arguments) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnexpectedArgumentsLayout, function_id, function.location); - } - if (!uses_res && has_result) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnexpectedResultLayout, function_id, function.location); - } -} - -} // namespace - -void validate_entrypoints(const facts::EntrypointFacts& facts, diagnostics::DiagnosticSet& diagnostic_set) { - const auto functions = facts.functions(); - for (size_t i = 0; i < functions.size(); ++i) { - const auto function_id = facts::FunctionId(static_cast(i)); - const facts::FunctionDecl& function = functions[i]; - const std::string_view name = facts.string(function.name); - if (!contract::is_entrypoint_function_name(name)) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingNamePrefix, function_id, function.location); - } - if (!function.has_c_linkage) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingCLinkage, function_id, function.location); - } - if (function.bridge_kind == facts::BridgeKind::kUnknown) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingEntrypointAttribute, function_id, function.location); - continue; - } - if (function.bridge_kind == facts::BridgeKind::kFastCGo) { - validate_fastcgo_function(facts, diagnostic_set, function_id, function); - } - } -} - -} // namespace entrypoint_codegen::validate diff --git a/pp/tools/entrypoint_codegen/validate/validate.h b/pp/tools/entrypoint_codegen/validate/validate.h deleted file mode 100644 index 115793e5e7..0000000000 --- a/pp/tools/entrypoint_codegen/validate/validate.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "diagnostics/diagnostics.h" -#include "facts/entrypoint_facts.h" - -namespace entrypoint_codegen::validate { - -void validate_entrypoints(const facts::EntrypointFacts& facts, diagnostics::DiagnosticSet& diagnostic_set); - -} // namespace entrypoint_codegen::validate From 080affc1c4ac7ac9eb307e6bd04a4579d8fccc06 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Wed, 8 Jul 2026 13:20:16 +0000 Subject: [PATCH 19/31] facts review --- pp/tools/entrypoint_codegen/app/argparse.cpp | 4 +- pp/tools/entrypoint_codegen/app/argparse.h | 4 +- .../entrypoint_codegen/app/argparse_tests.cpp | 20 ++-- pp/tools/entrypoint_codegen/app/main.cpp | 2 +- .../app/memory_tracking.cpp | 4 +- .../entrypoint_codegen/app/memory_tracking.h | 4 +- .../app/memory_tracking_tests.cpp | 8 +- .../entrypoint_codegen/app/memory_usage.h | 4 +- pp/tools/entrypoint_codegen/app/options.h | 4 +- pp/tools/entrypoint_codegen/app/run.cpp | 10 +- pp/tools/entrypoint_codegen/app/run.h | 4 +- pp/tools/entrypoint_codegen/app/run_tests.cpp | 24 ++-- .../entrypoint_codegen/app/runtime_debug.cpp | 4 +- .../entrypoint_codegen/app/runtime_debug.h | 4 +- .../app/runtime_debug_tests.cpp | 18 +-- .../clang_adapter/aggregate_source.cpp | 4 +- .../clang_adapter/aggregate_source.h | 4 +- .../clang_adapter/clang_runtime.cpp | 8 +- .../clang_adapter/clang_runtime.h | 19 ++- .../clang_adapter/parse.cpp | 10 +- .../entrypoint_codegen/clang_adapter/parse.h | 8 +- .../clang_adapter/parse_tests.cpp | 79 ++++++------ .../contract/entrypoint_contract.cpp | 12 +- .../contract/entrypoint_contract.h | 12 +- .../contract/entrypoint_contract_tests.cpp | 34 +++--- .../entrypoint_contract_validation_tests.cpp | 56 ++++----- .../diagnostics/diagnostics.cpp | 4 +- .../diagnostics/diagnostics.h | 4 +- .../diagnostics/diagnostics_tests.cpp | 34 +++--- pp/tools/entrypoint_codegen/emit/report.cpp | 32 ++--- pp/tools/entrypoint_codegen/emit/report.h | 8 +- .../entrypoint_codegen/emit/report_tests.cpp | 32 ++--- .../{entrypoint_facts.cpp => fact_arena.cpp} | 112 +++++++++++------- .../{entrypoint_facts.h => fact_arena.h} | 30 ++--- ...t_facts_tests.cpp => fact_arena_tests.cpp} | 52 ++++---- pp/tools/entrypoint_codegen/facts/facts.h | 27 ++--- .../entrypoint_codegen/facts/string_table.cpp | 4 +- .../entrypoint_codegen/facts/string_table.h | 4 +- .../facts/string_table_tests.cpp | 78 ++++++++++++ .../entrypoint_codegen/facts/tagged_index.h | 4 +- 40 files changed, 441 insertions(+), 348 deletions(-) rename pp/tools/entrypoint_codegen/facts/{entrypoint_facts.cpp => fact_arena.cpp} (56%) rename pp/tools/entrypoint_codegen/facts/{entrypoint_facts.h => fact_arena.h} (53%) rename pp/tools/entrypoint_codegen/facts/{entrypoint_facts_tests.cpp => fact_arena_tests.cpp} (69%) create mode 100644 pp/tools/entrypoint_codegen/facts/string_table_tests.cpp diff --git a/pp/tools/entrypoint_codegen/app/argparse.cpp b/pp/tools/entrypoint_codegen/app/argparse.cpp index 142e4487ff..ad8db5e161 100644 --- a/pp/tools/entrypoint_codegen/app/argparse.cpp +++ b/pp/tools/entrypoint_codegen/app/argparse.cpp @@ -8,7 +8,7 @@ #include #include -namespace entrypoint_codegen::app { +namespace epgen::app { namespace { @@ -154,4 +154,4 @@ CliOptions parse_arguments(int argc, char** argv) { return options; } -} // namespace entrypoint_codegen::app +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/argparse.h b/pp/tools/entrypoint_codegen/app/argparse.h index 3f36616ef0..aa510fc294 100644 --- a/pp/tools/entrypoint_codegen/app/argparse.h +++ b/pp/tools/entrypoint_codegen/app/argparse.h @@ -4,7 +4,7 @@ #include -namespace entrypoint_codegen::app { +namespace epgen::app { struct CliOptions { RunOptions run_options; @@ -14,4 +14,4 @@ struct CliOptions { CliOptions parse_arguments(int argc, char** argv); void write_help(std::ostream& out); -} // namespace entrypoint_codegen::app +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/argparse_tests.cpp b/pp/tools/entrypoint_codegen/app/argparse_tests.cpp index e5d9272e5a..07010c96e3 100644 --- a/pp/tools/entrypoint_codegen/app/argparse_tests.cpp +++ b/pp/tools/entrypoint_codegen/app/argparse_tests.cpp @@ -12,7 +12,7 @@ namespace { -entrypoint_codegen::app::CliOptions parse_args(std::initializer_list args) { +epgen::app::CliOptions parse_args(std::initializer_list args) { std::vector argv_storage; argv_storage.reserve(args.size()); for (std::string_view arg : args) { @@ -25,7 +25,7 @@ entrypoint_codegen::app::CliOptions parse_args(std::initializer_list(argv.size()), argv.data()); + return epgen::app::parse_arguments(static_cast(argv.size()), argv.data()); } std::filesystem::path test_tmp_dir() { @@ -43,7 +43,7 @@ std::filesystem::path touch_file(std::string_view name) { TEST(ArgparseTest, ReturnsHelpForHelpFlag) { // Act - const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--help"}); + const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--help"}); // Assert EXPECT_TRUE(options.help); @@ -56,7 +56,7 @@ TEST(ArgparseTest, CollectsExistingCppInputFiles) { touch_file("entrypoint_argparse.txt"); // Act - const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", source_file.string()}); + const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", source_file.string()}); const auto source_files = options.run_options.analysis.source_files; // Assert @@ -73,7 +73,7 @@ TEST(ArgparseTest, CollectsCppInputFilesRecursivelyFromDirectory) { std::ofstream out(source_file, std::ios::trunc); // Act - const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", root.string()}); + const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", root.string()}); const auto source_files = options.run_options.analysis.source_files; // Assert @@ -95,7 +95,7 @@ TEST(ArgparseTest, ParsesOutputDirectoryAsFactsFilePath) { const std::filesystem::path output_dir = test_tmp_dir() / "facts"; // Act - const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--output-dir=" + output_dir.string()}); + const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--output-dir=" + output_dir.string()}); // Assert EXPECT_EQ(options.run_options.output.output_path, output_dir / "entrypoint_facts.json"); @@ -103,15 +103,15 @@ TEST(ArgparseTest, ParsesOutputDirectoryAsFactsFilePath) { TEST(ArgparseTest, ParsesCheckModeAlias) { // Act - const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--no-output"}); + const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--no-output"}); // Assert - EXPECT_EQ(options.run_options.output.output_mode, entrypoint_codegen::app::OutputMode::kCheck); + EXPECT_EQ(options.run_options.output.output_mode, epgen::app::OutputMode::kCheck); } TEST(ArgparseTest, CollectsClangArgsFromFlagAndSeparator) { // Act - const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--clang-arg=-I.", "--", "-std=c++2b"}); + const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--clang-arg=-I.", "--", "-std=c++2b"}); const auto clang_args = options.run_options.analysis.clang_args; // Assert @@ -126,7 +126,7 @@ TEST(ArgparseTest, RejectsUnknownOutputMode) { TEST(ArgparseTest, ParsesRuntimeDebugPolicy) { // Act - const entrypoint_codegen::app::CliOptions options = parse_args({"entrypoint_codegen", "--runtime-debug"}); + const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--runtime-debug"}); // Assert EXPECT_TRUE(options.run_options.runtime.debug_diagnostics); diff --git a/pp/tools/entrypoint_codegen/app/main.cpp b/pp/tools/entrypoint_codegen/app/main.cpp index 577b9aa4b5..c213e81b50 100644 --- a/pp/tools/entrypoint_codegen/app/main.cpp +++ b/pp/tools/entrypoint_codegen/app/main.cpp @@ -5,7 +5,7 @@ #include int main(int argc, char** argv) { - namespace app = entrypoint_codegen::app; + namespace app = epgen::app; app::CliOptions cli_options; try { diff --git a/pp/tools/entrypoint_codegen/app/memory_tracking.cpp b/pp/tools/entrypoint_codegen/app/memory_tracking.cpp index 3c08781b61..cb3f4b6e0e 100644 --- a/pp/tools/entrypoint_codegen/app/memory_tracking.cpp +++ b/pp/tools/entrypoint_codegen/app/memory_tracking.cpp @@ -1,6 +1,6 @@ #include "app/memory_tracking.h" -namespace entrypoint_codegen::app { +namespace epgen::app { TrackingMemoryResource::TrackingMemoryResource(std::pmr::memory_resource* upstream) : upstream_(upstream) {} @@ -32,4 +32,4 @@ bool TrackingMemoryResource::do_is_equal(const std::pmr::memory_resource& other) return this == &other; } -} // namespace entrypoint_codegen::app +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/memory_tracking.h b/pp/tools/entrypoint_codegen/app/memory_tracking.h index a4f4783619..8e1f1edf04 100644 --- a/pp/tools/entrypoint_codegen/app/memory_tracking.h +++ b/pp/tools/entrypoint_codegen/app/memory_tracking.h @@ -5,7 +5,7 @@ #include #include -namespace entrypoint_codegen::app { +namespace epgen::app { class TrackingMemoryResource : public std::pmr::memory_resource { public: @@ -25,4 +25,4 @@ class TrackingMemoryResource : public std::pmr::memory_resource { size_t peak_live_bytes_ = 0; }; -} // namespace entrypoint_codegen::app +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/memory_tracking_tests.cpp b/pp/tools/entrypoint_codegen/app/memory_tracking_tests.cpp index 9f54717795..42a35e4509 100644 --- a/pp/tools/entrypoint_codegen/app/memory_tracking_tests.cpp +++ b/pp/tools/entrypoint_codegen/app/memory_tracking_tests.cpp @@ -8,16 +8,16 @@ namespace { TEST(TrackingMemoryResourceTest, SnapshotReportsAllocatedDeallocatedAndPeakLiveBytes) { // Arrange - entrypoint_codegen::app::TrackingMemoryResource memory_resource; + epgen::app::TrackingMemoryResource memory_resource; // Act void* first = memory_resource.allocate(8, alignof(std::max_align_t)); void* second = memory_resource.allocate(4, alignof(std::max_align_t)); - const entrypoint_codegen::app::MemoryUsageSnapshot after_allocations = memory_resource.snapshot(); + const epgen::app::MemoryUsageSnapshot after_allocations = memory_resource.snapshot(); memory_resource.deallocate(first, 8, alignof(std::max_align_t)); - const entrypoint_codegen::app::MemoryUsageSnapshot after_first_deallocate = memory_resource.snapshot(); + const epgen::app::MemoryUsageSnapshot after_first_deallocate = memory_resource.snapshot(); memory_resource.deallocate(second, 4, alignof(std::max_align_t)); - const entrypoint_codegen::app::MemoryUsageSnapshot after_all_deallocated = memory_resource.snapshot(); + const epgen::app::MemoryUsageSnapshot after_all_deallocated = memory_resource.snapshot(); // Assert EXPECT_EQ(after_allocations.allocated_bytes, 12); diff --git a/pp/tools/entrypoint_codegen/app/memory_usage.h b/pp/tools/entrypoint_codegen/app/memory_usage.h index 6950c8a3ef..5bd794f2b0 100644 --- a/pp/tools/entrypoint_codegen/app/memory_usage.h +++ b/pp/tools/entrypoint_codegen/app/memory_usage.h @@ -2,7 +2,7 @@ #include -namespace entrypoint_codegen::app { +namespace epgen::app { struct MemoryUsageSnapshot { size_t allocated_bytes = 0; @@ -10,4 +10,4 @@ struct MemoryUsageSnapshot { size_t peak_live_bytes = 0; }; -} // namespace entrypoint_codegen::app +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/options.h b/pp/tools/entrypoint_codegen/app/options.h index 8788f0d5c0..86f252ae15 100644 --- a/pp/tools/entrypoint_codegen/app/options.h +++ b/pp/tools/entrypoint_codegen/app/options.h @@ -6,7 +6,7 @@ #include #include -namespace entrypoint_codegen::app { +namespace epgen::app { enum class OutputMode : uint8_t { kJson, @@ -40,4 +40,4 @@ enum class ExitDecision : uint8_t { kAnalysisFailed, }; -} // namespace entrypoint_codegen::app +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/run.cpp b/pp/tools/entrypoint_codegen/app/run.cpp index d47db6a98b..d22ce74ab0 100644 --- a/pp/tools/entrypoint_codegen/app/run.cpp +++ b/pp/tools/entrypoint_codegen/app/run.cpp @@ -12,11 +12,11 @@ #include #include -namespace entrypoint_codegen::app { +namespace epgen::app { namespace { -void write_json_output(const OutputOptions& options, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { +void write_json_output(const OutputOptions& options, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { if (options.output_path.has_parent_path()) { std::filesystem::create_directories(options.output_path.parent_path()); } @@ -28,7 +28,7 @@ void write_json_output(const OutputOptions& options, const facts::EntrypointFact emit::write_report(output, emit::ReportFormat::kJson, facts, diagnostic_set); } -void write_lint_output(const OutputOptions& options, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { +void write_lint_output(const OutputOptions& options, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { std::ostream& output = options.diagnostics_output == nullptr ? std::cout : *options.diagnostics_output; emit::write_report(output, emit::ReportFormat::kCompilerDiagnostics, facts, diagnostic_set); } @@ -38,7 +38,7 @@ void write_lint_output(const OutputOptions& options, const facts::EntrypointFact RunReport run(const RunOptions& options) { TrackingMemoryResource memory_resource; diagnostics::DiagnosticSet diagnostic_set(&memory_resource); - facts::EntrypointFacts facts = clang_adapter::parse_files( + facts::FactArena facts = clang_adapter::parse_files( clang_adapter::ParseOptions{ .source_files = options.analysis.source_files, .clang_args = options.analysis.clang_args, @@ -75,4 +75,4 @@ RunReport run(const RunOptions& options) { }; } -} // namespace entrypoint_codegen::app +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/run.h b/pp/tools/entrypoint_codegen/app/run.h index c31412baeb..0bddaeeb3a 100644 --- a/pp/tools/entrypoint_codegen/app/run.h +++ b/pp/tools/entrypoint_codegen/app/run.h @@ -4,7 +4,7 @@ #include "app/options.h" #include "diagnostics/diagnostics.h" -namespace entrypoint_codegen::app { +namespace epgen::app { struct RunReport { ExitDecision decision; @@ -14,4 +14,4 @@ struct RunReport { RunReport run(const RunOptions& options); -} // namespace entrypoint_codegen::app +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/run_tests.cpp b/pp/tools/entrypoint_codegen/app/run_tests.cpp index 73a218160e..8f86f43784 100644 --- a/pp/tools/entrypoint_codegen/app/run_tests.cpp +++ b/pp/tools/entrypoint_codegen/app/run_tests.cpp @@ -24,19 +24,19 @@ std::filesystem::path write_source_file(std::string_view name, std::string_view return path; } -entrypoint_codegen::app::RunOptions check_options_for(std::filesystem::path source_file) { - return entrypoint_codegen::app::RunOptions{ +epgen::app::RunOptions check_options_for(std::filesystem::path source_file) { + return epgen::app::RunOptions{ .analysis = - entrypoint_codegen::app::AnalysisOptions{ + epgen::app::AnalysisOptions{ .source_files = {std::move(source_file)}, .clang_args = {"-std=c++2b"}, }, .output = - entrypoint_codegen::app::OutputOptions{ + epgen::app::OutputOptions{ .output_path = {}, - .output_mode = entrypoint_codegen::app::OutputMode::kCheck, + .output_mode = epgen::app::OutputMode::kCheck, }, - .runtime = entrypoint_codegen::app::RuntimeOptions{}, + .runtime = epgen::app::RuntimeOptions{}, }; } @@ -45,13 +45,13 @@ TEST(RunTest, CheckModeSucceedsForValidAnnotatedEntrypoint) { const std::filesystem::path source_file = write_source_file("entrypoint_codegen_run_valid.cpp", R"cpp( extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_store() {} )cpp"); - const entrypoint_codegen::app::RunOptions options = check_options_for(source_file); + const epgen::app::RunOptions options = check_options_for(source_file); // Act - const entrypoint_codegen::app::RunReport report = entrypoint_codegen::app::run(options); + const epgen::app::RunReport report = epgen::app::run(options); // Assert - EXPECT_EQ(report.decision, entrypoint_codegen::app::ExitDecision::kSuccess); + EXPECT_EQ(report.decision, epgen::app::ExitDecision::kSuccess); EXPECT_EQ(report.diagnostics.errors, 0); EXPECT_EQ(report.diagnostics.total, 0); } @@ -61,13 +61,13 @@ TEST(RunTest, CheckModeFailsWhenValidationReportsErrors) { const std::filesystem::path source_file = write_source_file("entrypoint_codegen_run_invalid.cpp", R"cpp( extern "C" void prompp_store() {} )cpp"); - const entrypoint_codegen::app::RunOptions options = check_options_for(source_file); + const epgen::app::RunOptions options = check_options_for(source_file); // Act - const entrypoint_codegen::app::RunReport report = entrypoint_codegen::app::run(options); + const epgen::app::RunReport report = epgen::app::run(options); // Assert - EXPECT_EQ(report.decision, entrypoint_codegen::app::ExitDecision::kAnalysisFailed); + EXPECT_EQ(report.decision, epgen::app::ExitDecision::kAnalysisFailed); EXPECT_EQ(report.diagnostics.errors, 1); EXPECT_EQ(report.diagnostics.total, 1); } diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug.cpp index 18a3b94933..752e86fe10 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug.cpp +++ b/pp/tools/entrypoint_codegen/app/runtime_debug.cpp @@ -3,7 +3,7 @@ #include #include -namespace entrypoint_codegen::app { +namespace epgen::app { void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, MemoryUsageSnapshot snapshot) { const std::string message = "App PMR allocations: allocated=" + std::to_string(snapshot.allocated_bytes) + @@ -18,4 +18,4 @@ void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set }); } -} // namespace entrypoint_codegen::app +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.h b/pp/tools/entrypoint_codegen/app/runtime_debug.h index aa1513bd0a..a3d383f375 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug.h +++ b/pp/tools/entrypoint_codegen/app/runtime_debug.h @@ -3,8 +3,8 @@ #include "app/memory_usage.h" #include "diagnostics/diagnostics.h" -namespace entrypoint_codegen::app { +namespace epgen::app { void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, MemoryUsageSnapshot snapshot); -} // namespace entrypoint_codegen::app +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp index 061e6d14b1..279d3df0a9 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp +++ b/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp @@ -8,10 +8,10 @@ namespace { -using entrypoint_codegen::app::MemoryUsageSnapshot; -using entrypoint_codegen::diagnostics::DiagnosticCode; -using entrypoint_codegen::diagnostics::DiagnosticSet; -using entrypoint_codegen::diagnostics::Severity; +using epgen::app::MemoryUsageSnapshot; +using epgen::diagnostics::DiagnosticCode; +using epgen::diagnostics::DiagnosticSet; +using epgen::diagnostics::Severity; class RuntimeDebugTest : public testing::Test { protected: @@ -20,11 +20,11 @@ class RuntimeDebugTest : public testing::Test { TEST_F(RuntimeDebugTest, AppendsMemoryUsageDiagnostic) { // Act - entrypoint_codegen::app::append_runtime_debug_diagnostics(diagnostics_, MemoryUsageSnapshot{ - .allocated_bytes = 11, - .deallocated_bytes = 7, - .peak_live_bytes = 9, - }); + epgen::app::append_runtime_debug_diagnostics(diagnostics_, MemoryUsageSnapshot{ + .allocated_bytes = 11, + .deallocated_bytes = 7, + .peak_live_bytes = 9, + }); const auto diagnostics = diagnostics_.diagnostics(); // Assert diff --git a/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp b/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp index 3e7164a7e5..7201bb5bb2 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp @@ -1,6 +1,6 @@ #include "clang_adapter/aggregate_source.h" -namespace entrypoint_codegen::clang_adapter { +namespace epgen::clang_adapter { AggregateSource build_aggregate_source(std::span source_files, std::pmr::memory_resource* memory_resource) { AggregateSource source{ @@ -16,4 +16,4 @@ AggregateSource build_aggregate_source(std::span so return source; } -} // namespace entrypoint_codegen::clang_adapter +} // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h b/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h index 45b992aafd..82ff29fe9d 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h +++ b/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h @@ -5,7 +5,7 @@ #include #include -namespace entrypoint_codegen::clang_adapter { +namespace epgen::clang_adapter { struct AggregateSource { std::pmr::string path; @@ -14,4 +14,4 @@ struct AggregateSource { [[nodiscard]] AggregateSource build_aggregate_source(std::span source_files, std::pmr::memory_resource* memory_resource); -} // namespace entrypoint_codegen::clang_adapter +} // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp index 12a5ec7f9a..626e45ee7f 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp @@ -8,7 +8,7 @@ #include #include -namespace entrypoint_codegen::clang_adapter { +namespace epgen::clang_adapter { namespace { @@ -440,7 +440,7 @@ class ParseSession::Impl { ParseSession::ParseSession(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set, - facts::EntrypointFacts& facts, + facts::FactArena& facts, const std::filesystem::path& source_file) : memory_resource_(options.memory_resource), diagnostics_(diagnostic_set), facts_(facts) { std::pmr::polymorphic_allocator allocator(memory_resource_); @@ -456,7 +456,7 @@ ParseSession::ParseSession(const ParseOptions& options, ParseSession::ParseSession(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set, - facts::EntrypointFacts& facts, + facts::FactArena& facts, std::span source_files, std::string_view virtual_source_path, std::string_view virtual_source) @@ -497,4 +497,4 @@ bool ParseSession::is_input_source_file(CursorView cursor) { return impl_->is_input_source_file(cursor); } -} // namespace entrypoint_codegen::clang_adapter +} // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h index 3976c7a735..54e2595e63 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h +++ b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h @@ -2,7 +2,7 @@ #include "clang_adapter/parse.h" #include "diagnostics/diagnostics.h" -#include "facts/entrypoint_facts.h" +#include "facts/fact_arena.h" #include "facts/facts.h" #include @@ -15,7 +15,7 @@ #include #include -namespace entrypoint_codegen::clang_adapter { +namespace epgen::clang_adapter { enum class CursorKind : uint8_t { kOther, @@ -75,13 +75,10 @@ void visit_children(CursorView cursor, VisitResult (*visitor)(CursorView cursor, class ParseSession { public: + ParseSession(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set, facts::FactArena& facts, const std::filesystem::path& source_file); ParseSession(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set, - facts::EntrypointFacts& facts, - const std::filesystem::path& source_file); - ParseSession(const ParseOptions& options, - diagnostics::DiagnosticSet& diagnostic_set, - facts::EntrypointFacts& facts, + facts::FactArena& facts, std::span source_files, std::string_view virtual_source_path, std::string_view virtual_source); @@ -91,8 +88,8 @@ class ParseSession { ParseSession& operator=(const ParseSession&) = delete; [[nodiscard]] std::pmr::memory_resource* memory_resource() const { return memory_resource_; } - facts::EntrypointFacts& facts() { return facts_; } - [[nodiscard]] const facts::EntrypointFacts& facts() const { return facts_; } + facts::FactArena& facts() { return facts_; } + [[nodiscard]] const facts::FactArena& facts() const { return facts_; } void add_clang_diagnostics(); @@ -105,8 +102,8 @@ class ParseSession { std::pmr::memory_resource* memory_resource_; diagnostics::DiagnosticSet& diagnostics_; - facts::EntrypointFacts& facts_; + facts::FactArena& facts_; Impl* impl_ = nullptr; }; -} // namespace entrypoint_codegen::clang_adapter +} // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp b/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp index 1d95d6c829..e7d7891c22 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp @@ -10,7 +10,7 @@ #include #include -namespace entrypoint_codegen::clang_adapter { +namespace epgen::clang_adapter { namespace { @@ -19,7 +19,7 @@ struct ParamNameAndRole { facts::ParamRole role; }; -ParamNameAndRole add_param_name_and_role(facts::EntrypointFacts& facts, CursorView cursor) { +ParamNameAndRole add_param_name_and_role(facts::FactArena& facts, CursorView cursor) { const std::string name = cursor.spelling(); return ParamNameAndRole{ .name = facts.add_string(name), @@ -295,12 +295,12 @@ void scan_translation_unit(ParseSession& session) { } // namespace -facts::EntrypointFacts parse_files(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set) { +facts::FactArena parse_files(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set) { if (options.source_files.empty()) { throw std::invalid_argument("no source files provided"); } - facts::EntrypointFacts facts(options.memory_resource); + facts::FactArena facts(options.memory_resource); const AggregateSource aggregate_source = build_aggregate_source(options.source_files, options.memory_resource); ParseSession session(options, diagnostic_set, facts, options.source_files, aggregate_source.path, aggregate_source.contents); session.add_clang_diagnostics(); @@ -309,4 +309,4 @@ facts::EntrypointFacts parse_files(const ParseOptions& options, diagnostics::Dia return facts; } -} // namespace entrypoint_codegen::clang_adapter +} // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse.h b/pp/tools/entrypoint_codegen/clang_adapter/parse.h index f85116247b..a9212fe822 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse.h +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse.h @@ -1,14 +1,14 @@ #pragma once #include "diagnostics/diagnostics.h" -#include "facts/entrypoint_facts.h" +#include "facts/fact_arena.h" #include #include #include #include -namespace entrypoint_codegen::clang_adapter { +namespace epgen::clang_adapter { struct ParseOptions { std::vector source_files; @@ -16,6 +16,6 @@ struct ParseOptions { std::pmr::memory_resource* memory_resource = std::pmr::get_default_resource(); }; -facts::EntrypointFacts parse_files(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set); +facts::FactArena parse_files(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set); -} // namespace entrypoint_codegen::clang_adapter +} // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp index e93539661d..eb4f0afc07 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp @@ -29,10 +29,10 @@ std::filesystem::path write_source_file(std::string_view name, std::string_view } TEST(ClangAdapterParseTest, RejectsEmptyInputList) { - const entrypoint_codegen::clang_adapter::ParseOptions options; - entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + const epgen::clang_adapter::ParseOptions options; + epgen::diagnostics::DiagnosticSet diagnostics; - EXPECT_THROW(entrypoint_codegen::clang_adapter::parse_files(options, diagnostics), std::invalid_argument); + EXPECT_THROW(epgen::clang_adapter::parse_files(options, diagnostics), std::invalid_argument); } TEST(ClangAdapterParseTest, ExtractsFastCgoFunctionFactsFromSourceFile) { @@ -47,40 +47,39 @@ TEST(ClangAdapterParseTest, ExtractsFastCgoFunctionFactsFromSourceFile) { }; } )cpp"); - entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + epgen::diagnostics::DiagnosticSet diagnostics; // Act - entrypoint_codegen::facts::EntrypointFacts facts = entrypoint_codegen::clang_adapter::parse_files( - entrypoint_codegen::clang_adapter::ParseOptions{ + epgen::facts::FactArena facts = epgen::clang_adapter::parse_files( + epgen::clang_adapter::ParseOptions{ .source_files = {source_file}, .clang_args = {"-std=c++2b"}, }, diagnostics); const auto functions = facts.functions(); - const entrypoint_codegen::facts::FunctionDecl* function = functions.empty() ? nullptr : &functions[0]; - const std::span params = - function == nullptr ? std::span() : facts.params(function->params); - const std::span layouts = - function == nullptr ? std::span() : facts.layouts(function->layouts); - const std::span argument_fields = - layouts.empty() ? std::span() : facts.fields(layouts[0].fields); - const std::span result_fields = - layouts.size() < 2 ? std::span() : facts.fields(layouts[1].fields); + const epgen::facts::FunctionDecl* function = functions.empty() ? nullptr : &functions[0]; + const std::span params = function == nullptr ? std::span() : facts.params(function->params); + const std::span layouts = + function == nullptr ? std::span() : facts.layouts(function->layouts); + const std::span argument_fields = + layouts.empty() ? std::span() : facts.fields(layouts[0].fields); + const std::span result_fields = + layouts.size() < 2 ? std::span() : facts.fields(layouts[1].fields); // Assert ASSERT_EQ(functions.size(), 1); ASSERT_NE(function, nullptr); EXPECT_EQ(facts.string(function->name), "prompp_store"); - EXPECT_EQ(function->bridge_kind, entrypoint_codegen::facts::BridgeKind::kFastCGo); + EXPECT_EQ(function->bridge_kind, epgen::facts::BridgeKind::kFastCGo); EXPECT_TRUE(function->has_c_linkage); ASSERT_EQ(params.size(), 2); EXPECT_EQ(facts.string(params[0].name), "args"); - EXPECT_EQ(params[0].role, entrypoint_codegen::facts::ParamRole::kArgs); + EXPECT_EQ(params[0].role, epgen::facts::ParamRole::kArgs); EXPECT_EQ(facts.string(params[1].name), "res"); - EXPECT_EQ(params[1].role, entrypoint_codegen::facts::ParamRole::kRes); + EXPECT_EQ(params[1].role, epgen::facts::ParamRole::kRes); ASSERT_EQ(layouts.size(), 2); - EXPECT_EQ(layouts[0].kind, entrypoint_codegen::facts::LayoutKind::kArguments); - EXPECT_EQ(layouts[1].kind, entrypoint_codegen::facts::LayoutKind::kResult); + EXPECT_EQ(layouts[0].kind, epgen::facts::LayoutKind::kArguments); + EXPECT_EQ(layouts[1].kind, epgen::facts::LayoutKind::kResult); ASSERT_EQ(argument_fields.size(), 1); EXPECT_EQ(facts.string(argument_fields[0].name), "series"); EXPECT_EQ(facts.string(argument_fields[0].type_spelling), "int"); @@ -92,11 +91,11 @@ TEST(ClangAdapterParseTest, ExtractsFastCgoFunctionFactsFromSourceFile) { TEST(ClangAdapterParseTest, RecordsClangDiagnosticsSeparately) { // Arrange const std::filesystem::path source_file = write_source_file("entrypoint_codegen_invalid_parse_test.cpp", "int broken = ;\n"); - entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + epgen::diagnostics::DiagnosticSet diagnostics; // Act - entrypoint_codegen::clang_adapter::parse_files( - entrypoint_codegen::clang_adapter::ParseOptions{ + epgen::clang_adapter::parse_files( + epgen::clang_adapter::ParseOptions{ .source_files = {source_file}, .clang_args = {"-std=c++2b"}, }, @@ -105,8 +104,8 @@ TEST(ClangAdapterParseTest, RecordsClangDiagnosticsSeparately) { // Assert ASSERT_EQ(diagnostic_values.size(), 1); - EXPECT_EQ(diagnostic_values[0].code, entrypoint_codegen::diagnostics::DiagnosticCode::kClangDiagnostic); - EXPECT_EQ(diagnostic_values[0].severity, entrypoint_codegen::diagnostics::Severity::kError); + EXPECT_EQ(diagnostic_values[0].code, epgen::diagnostics::DiagnosticCode::kClangDiagnostic); + EXPECT_EQ(diagnostic_values[0].severity, epgen::diagnostics::Severity::kError); } TEST(ClangAdapterParseTest, ExtractsFunctionsFromMultipleInputFiles) { @@ -117,11 +116,11 @@ TEST(ClangAdapterParseTest, ExtractsFunctionsFromMultipleInputFiles) { const std::filesystem::path second_source_file = write_source_file("entrypoint_codegen_batch_second.cpp", R"cpp( extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_second() {} )cpp"); - entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + epgen::diagnostics::DiagnosticSet diagnostics; // Act - entrypoint_codegen::facts::EntrypointFacts facts = entrypoint_codegen::clang_adapter::parse_files( - entrypoint_codegen::clang_adapter::ParseOptions{ + epgen::facts::FactArena facts = epgen::clang_adapter::parse_files( + epgen::clang_adapter::ParseOptions{ .source_files = {first_source_file, second_source_file}, .clang_args = {"-std=c++2b"}, }, @@ -153,11 +152,11 @@ TEST(ClangAdapterParseTest, ReportsAggregateTranslationUnitInternalLinkageCollis (void)helper(); } )cpp"); - entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + epgen::diagnostics::DiagnosticSet diagnostics; // Act - entrypoint_codegen::clang_adapter::parse_files( - entrypoint_codegen::clang_adapter::ParseOptions{ + epgen::clang_adapter::parse_files( + epgen::clang_adapter::ParseOptions{ .source_files = {first_source_file, second_source_file}, .clang_args = {"-std=c++2b"}, }, @@ -166,8 +165,8 @@ TEST(ClangAdapterParseTest, ReportsAggregateTranslationUnitInternalLinkageCollis // Assert ASSERT_FALSE(diagnostic_values.empty()); - EXPECT_EQ(diagnostic_values[0].code, entrypoint_codegen::diagnostics::DiagnosticCode::kClangDiagnostic); - EXPECT_EQ(diagnostic_values[0].severity, entrypoint_codegen::diagnostics::Severity::kError); + EXPECT_EQ(diagnostic_values[0].code, epgen::diagnostics::DiagnosticCode::kClangDiagnostic); + EXPECT_EQ(diagnostic_values[0].severity, epgen::diagnostics::Severity::kError); } TEST(ClangAdapterParseTest, IgnoresUnannotatedExternCFunctionWithoutEntrypointPrefix) { @@ -175,11 +174,11 @@ TEST(ClangAdapterParseTest, IgnoresUnannotatedExternCFunctionWithoutEntrypointPr const std::filesystem::path source_file = write_source_file("entrypoint_codegen_c_helper.cpp", R"cpp( extern "C" void helper_for_c_abi() {} )cpp"); - entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + epgen::diagnostics::DiagnosticSet diagnostics; // Act - entrypoint_codegen::facts::EntrypointFacts facts = entrypoint_codegen::clang_adapter::parse_files( - entrypoint_codegen::clang_adapter::ParseOptions{ + epgen::facts::FactArena facts = epgen::clang_adapter::parse_files( + epgen::clang_adapter::ParseOptions{ .source_files = {source_file}, .clang_args = {"-std=c++2b"}, }, @@ -194,11 +193,11 @@ TEST(ClangAdapterParseTest, ExtractsAnnotatedFunctionWithoutEntrypointPrefix) { const std::filesystem::path source_file = write_source_file("entrypoint_codegen_annotated_without_prefix.cpp", R"cpp( extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void store() {} )cpp"); - entrypoint_codegen::diagnostics::DiagnosticSet diagnostics; + epgen::diagnostics::DiagnosticSet diagnostics; // Act - entrypoint_codegen::facts::EntrypointFacts facts = entrypoint_codegen::clang_adapter::parse_files( - entrypoint_codegen::clang_adapter::ParseOptions{ + epgen::facts::FactArena facts = epgen::clang_adapter::parse_files( + epgen::clang_adapter::ParseOptions{ .source_files = {source_file}, .clang_args = {"-std=c++2b"}, }, @@ -208,7 +207,7 @@ TEST(ClangAdapterParseTest, ExtractsAnnotatedFunctionWithoutEntrypointPrefix) { // Assert ASSERT_EQ(functions.size(), 1); EXPECT_EQ(facts.string(functions[0].name), "store"); - EXPECT_EQ(functions[0].bridge_kind, entrypoint_codegen::facts::BridgeKind::kCGo); + EXPECT_EQ(functions[0].bridge_kind, epgen::facts::BridgeKind::kCGo); } } // namespace diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp index 149ca32a77..51562c3b70 100644 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp @@ -1,12 +1,12 @@ #include "contract/entrypoint_contract.h" #include "diagnostics/diagnostics.h" -#include "facts/entrypoint_facts.h" +#include "facts/fact_arena.h" #include #include -namespace entrypoint_codegen::contract { +namespace epgen::contract { namespace { @@ -23,7 +23,7 @@ void add_diagnostic(diagnostics::DiagnosticSet& diagnostic_set, }); } -bool has_layout(const facts::EntrypointFacts& facts, const facts::FunctionDecl& function, facts::LayoutKind kind) { +bool has_layout(const facts::FactArena& facts, const facts::FunctionDecl& function, facts::LayoutKind kind) { for (const facts::LayoutDecl& layout : facts.layouts(function.layouts)) { if (layout.kind == kind) { return true; @@ -32,7 +32,7 @@ bool has_layout(const facts::EntrypointFacts& facts, const facts::FunctionDecl& return false; } -void validate_fastcgo_function(const facts::EntrypointFacts& facts, +void validate_fastcgo_function(const facts::FactArena& facts, diagnostics::DiagnosticSet& diagnostic_set, facts::FunctionId function_id, const facts::FunctionDecl& function) { @@ -126,7 +126,7 @@ std::optional layout_kind_for_name(std::string_view name) { return std::nullopt; } -void validate_entrypoints(const facts::EntrypointFacts& facts, diagnostics::DiagnosticSet& diagnostic_set) { +void validate_entrypoints(const facts::FactArena& facts, diagnostics::DiagnosticSet& diagnostic_set) { const auto functions = facts.functions(); for (size_t i = 0; i < functions.size(); ++i) { const auto function_id = facts::FunctionId(static_cast(i)); @@ -148,4 +148,4 @@ void validate_entrypoints(const facts::EntrypointFacts& facts, diagnostics::Diag } } -} // namespace entrypoint_codegen::contract +} // namespace epgen::contract diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h index fe4a4451ec..d9d8c5f22d 100644 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h @@ -5,15 +5,15 @@ #include #include -namespace entrypoint_codegen::diagnostics { +namespace epgen::diagnostics { class DiagnosticSet; } -namespace entrypoint_codegen::facts { -class EntrypointFacts; +namespace epgen::facts { +class FactArena; } -namespace entrypoint_codegen::contract { +namespace epgen::contract { constexpr std::string_view kFunctionNamePrefix = "prompp_"; constexpr std::string_view kCGoAnnotation = "prompp.entrypoint.cgo"; @@ -31,6 +31,6 @@ constexpr std::string_view kResParamName = "res"; [[nodiscard]] facts::ParamRole param_role_for_name(std::string_view name); [[nodiscard]] std::optional layout_kind_for_name(std::string_view name); -void validate_entrypoints(const facts::EntrypointFacts& facts, diagnostics::DiagnosticSet& diagnostic_set); +void validate_entrypoints(const facts::FactArena& facts, diagnostics::DiagnosticSet& diagnostic_set); -} // namespace entrypoint_codegen::contract +} // namespace epgen::contract diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp index a105eec262..b2900ddf7f 100644 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp @@ -4,37 +4,37 @@ namespace { -using entrypoint_codegen::facts::BridgeKind; -using entrypoint_codegen::facts::LayoutKind; -using entrypoint_codegen::facts::ParamRole; +using epgen::facts::BridgeKind; +using epgen::facts::LayoutKind; +using epgen::facts::ParamRole; TEST(EntrypointContractTest, RecognizesPromppFunctionPrefix) { - EXPECT_TRUE(entrypoint_codegen::contract::is_entrypoint_function_name("prompp_fn")); - EXPECT_FALSE(entrypoint_codegen::contract::is_entrypoint_function_name("other_fn")); + EXPECT_TRUE(epgen::contract::is_entrypoint_function_name("prompp_fn")); + EXPECT_FALSE(epgen::contract::is_entrypoint_function_name("other_fn")); } TEST(EntrypointContractTest, ClassifiesBridgeAnnotationNames) { - EXPECT_EQ(entrypoint_codegen::contract::bridge_kind_for_annotation("prompp.entrypoint.cgo"), BridgeKind::kCGo); - EXPECT_EQ(entrypoint_codegen::contract::bridge_kind_for_annotation("prompp.entrypoint.fastcgo"), BridgeKind::kFastCGo); - EXPECT_EQ(entrypoint_codegen::contract::bridge_kind_for_annotation("other"), BridgeKind::kUnknown); + EXPECT_EQ(epgen::contract::bridge_kind_for_annotation("prompp.entrypoint.cgo"), BridgeKind::kCGo); + EXPECT_EQ(epgen::contract::bridge_kind_for_annotation("prompp.entrypoint.fastcgo"), BridgeKind::kFastCGo); + EXPECT_EQ(epgen::contract::bridge_kind_for_annotation("other"), BridgeKind::kUnknown); } TEST(EntrypointContractTest, ClassifiesParameterNames) { - EXPECT_EQ(entrypoint_codegen::contract::param_role_for_name("args"), ParamRole::kArgs); - EXPECT_EQ(entrypoint_codegen::contract::param_role_for_name("res"), ParamRole::kRes); - EXPECT_EQ(entrypoint_codegen::contract::param_role_for_name("other"), ParamRole::kOther); + EXPECT_EQ(epgen::contract::param_role_for_name("args"), ParamRole::kArgs); + EXPECT_EQ(epgen::contract::param_role_for_name("res"), ParamRole::kRes); + EXPECT_EQ(epgen::contract::param_role_for_name("other"), ParamRole::kOther); } TEST(EntrypointContractTest, ClassifiesLayoutNames) { - EXPECT_EQ(entrypoint_codegen::contract::layout_kind_for_name("Arguments"), LayoutKind::kArguments); - EXPECT_EQ(entrypoint_codegen::contract::layout_kind_for_name("Result"), LayoutKind::kResult); - EXPECT_FALSE(entrypoint_codegen::contract::layout_kind_for_name("Other").has_value()); + EXPECT_EQ(epgen::contract::layout_kind_for_name("Arguments"), LayoutKind::kArguments); + EXPECT_EQ(epgen::contract::layout_kind_for_name("Result"), LayoutKind::kResult); + EXPECT_FALSE(epgen::contract::layout_kind_for_name("Other").has_value()); } TEST(EntrypointContractTest, RecognizesVoidPointerSpellings) { - EXPECT_TRUE(entrypoint_codegen::contract::is_void_pointer_type("void*")); - EXPECT_TRUE(entrypoint_codegen::contract::is_void_pointer_type("void *")); - EXPECT_FALSE(entrypoint_codegen::contract::is_void_pointer_type("const void*")); + EXPECT_TRUE(epgen::contract::is_void_pointer_type("void*")); + EXPECT_TRUE(epgen::contract::is_void_pointer_type("void *")); + EXPECT_FALSE(epgen::contract::is_void_pointer_type("const void*")); } } // namespace diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract_validation_tests.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_validation_tests.cpp index 4c1b32ab84..6e3a380038 100644 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract_validation_tests.cpp +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_validation_tests.cpp @@ -3,28 +3,28 @@ #include #include "diagnostics/diagnostics.h" -#include "facts/entrypoint_facts.h" +#include "facts/fact_arena.h" #include #include namespace { -using entrypoint_codegen::diagnostics::DiagnosticCode; -using entrypoint_codegen::diagnostics::DiagnosticSet; -using entrypoint_codegen::diagnostics::Severity; -using entrypoint_codegen::facts::BridgeKind; -using entrypoint_codegen::facts::EntrypointFacts; -using entrypoint_codegen::facts::FunctionDecl; -using entrypoint_codegen::facts::LayoutDecl; -using entrypoint_codegen::facts::LayoutKind; -using entrypoint_codegen::facts::ParamDecl; -using entrypoint_codegen::facts::ParamRole; -using entrypoint_codegen::facts::SourceLocation; +using epgen::diagnostics::DiagnosticCode; +using epgen::diagnostics::DiagnosticSet; +using epgen::diagnostics::Severity; +using epgen::facts::BridgeKind; +using epgen::facts::FactArena; +using epgen::facts::FunctionDecl; +using epgen::facts::LayoutDecl; +using epgen::facts::LayoutKind; +using epgen::facts::ParamDecl; +using epgen::facts::ParamRole; +using epgen::facts::SourceLocation; class ValidateEntrypointsTest : public testing::Test { protected: - EntrypointFacts facts_; + FactArena facts_; DiagnosticSet diagnostics_; }; @@ -43,7 +43,7 @@ TEST_F(ValidateEntrypointsTest, AcceptsValidCgoFunction) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -73,7 +73,7 @@ TEST_F(ValidateEntrypointsTest, AcceptsValidFastCgoFunction) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -95,7 +95,7 @@ TEST_F(ValidateEntrypointsTest, ReportsMissingEntrypointPrefix) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -122,7 +122,7 @@ TEST_F(ValidateEntrypointsTest, ReportsMissingCLinkage) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -145,7 +145,7 @@ TEST_F(ValidateEntrypointsTest, ReportsMissingEntrypointAttribute) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -168,7 +168,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoReturnType) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -200,7 +200,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoParamCount) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -229,7 +229,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoParamType) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -255,7 +255,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnknownFastCgoParamRole) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -285,7 +285,7 @@ TEST_F(ValidateEntrypointsTest, ReportsInvalidTwoParamOrder) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -315,7 +315,7 @@ TEST_F(ValidateEntrypointsTest, ReportsInvalidSecondParamRole) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -341,7 +341,7 @@ TEST_F(ValidateEntrypointsTest, ReportsMissingArgumentsLayout) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -367,7 +367,7 @@ TEST_F(ValidateEntrypointsTest, ReportsMissingResultLayout) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -393,7 +393,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnexpectedArgumentsLayout) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert @@ -419,7 +419,7 @@ TEST_F(ValidateEntrypointsTest, ReportsUnexpectedResultLayout) { }); // Act - entrypoint_codegen::contract::validate_entrypoints(facts_, diagnostics_); + epgen::contract::validate_entrypoints(facts_, diagnostics_); const auto diagnostics = diagnostics_.diagnostics(); // Assert diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp index 21faa39764..fce4623cba 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp @@ -2,7 +2,7 @@ #include -namespace entrypoint_codegen::diagnostics { +namespace epgen::diagnostics { bool SeverityCounts::has_errors() const noexcept { return errors != 0; @@ -172,4 +172,4 @@ std::string_view severity_name(Severity severity) { return "error"; } -} // namespace entrypoint_codegen::diagnostics +} // namespace epgen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h index 6742a87495..45ed50ffca 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h @@ -10,7 +10,7 @@ #include #include -namespace entrypoint_codegen::diagnostics { +namespace epgen::diagnostics { enum class Severity : uint8_t { kInfo, @@ -73,4 +73,4 @@ class DiagnosticSet { [[nodiscard]] std::string_view diagnostic_message(const Diagnostic& diagnostic); [[nodiscard]] std::string_view severity_name(Severity severity); -} // namespace entrypoint_codegen::diagnostics +} // namespace epgen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp index 33f0a9ea3f..675ab48057 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp @@ -7,12 +7,12 @@ namespace { -using entrypoint_codegen::diagnostics::Diagnostic; -using entrypoint_codegen::diagnostics::DiagnosticCode; -using entrypoint_codegen::diagnostics::DiagnosticSet; -using entrypoint_codegen::diagnostics::Severity; -using entrypoint_codegen::facts::SourceFileId; -using entrypoint_codegen::facts::SourceLocation; +using epgen::diagnostics::Diagnostic; +using epgen::diagnostics::DiagnosticCode; +using epgen::diagnostics::DiagnosticSet; +using epgen::diagnostics::Severity; +using epgen::facts::SourceFileId; +using epgen::facts::SourceLocation; class DiagnosticSetTest : public testing::Test { protected: @@ -72,7 +72,7 @@ TEST_F(DiagnosticSetTest, CountsDiagnosticsBySeverity) { }); // Act - const entrypoint_codegen::diagnostics::SeverityCounts counts = entrypoint_codegen::diagnostics::count_by_severity(diagnostics_); + const epgen::diagnostics::SeverityCounts counts = epgen::diagnostics::count_by_severity(diagnostics_); // Assert EXPECT_EQ(counts.total, 3); @@ -84,7 +84,7 @@ TEST_F(DiagnosticSetTest, CountsDiagnosticsBySeverity) { TEST(DiagnosticsTest, NamesDiagnosticCode) { // Act - const std::string_view code = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kMissingNamePrefix); + const std::string_view code = epgen::diagnostics::diagnostic_code_name(DiagnosticCode::kMissingNamePrefix); // Assert EXPECT_EQ(code, "missing_name_prefix"); @@ -92,9 +92,9 @@ TEST(DiagnosticsTest, NamesDiagnosticCode) { TEST(DiagnosticsTest, NamesParameterRoleDiagnosticsWithDistinctCodes) { // Act - const std::string_view unknown_role = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kUnknownParamRole); - const std::string_view invalid_order = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidTwoParamOrder); - const std::string_view invalid_second = entrypoint_codegen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidSecondParamRole); + const std::string_view unknown_role = epgen::diagnostics::diagnostic_code_name(DiagnosticCode::kUnknownParamRole); + const std::string_view invalid_order = epgen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidTwoParamOrder); + const std::string_view invalid_second = epgen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidSecondParamRole); // Assert EXPECT_EQ(unknown_role, "unknown_param_role"); @@ -104,7 +104,7 @@ TEST(DiagnosticsTest, NamesParameterRoleDiagnosticsWithDistinctCodes) { TEST(DiagnosticsTest, ProvidesDefaultMessageForDiagnosticCode) { // Act - const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_default_message(DiagnosticCode::kMissingNamePrefix); + const std::string_view message = epgen::diagnostics::diagnostic_default_message(DiagnosticCode::kMissingNamePrefix); // Assert EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); @@ -122,7 +122,7 @@ TEST(DiagnosticsTest, UsesDiagnosticMessageWhenPresent) { }; // Act - const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_message(diagnostic); + const std::string_view message = epgen::diagnostics::diagnostic_message(diagnostic); // Assert EXPECT_EQ(message, "clang says no"); @@ -140,7 +140,7 @@ TEST(DiagnosticsTest, FallsBackToDefaultMessageWhenDiagnosticMessageIsAbsent) { }; // Act - const std::string_view message = entrypoint_codegen::diagnostics::diagnostic_message(diagnostic); + const std::string_view message = epgen::diagnostics::diagnostic_message(diagnostic); // Assert EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); @@ -148,9 +148,9 @@ TEST(DiagnosticsTest, FallsBackToDefaultMessageWhenDiagnosticMessageIsAbsent) { TEST(DiagnosticsTest, NamesSeverityValues) { // Act - const std::string_view info = entrypoint_codegen::diagnostics::severity_name(Severity::kInfo); - const std::string_view warning = entrypoint_codegen::diagnostics::severity_name(Severity::kWarning); - const std::string_view error = entrypoint_codegen::diagnostics::severity_name(Severity::kError); + const std::string_view info = epgen::diagnostics::severity_name(Severity::kInfo); + const std::string_view warning = epgen::diagnostics::severity_name(Severity::kWarning); + const std::string_view error = epgen::diagnostics::severity_name(Severity::kError); // Assert EXPECT_EQ(info, "info"); diff --git a/pp/tools/entrypoint_codegen/emit/report.cpp b/pp/tools/entrypoint_codegen/emit/report.cpp index f541e0b75c..c43e1ffa8a 100644 --- a/pp/tools/entrypoint_codegen/emit/report.cpp +++ b/pp/tools/entrypoint_codegen/emit/report.cpp @@ -5,7 +5,7 @@ #include #include -namespace entrypoint_codegen::emit { +namespace epgen::emit { namespace { @@ -92,16 +92,16 @@ std::string_view layout_kind_name(facts::LayoutKind kind) { return "unknown"; } -void write_location(std::ostream& out, const facts::EntrypointFacts& facts, facts::SourceLocation location) { +void write_location(std::ostream& out, const facts::FactArena& facts, facts::SourceLocation location) { out << "{" << "\"file\": \"" << json_escape(facts.string(facts.source_file(location.file).path)) << "\", " << "\"line\": " << location.line << ", " << "\"column\": " << location.column << "}"; } -void write_params(std::ostream& out, const facts::EntrypointFacts& facts, facts::ParamRange range) { +void write_params(std::ostream& out, const facts::FactArena& facts, facts::ParamListId id) { out << "["; - const auto params = facts.params(range); + const auto params = facts.params(id); for (size_t i = 0; i < params.size(); ++i) { if (i != 0) { out << ", "; @@ -118,9 +118,9 @@ void write_params(std::ostream& out, const facts::EntrypointFacts& facts, facts: out << "]"; } -void write_layouts(std::ostream& out, const facts::EntrypointFacts& facts, facts::LayoutRange range) { +void write_layouts(std::ostream& out, const facts::FactArena& facts, facts::LayoutListId id) { out << "["; - const auto layouts = facts.layouts(range); + const auto layouts = facts.layouts(id); for (size_t i = 0; i < layouts.size(); ++i) { if (i != 0) { out << ", "; @@ -149,7 +149,7 @@ void write_layouts(std::ostream& out, const facts::EntrypointFacts& facts, facts out << "]"; } -void write_source_files(std::ostream& out, const facts::EntrypointFacts& facts) { +void write_source_files(std::ostream& out, const facts::FactArena& facts) { out << " \"source_files\": [\n"; const auto source_files = facts.source_files(); for (size_t i = 0; i < source_files.size(); ++i) { @@ -162,7 +162,7 @@ void write_source_files(std::ostream& out, const facts::EntrypointFacts& facts) out << " ]"; } -void write_function(std::ostream& out, const facts::EntrypointFacts& facts, const facts::FunctionDecl& function) { +void write_function(std::ostream& out, const facts::FactArena& facts, const facts::FunctionDecl& function) { out << " {\n"; out << " \"name\": \"" << json_escape(facts.string(function.name)) << "\",\n"; out << " \"return_type\": \"" << json_escape(facts.string(function.return_type_spelling)) << "\",\n"; @@ -181,7 +181,7 @@ void write_function(std::ostream& out, const facts::EntrypointFacts& facts, cons out << " }"; } -void write_functions(std::ostream& out, const facts::EntrypointFacts& facts) { +void write_functions(std::ostream& out, const facts::FactArena& facts) { out << " \"functions\": [\n"; const auto functions = facts.functions(); for (size_t i = 0; i < functions.size(); ++i) { @@ -194,7 +194,7 @@ void write_functions(std::ostream& out, const facts::EntrypointFacts& facts) { out << " ]"; } -void write_diagnostic_function(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::Diagnostic& diagnostic) { +void write_diagnostic_function(std::ostream& out, const facts::FactArena& facts, const diagnostics::Diagnostic& diagnostic) { if (diagnostic.function.has_value()) { out << "\"" << json_escape(facts.string(facts.function(*diagnostic.function).name)) << "\""; return; @@ -202,7 +202,7 @@ void write_diagnostic_function(std::ostream& out, const facts::EntrypointFacts& out << "null"; } -void write_diagnostic(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::Diagnostic& diagnostic) { +void write_diagnostic(std::ostream& out, const facts::FactArena& facts, const diagnostics::Diagnostic& diagnostic) { const std::string_view message = diagnostics::diagnostic_message(diagnostic); out << " {" << "\"code\": \"" << json_escape(diagnostics::diagnostic_code_name(diagnostic.code)) << "\", " @@ -219,7 +219,7 @@ void write_diagnostic(std::ostream& out, const facts::EntrypointFacts& facts, co out << "}"; } -void write_json_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { +void write_json_diagnostics(std::ostream& out, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { out << " \"diagnostics\": [\n"; const auto diagnostic_values = diagnostic_set.diagnostics(); for (size_t i = 0; i < diagnostic_values.size(); ++i) { @@ -232,7 +232,7 @@ void write_json_diagnostics(std::ostream& out, const facts::EntrypointFacts& fac out << " ]"; } -void write_json_report(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { +void write_json_report(std::ostream& out, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { if (!out) { throw std::runtime_error("output stream is not writable"); } @@ -247,7 +247,7 @@ void write_json_report(std::ostream& out, const facts::EntrypointFacts& facts, c out << "}\n"; } -void write_compiler_diagnostics(std::ostream& out, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { +void write_compiler_diagnostics(std::ostream& out, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { for (const diagnostics::Diagnostic& diagnostic : diagnostic_set.diagnostics()) { if (diagnostic.location.has_value()) { const facts::SourceLocation location = *diagnostic.location; @@ -260,7 +260,7 @@ void write_compiler_diagnostics(std::ostream& out, const facts::EntrypointFacts& } // namespace -void write_report(std::ostream& out, ReportFormat format, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set) { +void write_report(std::ostream& out, ReportFormat format, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { switch (format) { case ReportFormat::kJson: { write_json_report(out, facts, diagnostic_set); @@ -273,4 +273,4 @@ void write_report(std::ostream& out, ReportFormat format, const facts::Entrypoin } } -} // namespace entrypoint_codegen::emit +} // namespace epgen::emit diff --git a/pp/tools/entrypoint_codegen/emit/report.h b/pp/tools/entrypoint_codegen/emit/report.h index 4cbf1a78ad..bfe74bc8d4 100644 --- a/pp/tools/entrypoint_codegen/emit/report.h +++ b/pp/tools/entrypoint_codegen/emit/report.h @@ -1,18 +1,18 @@ #pragma once #include "diagnostics/diagnostics.h" -#include "facts/entrypoint_facts.h" +#include "facts/fact_arena.h" #include #include -namespace entrypoint_codegen::emit { +namespace epgen::emit { enum class ReportFormat : uint8_t { kJson, kCompilerDiagnostics, }; -void write_report(std::ostream& out, ReportFormat format, const facts::EntrypointFacts& facts, const diagnostics::DiagnosticSet& diagnostic_set); +void write_report(std::ostream& out, ReportFormat format, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set); -} // namespace entrypoint_codegen::emit +} // namespace epgen::emit diff --git a/pp/tools/entrypoint_codegen/emit/report_tests.cpp b/pp/tools/entrypoint_codegen/emit/report_tests.cpp index 67093004ef..69663bd09a 100644 --- a/pp/tools/entrypoint_codegen/emit/report_tests.cpp +++ b/pp/tools/entrypoint_codegen/emit/report_tests.cpp @@ -11,14 +11,14 @@ namespace { -using entrypoint_codegen::diagnostics::Diagnostic; -using entrypoint_codegen::diagnostics::DiagnosticCode; -using entrypoint_codegen::diagnostics::DiagnosticSet; -using entrypoint_codegen::diagnostics::Severity; -using entrypoint_codegen::facts::BridgeKind; -using entrypoint_codegen::facts::EntrypointFacts; -using entrypoint_codegen::facts::FunctionDecl; -using entrypoint_codegen::facts::SourceLocation; +using epgen::diagnostics::Diagnostic; +using epgen::diagnostics::DiagnosticCode; +using epgen::diagnostics::DiagnosticSet; +using epgen::diagnostics::Severity; +using epgen::facts::BridgeKind; +using epgen::facts::FactArena; +using epgen::facts::FunctionDecl; +using epgen::facts::SourceLocation; bool has_unescaped_control_character_in_string(std::string_view json) { bool in_string = false; @@ -52,7 +52,7 @@ bool has_unescaped_control_character_in_string(std::string_view json) { class EmitReportTest : public testing::Test { protected: - EntrypointFacts facts_; + FactArena facts_; DiagnosticSet diagnostics_; std::ostringstream output_; }; @@ -72,7 +72,7 @@ TEST_F(EmitReportTest, WritesJsonFunctionFacts) { }); // Act - entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kJson, facts_, diagnostics_); + epgen::emit::write_report(output_, epgen::emit::ReportFormat::kJson, facts_, diagnostics_); const std::string json = output_.str(); const bool has_function_name = json.find("\"name\": \"prompp_store\"") != std::string::npos; const bool has_bridge_kind = json.find("\"bridge_kind\": \"cgo\"") != std::string::npos; @@ -96,7 +96,7 @@ TEST_F(EmitReportTest, EscapesJsonStringFieldsInOutput) { }); // Act - entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kJson, facts_, diagnostics_); + epgen::emit::write_report(output_, epgen::emit::ReportFormat::kJson, facts_, diagnostics_); const std::string json = output_.str(); const bool has_escaped_path = json.find("\"path\": \"entry\\\"point.cpp\"") != std::string::npos; const bool has_escaped_message = json.find("\"message\": \"line\\nmessage\"") != std::string::npos; @@ -122,7 +122,7 @@ TEST_F(EmitReportTest, EscapesNonWhitespaceControlCharactersInJsonStringFields) }); // Act - entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kJson, facts_, diagnostics_); + epgen::emit::write_report(output_, epgen::emit::ReportFormat::kJson, facts_, diagnostics_); const std::string json = output_.str(); // Assert @@ -144,7 +144,7 @@ TEST_F(EmitReportTest, WritesJsonDiagnosticLocationObjectWhenPresent) { }); // Act - entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kJson, facts_, diagnostics_); + epgen::emit::write_report(output_, epgen::emit::ReportFormat::kJson, facts_, diagnostics_); const std::string json = output_.str(); const bool has_location = json.find("\"location\": {\"file\": \"entrypoint.cpp\", \"line\": 7, \"column\": 9}") != std::string::npos; @@ -163,7 +163,7 @@ TEST_F(EmitReportTest, WritesNullJsonDiagnosticLocationWhenAbsent) { }); // Act - entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kJson, facts_, diagnostics_); + epgen::emit::write_report(output_, epgen::emit::ReportFormat::kJson, facts_, diagnostics_); const std::string json = output_.str(); const bool has_null_location = json.find("\"location\": null") != std::string::npos; @@ -183,7 +183,7 @@ TEST_F(EmitReportTest, WritesCompilerStyleDiagnosticLine) { }); // Act - entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kCompilerDiagnostics, facts_, diagnostics_); + epgen::emit::write_report(output_, epgen::emit::ReportFormat::kCompilerDiagnostics, facts_, diagnostics_); // Assert EXPECT_EQ(output_.str(), "entrypoint.cpp:7:9: error: entrypoint function must use prompp_ prefix [missing_name_prefix]\n"); @@ -200,7 +200,7 @@ TEST_F(EmitReportTest, WritesLocationlessDiagnosticLine) { }); // Act - entrypoint_codegen::emit::write_report(output_, entrypoint_codegen::emit::ReportFormat::kCompilerDiagnostics, facts_, diagnostics_); + epgen::emit::write_report(output_, epgen::emit::ReportFormat::kCompilerDiagnostics, facts_, diagnostics_); // Assert EXPECT_EQ(output_.str(), "info: runtime memory usage [runtime_memory_usage]\n"); diff --git a/pp/tools/entrypoint_codegen/facts/entrypoint_facts.cpp b/pp/tools/entrypoint_codegen/facts/fact_arena.cpp similarity index 56% rename from pp/tools/entrypoint_codegen/facts/entrypoint_facts.cpp rename to pp/tools/entrypoint_codegen/facts/fact_arena.cpp index 8d0b15ce39..0dd5198afc 100644 --- a/pp/tools/entrypoint_codegen/facts/entrypoint_facts.cpp +++ b/pp/tools/entrypoint_codegen/facts/fact_arena.cpp @@ -1,4 +1,4 @@ -#include "facts/entrypoint_facts.h" +#include "facts/fact_arena.h" #include "facts/string_table.h" @@ -7,10 +7,16 @@ #include #include -namespace entrypoint_codegen::facts { +namespace epgen::facts { namespace { +template +struct StoredList { + Id begin; + uint32_t count; +}; + template std::span range_span(const std::pmr::vector& values, uint32_t begin, uint32_t count) { const uint32_t offset = begin; @@ -21,15 +27,18 @@ std::span range_span(const std::pmr::vector& values, uint32_t begin, } // namespace -class EntrypointFacts::Impl { +class FactArena::Impl { public: explicit Impl(std::pmr::memory_resource* memory_resource) : strings_(memory_resource), source_files_(memory_resource), functions_(memory_resource), params_(memory_resource), + param_lists_(memory_resource), layouts_(memory_resource), - fields_(memory_resource) {} + layout_lists_(memory_resource), + fields_(memory_resource), + field_lists_(memory_resource) {} StringId add_string(std::string_view value) { return strings_.add(value); } @@ -41,31 +50,37 @@ class EntrypointFacts::Impl { return id; } - ParamRange add_params(std::span params) { + ParamListId add_params(std::span params) { const auto begin = ParamId(static_cast(params_.size())); + const auto id = ParamListId(static_cast(param_lists_.size())); params_.insert(params_.end(), params.begin(), params.end()); - return ParamRange{ + param_lists_.push_back(StoredList{ .begin = begin, .count = static_cast(params.size()), - }; + }); + return id; } - FieldRange add_fields(std::span fields) { + FieldListId add_fields(std::span fields) { const auto begin = FieldId(static_cast(fields_.size())); + const auto id = FieldListId(static_cast(field_lists_.size())); fields_.insert(fields_.end(), fields.begin(), fields.end()); - return FieldRange{ + field_lists_.push_back(StoredList{ .begin = begin, .count = static_cast(fields.size()), - }; + }); + return id; } - LayoutRange add_layouts(std::span layouts) { + LayoutListId add_layouts(std::span layouts) { const auto begin = LayoutId(static_cast(layouts_.size())); + const auto id = LayoutListId(static_cast(layout_lists_.size())); layouts_.insert(layouts_.end(), layouts.begin(), layouts.end()); - return LayoutRange{ + layout_lists_.push_back(StoredList{ .begin = begin, .count = static_cast(layouts.size()), - }; + }); + return id; } FunctionId add_function(FunctionDecl function) { @@ -92,18 +107,30 @@ class EntrypointFacts::Impl { [[nodiscard]] std::span params(FunctionId id) const { return params(function(id).params); } - [[nodiscard]] std::span params(ParamRange range) const { return range_span(params_, range.begin.get(), range.count); } + [[nodiscard]] std::span params(ParamListId id) const { + assert(id.get() < param_lists_.size()); + const StoredList list = param_lists_[id.get()]; + return range_span(params_, list.begin.get(), list.count); + } [[nodiscard]] std::span layouts(FunctionId id) const { return layouts(function(id).layouts); } - [[nodiscard]] std::span layouts(LayoutRange range) const { return range_span(layouts_, range.begin.get(), range.count); } + [[nodiscard]] std::span layouts(LayoutListId id) const { + assert(id.get() < layout_lists_.size()); + const StoredList list = layout_lists_[id.get()]; + return range_span(layouts_, list.begin.get(), list.count); + } [[nodiscard]] std::span fields(LayoutId id) const { assert(id.get() < layouts_.size()); return fields(layouts_[id.get()].fields); } - [[nodiscard]] std::span fields(FieldRange range) const { return range_span(fields_, range.begin.get(), range.count); } + [[nodiscard]] std::span fields(FieldListId id) const { + assert(id.get() < field_lists_.size()); + const StoredList list = field_lists_[id.get()]; + return range_span(fields_, list.begin.get(), list.count); + } private: StringTable strings_; @@ -111,11 +138,14 @@ class EntrypointFacts::Impl { std::pmr::vector source_files_; std::pmr::vector functions_; std::pmr::vector params_; + std::pmr::vector> param_lists_; std::pmr::vector layouts_; + std::pmr::vector> layout_lists_; std::pmr::vector fields_; + std::pmr::vector> field_lists_; }; -EntrypointFacts::EntrypointFacts(std::pmr::memory_resource* memory_resource) : memory_resource_(memory_resource) { +FactArena::FactArena(std::pmr::memory_resource* memory_resource) : memory_resource_(memory_resource) { std::pmr::polymorphic_allocator allocator(memory_resource_); impl_ = allocator.allocate(1); try { @@ -127,7 +157,7 @@ EntrypointFacts::EntrypointFacts(std::pmr::memory_resource* memory_resource) : m } } -EntrypointFacts::~EntrypointFacts() { +FactArena::~FactArena() { if (impl_ == nullptr) { return; } @@ -136,11 +166,11 @@ EntrypointFacts::~EntrypointFacts() { allocator.deallocate(impl_, 1); } -EntrypointFacts::EntrypointFacts(EntrypointFacts&& other) noexcept : impl_(other.impl_), memory_resource_(other.memory_resource_) { +FactArena::FactArena(FactArena&& other) noexcept : impl_(other.impl_), memory_resource_(other.memory_resource_) { other.impl_ = nullptr; } -EntrypointFacts& EntrypointFacts::operator=(EntrypointFacts&& other) noexcept { +FactArena& FactArena::operator=(FactArena&& other) noexcept { if (this == &other) { return *this; } @@ -155,72 +185,72 @@ EntrypointFacts& EntrypointFacts::operator=(EntrypointFacts&& other) noexcept { return *this; } -StringId EntrypointFacts::add_string(std::string_view value) { +StringId FactArena::add_string(std::string_view value) { return impl_->add_string(value); } -SourceFileId EntrypointFacts::add_source_file(std::string_view path) { +SourceFileId FactArena::add_source_file(std::string_view path) { return impl_->add_source_file(path); } -ParamRange EntrypointFacts::add_params(std::span params) { +ParamListId FactArena::add_params(std::span params) { return impl_->add_params(params); } -FieldRange EntrypointFacts::add_fields(std::span fields) { +FieldListId FactArena::add_fields(std::span fields) { return impl_->add_fields(fields); } -LayoutRange EntrypointFacts::add_layouts(std::span layouts) { +LayoutListId FactArena::add_layouts(std::span layouts) { return impl_->add_layouts(layouts); } -FunctionId EntrypointFacts::add_function(FunctionDecl function) { +FunctionId FactArena::add_function(FunctionDecl function) { return impl_->add_function(function); } -std::string_view EntrypointFacts::string(StringId id) const { +std::string_view FactArena::string(StringId id) const { return impl_->string(id); } -const SourceFileDecl& EntrypointFacts::source_file(SourceFileId id) const { +const SourceFileDecl& FactArena::source_file(SourceFileId id) const { return impl_->source_file(id); } -const FunctionDecl& EntrypointFacts::function(FunctionId id) const { +const FunctionDecl& FactArena::function(FunctionId id) const { return impl_->function(id); } -std::span EntrypointFacts::source_files() const { +std::span FactArena::source_files() const { return impl_->source_files(); } -std::span EntrypointFacts::functions() const { +std::span FactArena::functions() const { return impl_->functions(); } -std::span EntrypointFacts::params(FunctionId id) const { +std::span FactArena::params(FunctionId id) const { return impl_->params(id); } -std::span EntrypointFacts::params(ParamRange range) const { - return impl_->params(range); +std::span FactArena::params(ParamListId id) const { + return impl_->params(id); } -std::span EntrypointFacts::layouts(FunctionId id) const { +std::span FactArena::layouts(FunctionId id) const { return impl_->layouts(id); } -std::span EntrypointFacts::layouts(LayoutRange range) const { - return impl_->layouts(range); +std::span FactArena::layouts(LayoutListId id) const { + return impl_->layouts(id); } -std::span EntrypointFacts::fields(LayoutId id) const { +std::span FactArena::fields(LayoutId id) const { return impl_->fields(id); } -std::span EntrypointFacts::fields(FieldRange range) const { - return impl_->fields(range); +std::span FactArena::fields(FieldListId id) const { + return impl_->fields(id); } -} // namespace entrypoint_codegen::facts +} // namespace epgen::facts diff --git a/pp/tools/entrypoint_codegen/facts/entrypoint_facts.h b/pp/tools/entrypoint_codegen/facts/fact_arena.h similarity index 53% rename from pp/tools/entrypoint_codegen/facts/entrypoint_facts.h rename to pp/tools/entrypoint_codegen/facts/fact_arena.h index c6da2b38d7..e18e5d0f75 100644 --- a/pp/tools/entrypoint_codegen/facts/entrypoint_facts.h +++ b/pp/tools/entrypoint_codegen/facts/fact_arena.h @@ -7,24 +7,24 @@ #include #include -namespace entrypoint_codegen::facts { +namespace epgen::facts { -class EntrypointFacts { +class FactArena { public: - explicit EntrypointFacts(std::pmr::memory_resource* memory_resource = std::pmr::get_default_resource()); - ~EntrypointFacts(); + explicit FactArena(std::pmr::memory_resource* memory_resource = std::pmr::get_default_resource()); + ~FactArena(); - EntrypointFacts(EntrypointFacts&&) noexcept; - EntrypointFacts& operator=(EntrypointFacts&&) noexcept; + FactArena(FactArena&&) noexcept; + FactArena& operator=(FactArena&&) noexcept; - EntrypointFacts(const EntrypointFacts&) = delete; - EntrypointFacts& operator=(const EntrypointFacts&) = delete; + FactArena(const FactArena&) = delete; + FactArena& operator=(const FactArena&) = delete; StringId add_string(std::string_view value); SourceFileId add_source_file(std::string_view path); - ParamRange add_params(std::span params); - FieldRange add_fields(std::span fields); - LayoutRange add_layouts(std::span layouts); + ParamListId add_params(std::span params); + FieldListId add_fields(std::span fields); + LayoutListId add_layouts(std::span layouts); FunctionId add_function(FunctionDecl function); [[nodiscard]] std::string_view string(StringId id) const; @@ -36,13 +36,13 @@ class EntrypointFacts { [[nodiscard]] std::span functions() const; [[nodiscard]] std::span params(FunctionId id) const; - [[nodiscard]] std::span params(ParamRange range) const; + [[nodiscard]] std::span params(ParamListId id) const; [[nodiscard]] std::span layouts(FunctionId id) const; - [[nodiscard]] std::span layouts(LayoutRange range) const; + [[nodiscard]] std::span layouts(LayoutListId id) const; [[nodiscard]] std::span fields(LayoutId id) const; - [[nodiscard]] std::span fields(FieldRange range) const; + [[nodiscard]] std::span fields(FieldListId id) const; private: class Impl; @@ -50,4 +50,4 @@ class EntrypointFacts { std::pmr::memory_resource* memory_resource_ = std::pmr::get_default_resource(); }; -} // namespace entrypoint_codegen::facts +} // namespace epgen::facts diff --git a/pp/tools/entrypoint_codegen/facts/entrypoint_facts_tests.cpp b/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp similarity index 69% rename from pp/tools/entrypoint_codegen/facts/entrypoint_facts_tests.cpp rename to pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp index 9cfe17d686..110a41fad3 100644 --- a/pp/tools/entrypoint_codegen/facts/entrypoint_facts_tests.cpp +++ b/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp @@ -1,4 +1,4 @@ -#include "facts/entrypoint_facts.h" +#include "facts/fact_arena.h" #include @@ -7,22 +7,22 @@ namespace { -using entrypoint_codegen::facts::BridgeKind; -using entrypoint_codegen::facts::EntrypointFacts; -using entrypoint_codegen::facts::FieldDecl; -using entrypoint_codegen::facts::FunctionDecl; -using entrypoint_codegen::facts::LayoutDecl; -using entrypoint_codegen::facts::LayoutKind; -using entrypoint_codegen::facts::ParamDecl; -using entrypoint_codegen::facts::ParamRole; -using entrypoint_codegen::facts::SourceLocation; - -class EntrypointFactsTest : public testing::Test { +using epgen::facts::BridgeKind; +using epgen::facts::FactArena; +using epgen::facts::FieldDecl; +using epgen::facts::FunctionDecl; +using epgen::facts::LayoutDecl; +using epgen::facts::LayoutKind; +using epgen::facts::ParamDecl; +using epgen::facts::ParamRole; +using epgen::facts::SourceLocation; + +class FactArenaTest : public testing::Test { protected: - EntrypointFacts facts_; + FactArena facts_; }; -TEST_F(EntrypointFactsTest, StoresStringsAndSourceFiles) { +TEST_F(FactArenaTest, StoresStringsAndSourceFiles) { // Act const auto string_id = facts_.add_string("prompp_fn"); const auto source_file_id = facts_.add_source_file("entrypoint.cpp"); @@ -34,7 +34,7 @@ TEST_F(EntrypointFactsTest, StoresStringsAndSourceFiles) { EXPECT_EQ(facts_.string(facts_.source_file(source_file_id).path), "entrypoint.cpp"); } -TEST_F(EntrypointFactsTest, ResolvesContiguousRangesToStoredRecords) { +TEST_F(FactArenaTest, ResolvesListIdsToStoredRecords) { // Arrange const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 3, .column = 5}; const std::vector params{ @@ -46,10 +46,10 @@ TEST_F(EntrypointFactsTest, ResolvesContiguousRangesToStoredRecords) { }; // Act - const auto param_range = facts_.add_params(params); - const auto field_range = facts_.add_fields(fields); - const auto stored_params = facts_.params(param_range); - const auto stored_fields = facts_.fields(field_range); + const auto param_list_id = facts_.add_params(params); + const auto field_list_id = facts_.add_fields(fields); + const auto stored_params = facts_.params(param_list_id); + const auto stored_fields = facts_.fields(field_list_id); // Assert ASSERT_EQ(stored_params.size(), 2); @@ -59,7 +59,7 @@ TEST_F(EntrypointFactsTest, ResolvesContiguousRangesToStoredRecords) { EXPECT_EQ(facts_.string(stored_fields[0].name), "series"); } -TEST_F(EntrypointFactsTest, ResolvesFunctionOwnedRanges) { +TEST_F(FactArenaTest, ResolvesFunctionOwnedLists) { // Arrange const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 3, .column = 5}; const std::vector params{ @@ -68,8 +68,8 @@ TEST_F(EntrypointFactsTest, ResolvesFunctionOwnedRanges) { const std::vector layouts{ LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, }; - const auto param_range = facts_.add_params(params); - const auto layout_range = facts_.add_layouts(layouts); + const auto param_list_id = facts_.add_params(params); + const auto layout_list_id = facts_.add_layouts(layouts); // Act const auto function_id = facts_.add_function(FunctionDecl{ @@ -77,8 +77,8 @@ TEST_F(EntrypointFactsTest, ResolvesFunctionOwnedRanges) { .return_type_spelling = facts_.add_string("void"), .documentation = facts_.add_string(""), .bridge_kind = BridgeKind::kFastCGo, - .params = param_range, - .layouts = layout_range, + .params = param_list_id, + .layouts = layout_list_id, .location = location, .has_c_linkage = true, }); @@ -95,12 +95,12 @@ TEST_F(EntrypointFactsTest, ResolvesFunctionOwnedRanges) { EXPECT_EQ(stored_layouts[0].kind, LayoutKind::kArguments); } -TEST_F(EntrypointFactsTest, MoveTransfersStoredFacts) { +TEST_F(FactArenaTest, MoveTransfersStoredFacts) { // Arrange const auto string_id = facts_.add_string("prompp_fn"); // Act - EntrypointFacts moved = std::move(facts_); + FactArena moved = std::move(facts_); // Assert EXPECT_EQ(moved.string(string_id), "prompp_fn"); diff --git a/pp/tools/entrypoint_codegen/facts/facts.h b/pp/tools/entrypoint_codegen/facts/facts.h index 7265fb0c6f..47a4501ef1 100644 --- a/pp/tools/entrypoint_codegen/facts/facts.h +++ b/pp/tools/entrypoint_codegen/facts/facts.h @@ -4,7 +4,7 @@ #include -namespace entrypoint_codegen::facts { +namespace epgen::facts { using SourceFileId = TaggedIndex; using FunctionId = TaggedIndex; @@ -13,20 +13,9 @@ using ParamId = TaggedIndex; using FieldId = TaggedIndex; using StringId = TaggedIndex; -struct ParamRange { - ParamId begin; - uint32_t count; -}; - -struct LayoutRange { - LayoutId begin; - uint32_t count; -}; - -struct FieldRange { - FieldId begin; - uint32_t count; -}; +using ParamListId = TaggedIndex; +using LayoutListId = TaggedIndex; +using FieldListId = TaggedIndex; enum class BridgeKind : uint8_t { kUnknown, @@ -70,7 +59,7 @@ struct FieldDecl { struct LayoutDecl { LayoutKind kind; - FieldRange fields; + FieldListId fields; SourceLocation location; }; @@ -79,10 +68,10 @@ struct FunctionDecl { StringId return_type_spelling; StringId documentation; BridgeKind bridge_kind; - ParamRange params; - LayoutRange layouts; + ParamListId params; + LayoutListId layouts; SourceLocation location; bool has_c_linkage; }; -} // namespace entrypoint_codegen::facts +} // namespace epgen::facts diff --git a/pp/tools/entrypoint_codegen/facts/string_table.cpp b/pp/tools/entrypoint_codegen/facts/string_table.cpp index 2f27bce8f5..a0089e7027 100644 --- a/pp/tools/entrypoint_codegen/facts/string_table.cpp +++ b/pp/tools/entrypoint_codegen/facts/string_table.cpp @@ -7,7 +7,7 @@ #include #include -namespace entrypoint_codegen::facts { +namespace epgen::facts { class StringTable::Impl { public: @@ -114,4 +114,4 @@ bool StringTable::empty() const noexcept { return impl_->empty(); } -} // namespace entrypoint_codegen::facts +} // namespace epgen::facts diff --git a/pp/tools/entrypoint_codegen/facts/string_table.h b/pp/tools/entrypoint_codegen/facts/string_table.h index 4aff072be3..7087805250 100644 --- a/pp/tools/entrypoint_codegen/facts/string_table.h +++ b/pp/tools/entrypoint_codegen/facts/string_table.h @@ -6,7 +6,7 @@ #include #include -namespace entrypoint_codegen::facts { +namespace epgen::facts { class StringTable { public: @@ -31,4 +31,4 @@ class StringTable { std::pmr::memory_resource* memory_resource_{}; }; -} // namespace entrypoint_codegen::facts \ No newline at end of file +} // namespace epgen::facts \ No newline at end of file diff --git a/pp/tools/entrypoint_codegen/facts/string_table_tests.cpp b/pp/tools/entrypoint_codegen/facts/string_table_tests.cpp new file mode 100644 index 0000000000..ab5e8188a8 --- /dev/null +++ b/pp/tools/entrypoint_codegen/facts/string_table_tests.cpp @@ -0,0 +1,78 @@ +#include "facts/string_table.h" + +#include + +#include +#include + +namespace { + +using epgen::facts::StringTable; + +TEST(StringTableTest, StartsEmpty) { + // Arrange + const StringTable table; + + // Assert + EXPECT_TRUE(table.empty()); + EXPECT_EQ(table.size(), 0); +} + +TEST(StringTableTest, StoresStringsByStableId) { + // Arrange + StringTable table; + + // Act + const auto first = table.add("prompp_fn"); + const auto second = table.add("entrypoint.cpp"); + + // Assert + EXPECT_FALSE(table.empty()); + EXPECT_EQ(table.size(), 2); + EXPECT_EQ(table.get(first), "prompp_fn"); + EXPECT_EQ(table.get(second), "entrypoint.cpp"); +} + +TEST(StringTableTest, StoresEmptyAndRepeatedStringsAsDistinctEntries) { + // Arrange + StringTable table; + + // Act + const auto empty = table.add(""); + const auto first = table.add("same"); + const auto second = table.add("same"); + + // Assert + EXPECT_EQ(table.size(), 3); + EXPECT_EQ(table.get(empty), ""); + EXPECT_EQ(table.get(first), "same"); + EXPECT_EQ(table.get(second), "same"); + EXPECT_NE(first, second); +} + +TEST(StringTableTest, PreservesEmbeddedNullBytes) { + // Arrange + StringTable table; + constexpr char value[] = {'a', '\0', 'b'}; + + // Act + const auto id = table.add(std::string_view(value, sizeof(value))); + + // Assert + EXPECT_EQ(table.get(id), std::string_view(value, sizeof(value))); +} + +TEST(StringTableTest, MoveTransfersStoredStrings) { + // Arrange + StringTable table; + const auto id = table.add("prompp_fn"); + + // Act + StringTable moved = std::move(table); + + // Assert + EXPECT_EQ(moved.size(), 1); + EXPECT_EQ(moved.get(id), "prompp_fn"); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/facts/tagged_index.h b/pp/tools/entrypoint_codegen/facts/tagged_index.h index edad9f323f..99cf274ead 100644 --- a/pp/tools/entrypoint_codegen/facts/tagged_index.h +++ b/pp/tools/entrypoint_codegen/facts/tagged_index.h @@ -4,7 +4,7 @@ #include #include -namespace entrypoint_codegen::facts { +namespace epgen::facts { template class TaggedIndex { @@ -24,4 +24,4 @@ class TaggedIndex { UInt value_{}; }; -} // namespace entrypoint_codegen::facts \ No newline at end of file +} // namespace epgen::facts \ No newline at end of file From fc0699fa59aec28de6cd93c2b9cde97346f7d9c5 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Wed, 8 Jul 2026 13:46:18 +0000 Subject: [PATCH 20/31] facts tests tweak --- .../facts/fact_arena_tests.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp b/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp index 110a41fad3..e0e08a3861 100644 --- a/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp +++ b/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp @@ -23,6 +23,9 @@ class FactArenaTest : public testing::Test { }; TEST_F(FactArenaTest, StoresStringsAndSourceFiles) { + // // entrypoint.cpp + // void prompp_fn(); + // Act const auto string_id = facts_.add_string("prompp_fn"); const auto source_file_id = facts_.add_source_file("entrypoint.cpp"); @@ -35,6 +38,9 @@ TEST_F(FactArenaTest, StoresStringsAndSourceFiles) { } TEST_F(FactArenaTest, ResolvesListIdsToStoredRecords) { + // // entrypoint.cpp + // void prompp_fn(void* args, void* res); + // Arrange const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 3, .column = 5}; const std::vector params{ @@ -54,12 +60,19 @@ TEST_F(FactArenaTest, ResolvesListIdsToStoredRecords) { // Assert ASSERT_EQ(stored_params.size(), 2); EXPECT_EQ(facts_.string(stored_params[0].name), "args"); + EXPECT_EQ(facts_.string(stored_params[1].name), "res"); + + EXPECT_EQ(stored_params[0].role, ParamRole::kArgs); EXPECT_EQ(stored_params[1].role, ParamRole::kRes); + ASSERT_EQ(stored_fields.size(), 1); EXPECT_EQ(facts_.string(stored_fields[0].name), "series"); } TEST_F(FactArenaTest, ResolvesFunctionOwnedLists) { + // // entrypoint.cpp + // void prompp_fn(void* args); + // Arrange const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 3, .column = 5}; const std::vector params{ @@ -89,13 +102,17 @@ TEST_F(FactArenaTest, ResolvesFunctionOwnedLists) { // Assert ASSERT_EQ(functions.size(), 1); EXPECT_EQ(facts_.string(facts_.function(function_id).name), "prompp_fn"); + ASSERT_EQ(stored_params.size(), 1); EXPECT_EQ(facts_.string(stored_params[0].name), "args"); + ASSERT_EQ(stored_layouts.size(), 1); EXPECT_EQ(stored_layouts[0].kind, LayoutKind::kArguments); } TEST_F(FactArenaTest, MoveTransfersStoredFacts) { + // void prompp_fn(); + // Arrange const auto string_id = facts_.add_string("prompp_fn"); From 11df36a168db9ea0a1d967151a5662ea88f8b2b9 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Wed, 8 Jul 2026 14:46:11 +0000 Subject: [PATCH 21/31] contract review --- .../entrypoint_codegen/app/argparse_tests.cpp | 1 + pp/tools/entrypoint_codegen/app/run.cpp | 2 +- .../app/runtime_debug_tests.cpp | 34 +- .../clang_adapter/parse_tests.cpp | 4 + .../contract/entrypoint_contract.cpp | 94 +-- .../contract/entrypoint_contract.h | 3 +- .../contract/entrypoint_contract_tests.cpp | 590 +++++++++++++++++- .../entrypoint_contract_validation_tests.cpp | 430 ------------- .../facts/fact_arena_tests.cpp | 9 +- 9 files changed, 657 insertions(+), 510 deletions(-) delete mode 100644 pp/tools/entrypoint_codegen/contract/entrypoint_contract_validation_tests.cpp diff --git a/pp/tools/entrypoint_codegen/app/argparse_tests.cpp b/pp/tools/entrypoint_codegen/app/argparse_tests.cpp index 07010c96e3..821cc3b822 100644 --- a/pp/tools/entrypoint_codegen/app/argparse_tests.cpp +++ b/pp/tools/entrypoint_codegen/app/argparse_tests.cpp @@ -121,6 +121,7 @@ TEST(ArgparseTest, CollectsClangArgsFromFlagAndSeparator) { } TEST(ArgparseTest, RejectsUnknownOutputMode) { + // Act / Assert EXPECT_THROW(parse_args({"entrypoint_codegen", "--mode=xml"}), std::runtime_error); } diff --git a/pp/tools/entrypoint_codegen/app/run.cpp b/pp/tools/entrypoint_codegen/app/run.cpp index d22ce74ab0..4f65a53d6a 100644 --- a/pp/tools/entrypoint_codegen/app/run.cpp +++ b/pp/tools/entrypoint_codegen/app/run.cpp @@ -46,7 +46,7 @@ RunReport run(const RunOptions& options) { }, diagnostic_set); - contract::validate_entrypoints(facts, diagnostic_set); + contract::validate_contract(facts, diagnostic_set); if (options.runtime.debug_diagnostics) { append_runtime_debug_diagnostics(diagnostic_set, memory_resource.snapshot()); diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp index 279d3df0a9..748fd6c07e 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp +++ b/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp @@ -4,8 +4,6 @@ #include "diagnostics/diagnostics.h" -#include - namespace { using epgen::app::MemoryUsageSnapshot; @@ -13,27 +11,25 @@ using epgen::diagnostics::DiagnosticCode; using epgen::diagnostics::DiagnosticSet; using epgen::diagnostics::Severity; -class RuntimeDebugTest : public testing::Test { - protected: - DiagnosticSet diagnostics_; -}; +TEST(RuntimeDebugTest, AppendsMemoryUsageDiagnostic) { + // Arrange + DiagnosticSet diagnostics; -TEST_F(RuntimeDebugTest, AppendsMemoryUsageDiagnostic) { // Act - epgen::app::append_runtime_debug_diagnostics(diagnostics_, MemoryUsageSnapshot{ - .allocated_bytes = 11, - .deallocated_bytes = 7, - .peak_live_bytes = 9, - }); - const auto diagnostics = diagnostics_.diagnostics(); + epgen::app::append_runtime_debug_diagnostics(diagnostics, MemoryUsageSnapshot{ + .allocated_bytes = 11, + .deallocated_bytes = 7, + .peak_live_bytes = 9, + }); + const auto diagnostic_values = diagnostics.diagnostics(); // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kRuntimeMemoryUsage); - EXPECT_EQ(diagnostics[0].severity, Severity::kInfo); - ASSERT_TRUE(diagnostics[0].message.has_value()); - EXPECT_EQ(*diagnostics[0].message, "App PMR allocations: allocated=11 deallocated=7 peak_live=9 bytes"); - EXPECT_FALSE(diagnostics[0].location.has_value()); + ASSERT_EQ(diagnostic_values.size(), 1); + EXPECT_EQ(diagnostic_values[0].code, DiagnosticCode::kRuntimeMemoryUsage); + EXPECT_EQ(diagnostic_values[0].severity, Severity::kInfo); + ASSERT_TRUE(diagnostic_values[0].message.has_value()); + EXPECT_EQ(*diagnostic_values[0].message, "App PMR allocations: allocated=11 deallocated=7 peak_live=9 bytes"); + EXPECT_FALSE(diagnostic_values[0].location.has_value()); } } // namespace diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp index eb4f0afc07..f5fc33ca9c 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp @@ -29,9 +29,13 @@ std::filesystem::path write_source_file(std::string_view name, std::string_view } TEST(ClangAdapterParseTest, RejectsEmptyInputList) { + // Arrange const epgen::clang_adapter::ParseOptions options; epgen::diagnostics::DiagnosticSet diagnostics; + // Act + + // Assert EXPECT_THROW(epgen::clang_adapter::parse_files(options, diagnostics), std::invalid_argument); } diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp index 51562c3b70..e84fd10ba7 100644 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp @@ -14,10 +14,12 @@ void add_diagnostic(diagnostics::DiagnosticSet& diagnostic_set, diagnostics::DiagnosticCode code, facts::FunctionId function_id, facts::SourceLocation location) { + using diagnostics::Severity; + diagnostic_set.add(diagnostics::Diagnostic{ .code = code, .message = std::nullopt, - .severity = diagnostics::Severity::kError, + .severity = Severity::kError, .function = function_id, .location = location, }); @@ -36,60 +38,61 @@ void validate_fastcgo_function(const facts::FactArena& facts, diagnostics::DiagnosticSet& diagnostic_set, facts::FunctionId function_id, const facts::FunctionDecl& function) { + using diagnostics::DiagnosticCode; + using facts::LayoutKind; + using facts::ParamDecl; + using facts::ParamRole; + if (facts.string(function.return_type_spelling) != "void") { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedReturnType, function_id, function.location); + add_diagnostic(diagnostic_set, DiagnosticCode::kUnsupportedReturnType, function_id, function.location); } const auto params = facts.params(function.params); if (params.size() > 2) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedParamCount, function_id, function.location); + add_diagnostic(diagnostic_set, DiagnosticCode::kUnsupportedParamCount, function_id, function.location); } bool uses_args = false; bool uses_res = false; for (size_t i = 0; i < params.size(); ++i) { - const facts::ParamDecl& param = params[i]; + const ParamDecl& param = params[i]; if (!is_void_pointer_type(facts.string(param.type_spelling))) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnsupportedParamType, function_id, param.location); + add_diagnostic(diagnostic_set, DiagnosticCode::kUnsupportedParamType, function_id, param.location); } - if (param.role == facts::ParamRole::kOther) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnknownParamRole, function_id, param.location); + if (param.role == ParamRole::kOther) { + add_diagnostic(diagnostic_set, DiagnosticCode::kUnknownParamRole, function_id, param.location); continue; } - if (i == 0 && param.role == facts::ParamRole::kRes && params.size() == 2) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kInvalidTwoParamOrder, function_id, param.location); + if (i == 0 && param.role == ParamRole::kRes && params.size() == 2) { + add_diagnostic(diagnostic_set, DiagnosticCode::kInvalidTwoParamOrder, function_id, param.location); } - if (i == 1 && param.role != facts::ParamRole::kRes) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kInvalidSecondParamRole, function_id, param.location); + if (i == 1 && param.role != ParamRole::kRes) { + add_diagnostic(diagnostic_set, DiagnosticCode::kInvalidSecondParamRole, function_id, param.location); } - uses_args |= param.role == facts::ParamRole::kArgs; - uses_res |= param.role == facts::ParamRole::kRes; + uses_args |= param.role == ParamRole::kArgs; + uses_res |= param.role == ParamRole::kRes; } - const bool has_arguments = has_layout(facts, function, facts::LayoutKind::kArguments); - const bool has_result = has_layout(facts, function, facts::LayoutKind::kResult); + const bool has_arguments = has_layout(facts, function, LayoutKind::kArguments); + const bool has_result = has_layout(facts, function, LayoutKind::kResult); if (uses_args && !has_arguments) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingArgumentsLayout, function_id, function.location); + add_diagnostic(diagnostic_set, DiagnosticCode::kMissingArgumentsLayout, function_id, function.location); } if (uses_res && !has_result) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingResultLayout, function_id, function.location); + add_diagnostic(diagnostic_set, DiagnosticCode::kMissingResultLayout, function_id, function.location); } if (!uses_args && has_arguments) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnexpectedArgumentsLayout, function_id, function.location); + add_diagnostic(diagnostic_set, DiagnosticCode::kUnexpectedArgumentsLayout, function_id, function.location); } if (!uses_res && has_result) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kUnexpectedResultLayout, function_id, function.location); + add_diagnostic(diagnostic_set, DiagnosticCode::kUnexpectedResultLayout, function_id, function.location); } } } // namespace -bool starts_with(std::string_view value, std::string_view prefix) { - return value.substr(0, prefix.size()) == prefix; -} - bool is_entrypoint_function_name(std::string_view name) { - return starts_with(name, kFunctionNamePrefix); + return name.starts_with(kFunctionNamePrefix); } bool is_void_pointer_type(std::string_view type) { @@ -97,52 +100,63 @@ bool is_void_pointer_type(std::string_view type) { } facts::BridgeKind bridge_kind_for_annotation(std::string_view annotation) { + using facts::BridgeKind; + if (annotation == kCGoAnnotation) { - return facts::BridgeKind::kCGo; + return BridgeKind::kCGo; } if (annotation == kFastCGoAnnotation) { - return facts::BridgeKind::kFastCGo; + return BridgeKind::kFastCGo; } - return facts::BridgeKind::kUnknown; + return BridgeKind::kUnknown; } facts::ParamRole param_role_for_name(std::string_view name) { + using facts::ParamRole; + if (name == kArgsParamName) { - return facts::ParamRole::kArgs; + return ParamRole::kArgs; } if (name == kResParamName) { - return facts::ParamRole::kRes; + return ParamRole::kRes; } - return facts::ParamRole::kOther; + return ParamRole::kOther; } std::optional layout_kind_for_name(std::string_view name) { + using facts::LayoutKind; + if (name == kArgumentsLayoutName) { - return facts::LayoutKind::kArguments; + return LayoutKind::kArguments; } if (name == kResultLayoutName) { - return facts::LayoutKind::kResult; + return LayoutKind::kResult; } return std::nullopt; } -void validate_entrypoints(const facts::FactArena& facts, diagnostics::DiagnosticSet& diagnostic_set) { +void validate_contract(const facts::FactArena& facts, diagnostics::DiagnosticSet& diagnostic_set) { + using diagnostics::DiagnosticCode; + using facts::BridgeKind; + using facts::FunctionDecl; + using facts::FunctionId; + const auto functions = facts.functions(); for (size_t i = 0; i < functions.size(); ++i) { - const auto function_id = facts::FunctionId(static_cast(i)); - const facts::FunctionDecl& function = functions[i]; + const auto function_id = FunctionId(static_cast(i)); + const FunctionDecl& function = functions[i]; const std::string_view name = facts.string(function.name); if (!is_entrypoint_function_name(name)) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingNamePrefix, function_id, function.location); + add_diagnostic(diagnostic_set, DiagnosticCode::kMissingNamePrefix, function_id, function.location); } if (!function.has_c_linkage) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingCLinkage, function_id, function.location); + add_diagnostic(diagnostic_set, DiagnosticCode::kMissingCLinkage, function_id, function.location); } - if (function.bridge_kind == facts::BridgeKind::kUnknown) { - add_diagnostic(diagnostic_set, diagnostics::DiagnosticCode::kMissingEntrypointAttribute, function_id, function.location); + if (function.bridge_kind == BridgeKind::kUnknown) { + add_diagnostic(diagnostic_set, DiagnosticCode::kMissingEntrypointAttribute, function_id, function.location); continue; } - if (function.bridge_kind == facts::BridgeKind::kFastCGo) { + if (function.bridge_kind == BridgeKind::kFastCGo) { validate_fastcgo_function(facts, diagnostic_set, function_id, function); } } diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h index d9d8c5f22d..7642b9e8a7 100644 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.h @@ -23,7 +23,6 @@ constexpr std::string_view kResultLayoutName = "Result"; constexpr std::string_view kArgsParamName = "args"; constexpr std::string_view kResParamName = "res"; -[[nodiscard]] bool starts_with(std::string_view value, std::string_view prefix); [[nodiscard]] bool is_entrypoint_function_name(std::string_view name); [[nodiscard]] bool is_void_pointer_type(std::string_view type); @@ -31,6 +30,6 @@ constexpr std::string_view kResParamName = "res"; [[nodiscard]] facts::ParamRole param_role_for_name(std::string_view name); [[nodiscard]] std::optional layout_kind_for_name(std::string_view name); -void validate_entrypoints(const facts::FactArena& facts, diagnostics::DiagnosticSet& diagnostic_set); +void validate_contract(const facts::FactArena& facts, diagnostics::DiagnosticSet& diagnostic_set); } // namespace epgen::contract diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp index b2900ddf7f..2e7295f6b9 100644 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp @@ -1,40 +1,602 @@ #include "contract/entrypoint_contract.h" +#include #include +#include "diagnostics/diagnostics.h" +#include "facts/fact_arena.h" + +#include +#include + namespace { +using epgen::diagnostics::DiagnosticCode; +using epgen::diagnostics::DiagnosticSet; +using epgen::diagnostics::Severity; using epgen::facts::BridgeKind; +using epgen::facts::FactArena; +using epgen::facts::FunctionDecl; +using epgen::facts::LayoutDecl; using epgen::facts::LayoutKind; +using epgen::facts::ParamDecl; using epgen::facts::ParamRole; +using epgen::facts::SourceLocation; TEST(EntrypointContractTest, RecognizesPromppFunctionPrefix) { - EXPECT_TRUE(epgen::contract::is_entrypoint_function_name("prompp_fn")); - EXPECT_FALSE(epgen::contract::is_entrypoint_function_name("other_fn")); + // Arrange + + // Act + const bool entrypoint_name = epgen::contract::is_entrypoint_function_name("prompp_fn"); + const bool other_name = epgen::contract::is_entrypoint_function_name("other_fn"); + + // Assert + EXPECT_TRUE(entrypoint_name); + EXPECT_FALSE(other_name); } TEST(EntrypointContractTest, ClassifiesBridgeAnnotationNames) { - EXPECT_EQ(epgen::contract::bridge_kind_for_annotation("prompp.entrypoint.cgo"), BridgeKind::kCGo); - EXPECT_EQ(epgen::contract::bridge_kind_for_annotation("prompp.entrypoint.fastcgo"), BridgeKind::kFastCGo); - EXPECT_EQ(epgen::contract::bridge_kind_for_annotation("other"), BridgeKind::kUnknown); + // Arrange + + // Act + const BridgeKind cgo = epgen::contract::bridge_kind_for_annotation("prompp.entrypoint.cgo"); + const BridgeKind fastcgo = epgen::contract::bridge_kind_for_annotation("prompp.entrypoint.fastcgo"); + const BridgeKind unknown = epgen::contract::bridge_kind_for_annotation("other"); + + // Assert + EXPECT_EQ(cgo, BridgeKind::kCGo); + EXPECT_EQ(fastcgo, BridgeKind::kFastCGo); + EXPECT_EQ(unknown, BridgeKind::kUnknown); } TEST(EntrypointContractTest, ClassifiesParameterNames) { - EXPECT_EQ(epgen::contract::param_role_for_name("args"), ParamRole::kArgs); - EXPECT_EQ(epgen::contract::param_role_for_name("res"), ParamRole::kRes); - EXPECT_EQ(epgen::contract::param_role_for_name("other"), ParamRole::kOther); + // Arrange + + // Act + const ParamRole args = epgen::contract::param_role_for_name("args"); + const ParamRole res = epgen::contract::param_role_for_name("res"); + const ParamRole other = epgen::contract::param_role_for_name("other"); + + // Assert + EXPECT_EQ(args, ParamRole::kArgs); + EXPECT_EQ(res, ParamRole::kRes); + EXPECT_EQ(other, ParamRole::kOther); } TEST(EntrypointContractTest, ClassifiesLayoutNames) { - EXPECT_EQ(epgen::contract::layout_kind_for_name("Arguments"), LayoutKind::kArguments); - EXPECT_EQ(epgen::contract::layout_kind_for_name("Result"), LayoutKind::kResult); - EXPECT_FALSE(epgen::contract::layout_kind_for_name("Other").has_value()); + // Arrange + + // Act + const std::optional arguments = epgen::contract::layout_kind_for_name("Arguments"); + const std::optional result = epgen::contract::layout_kind_for_name("Result"); + const std::optional other = epgen::contract::layout_kind_for_name("Other"); + + // Assert + ASSERT_TRUE(arguments.has_value()); + EXPECT_EQ(*arguments, LayoutKind::kArguments); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(*result, LayoutKind::kResult); + EXPECT_FALSE(other.has_value()); } TEST(EntrypointContractTest, RecognizesVoidPointerSpellings) { - EXPECT_TRUE(epgen::contract::is_void_pointer_type("void*")); - EXPECT_TRUE(epgen::contract::is_void_pointer_type("void *")); - EXPECT_FALSE(epgen::contract::is_void_pointer_type("const void*")); + // Arrange + + // Act + const bool compact = epgen::contract::is_void_pointer_type("void*"); + const bool spaced = epgen::contract::is_void_pointer_type("void *"); + const bool const_pointer = epgen::contract::is_void_pointer_type("const void*"); + + // Assert + EXPECT_TRUE(compact); + EXPECT_TRUE(spaced); + EXPECT_FALSE(const_pointer); +} + +class ValidateContractTest : public testing::Test { + protected: + FactArena facts_; + DiagnosticSet diagnostics_; +}; + +TEST_F(ValidateContractTest, AcceptsValidCgoFunction) { + // Function layout: + // extern C cgo void prompp_fn() + // layouts: none + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + EXPECT_TRUE(diagnostics.empty()); +} + +TEST_F(ValidateContractTest, AcceptsValidFastCgoFunction) { + // Function layout: + // extern C fastcgo void prompp_fn(void* args, void* res) + // layouts: Arguments, Result + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, + }; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, + LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + EXPECT_TRUE(diagnostics.empty()); +} + +TEST_F(ValidateContractTest, ReportsMissingEntrypointPrefix) { + // Function layout: + // extern C cgo void other_fn() + // layouts: none + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const auto function_id = facts_.add_function(FunctionDecl{ + .name = facts_.add_string("other_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingNamePrefix); + EXPECT_EQ(diagnostics[0].severity, Severity::kError); + EXPECT_EQ(diagnostics[0].function, function_id); + ASSERT_TRUE(diagnostics[0].location.has_value()); + EXPECT_EQ(diagnostics[0].location->line, location.line); +} + +TEST_F(ValidateContractTest, ReportsMissingCLinkage) { + // Function layout: + // C++ linkage cgo void prompp_fn() + // layouts: none + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = false, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingCLinkage); +} + +TEST_F(ValidateContractTest, ReportsMissingEntrypointAttribute) { + // Function layout: + // extern C unknown-bridge void prompp_fn() + // layouts: none + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kUnknown, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingEntrypointAttribute); +} + +TEST_F(ValidateContractTest, ReportsMultipleDiagnosticsForOneFunction) { + // Function layout: + // C++ linkage fastcgo int other_fn(int* args) + // layouts: none + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("int*"), .role = ParamRole::kArgs, .location = location}, + }; + const auto function_id = facts_.add_function(FunctionDecl{ + .name = facts_.add_string("other_fn"), + .return_type_spelling = facts_.add_string("int"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = false, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + std::vector diagnostic_codes; + std::vector> diagnostic_functions; + + for (const auto& diagnostic : diagnostics) { + diagnostic_codes.push_back(diagnostic.code); + diagnostic_functions.push_back(diagnostic.function); + } + + // Assert + ASSERT_EQ(diagnostics.size(), 5); + EXPECT_THAT(diagnostic_codes, + testing::UnorderedElementsAre(DiagnosticCode::kMissingNamePrefix, DiagnosticCode::kMissingCLinkage, DiagnosticCode::kUnsupportedReturnType, + DiagnosticCode::kUnsupportedParamType, DiagnosticCode::kMissingArgumentsLayout)); + EXPECT_THAT(diagnostic_functions, testing::Each(std::optional(function_id))); +} + +TEST_F(ValidateContractTest, ReportsUnsupportedFastCgoReturnType) { + // Function layout: + // extern C fastcgo int prompp_fn() + // layouts: none + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("int"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnsupportedReturnType); +} + +TEST_F(ValidateContractTest, ReportsUnsupportedFastCgoParamCount) { + // Function layout: + // extern C fastcgo void prompp_fn(void* args, void* res, void* res) + // layouts: Arguments, Result + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, + }; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, + LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnsupportedParamCount); +} + +TEST_F(ValidateContractTest, ReportsUnsupportedFastCgoParamType) { + // Function layout: + // extern C fastcgo void prompp_fn(int* args) + // layouts: Arguments + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("int*"), .role = ParamRole::kArgs, .location = location}, + }; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnsupportedParamType); +} + +TEST_F(ValidateContractTest, ReportsUnknownFastCgoParamRole) { + // Function layout: + // extern C fastcgo void prompp_fn(void* other) + // layouts: none + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("other"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kOther, .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnknownParamRole); +} + +TEST_F(ValidateContractTest, ReportsInvalidTwoParamOrder) { + // Function layout: + // extern C fastcgo void prompp_fn(void* res, void* res) + // layouts: Result + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, + }; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kInvalidTwoParamOrder); +} + +TEST_F(ValidateContractTest, ReportsInvalidSecondParamRole) { + // Function layout: + // extern C fastcgo void prompp_fn(void* args, void* args) + // layouts: Arguments + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, + }; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kInvalidSecondParamRole); +} + +TEST_F(ValidateContractTest, ReportsMissingArgumentsLayout) { + // Function layout: + // extern C fastcgo void prompp_fn(void* args) + // layouts: none + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingArgumentsLayout); +} + +TEST_F(ValidateContractTest, ReportsMissingResultLayout) { + // Function layout: + // extern C fastcgo void prompp_fn(void* res) + // layouts: none + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector params{ + ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params(params), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingResultLayout); +} + +TEST_F(ValidateContractTest, ReportsUnexpectedArgumentsLayout) { + // Function layout: + // extern C fastcgo void prompp_fn() + // layouts: Arguments + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnexpectedArgumentsLayout); +} + +TEST_F(ValidateContractTest, ReportsUnexpectedResultLayout) { + // Function layout: + // extern C fastcgo void prompp_fn() + // layouts: Result + + // Arrange + const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; + const std::vector layouts{ + LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, + }; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_fn"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kFastCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts(layouts), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::contract::validate_contract(facts_, diagnostics_); + const auto diagnostics = diagnostics_.diagnostics(); + + // Assert + ASSERT_EQ(diagnostics.size(), 1); + EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnexpectedResultLayout); } } // namespace diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract_validation_tests.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_validation_tests.cpp deleted file mode 100644 index 6e3a380038..0000000000 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract_validation_tests.cpp +++ /dev/null @@ -1,430 +0,0 @@ -#include "contract/entrypoint_contract.h" - -#include - -#include "diagnostics/diagnostics.h" -#include "facts/fact_arena.h" - -#include -#include - -namespace { - -using epgen::diagnostics::DiagnosticCode; -using epgen::diagnostics::DiagnosticSet; -using epgen::diagnostics::Severity; -using epgen::facts::BridgeKind; -using epgen::facts::FactArena; -using epgen::facts::FunctionDecl; -using epgen::facts::LayoutDecl; -using epgen::facts::LayoutKind; -using epgen::facts::ParamDecl; -using epgen::facts::ParamRole; -using epgen::facts::SourceLocation; - -class ValidateEntrypointsTest : public testing::Test { - protected: - FactArena facts_; - DiagnosticSet diagnostics_; -}; - -TEST_F(ValidateEntrypointsTest, AcceptsValidCgoFunction) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kCGo, - .params = facts_.add_params({}), - .layouts = facts_.add_layouts({}), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - EXPECT_TRUE(diagnostics.empty()); -} - -TEST_F(ValidateEntrypointsTest, AcceptsValidFastCgoFunction) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - const std::vector params{ - ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, - ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, - }; - const std::vector layouts{ - LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, - LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, - }; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kFastCGo, - .params = facts_.add_params(params), - .layouts = facts_.add_layouts(layouts), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - EXPECT_TRUE(diagnostics.empty()); -} - -TEST_F(ValidateEntrypointsTest, ReportsMissingEntrypointPrefix) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - const auto function_id = facts_.add_function(FunctionDecl{ - .name = facts_.add_string("other_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kCGo, - .params = facts_.add_params({}), - .layouts = facts_.add_layouts({}), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingNamePrefix); - EXPECT_EQ(diagnostics[0].severity, Severity::kError); - EXPECT_EQ(diagnostics[0].function, function_id); - ASSERT_TRUE(diagnostics[0].location.has_value()); - EXPECT_EQ(diagnostics[0].location->line, location.line); -} - -TEST_F(ValidateEntrypointsTest, ReportsMissingCLinkage) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kCGo, - .params = facts_.add_params({}), - .layouts = facts_.add_layouts({}), - .location = location, - .has_c_linkage = false, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingCLinkage); -} - -TEST_F(ValidateEntrypointsTest, ReportsMissingEntrypointAttribute) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kUnknown, - .params = facts_.add_params({}), - .layouts = facts_.add_layouts({}), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingEntrypointAttribute); -} - -TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoReturnType) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("int"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kFastCGo, - .params = facts_.add_params({}), - .layouts = facts_.add_layouts({}), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnsupportedReturnType); -} - -TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoParamCount) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - const std::vector params{ - ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, - ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, - ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, - }; - const std::vector layouts{ - LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, - LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, - }; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kFastCGo, - .params = facts_.add_params(params), - .layouts = facts_.add_layouts(layouts), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnsupportedParamCount); -} - -TEST_F(ValidateEntrypointsTest, ReportsUnsupportedFastCgoParamType) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - const std::vector params{ - ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("int*"), .role = ParamRole::kArgs, .location = location}, - }; - const std::vector layouts{ - LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, - }; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kFastCGo, - .params = facts_.add_params(params), - .layouts = facts_.add_layouts(layouts), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnsupportedParamType); -} - -TEST_F(ValidateEntrypointsTest, ReportsUnknownFastCgoParamRole) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - const std::vector params{ - ParamDecl{.name = facts_.add_string("other"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kOther, .location = location}, - }; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kFastCGo, - .params = facts_.add_params(params), - .layouts = facts_.add_layouts({}), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnknownParamRole); -} - -TEST_F(ValidateEntrypointsTest, ReportsInvalidTwoParamOrder) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - const std::vector params{ - ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, - ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, - }; - const std::vector layouts{ - LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, - }; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kFastCGo, - .params = facts_.add_params(params), - .layouts = facts_.add_layouts(layouts), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kInvalidTwoParamOrder); -} - -TEST_F(ValidateEntrypointsTest, ReportsInvalidSecondParamRole) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - const std::vector params{ - ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, - ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, - }; - const std::vector layouts{ - LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, - }; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kFastCGo, - .params = facts_.add_params(params), - .layouts = facts_.add_layouts(layouts), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kInvalidSecondParamRole); -} - -TEST_F(ValidateEntrypointsTest, ReportsMissingArgumentsLayout) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - const std::vector params{ - ParamDecl{.name = facts_.add_string("args"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kArgs, .location = location}, - }; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kFastCGo, - .params = facts_.add_params(params), - .layouts = facts_.add_layouts({}), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingArgumentsLayout); -} - -TEST_F(ValidateEntrypointsTest, ReportsMissingResultLayout) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - const std::vector params{ - ParamDecl{.name = facts_.add_string("res"), .type_spelling = facts_.add_string("void*"), .role = ParamRole::kRes, .location = location}, - }; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kFastCGo, - .params = facts_.add_params(params), - .layouts = facts_.add_layouts({}), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingResultLayout); -} - -TEST_F(ValidateEntrypointsTest, ReportsUnexpectedArgumentsLayout) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - const std::vector layouts{ - LayoutDecl{.kind = LayoutKind::kArguments, .fields = facts_.add_fields({}), .location = location}, - }; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kFastCGo, - .params = facts_.add_params({}), - .layouts = facts_.add_layouts(layouts), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnexpectedArgumentsLayout); -} - -TEST_F(ValidateEntrypointsTest, ReportsUnexpectedResultLayout) { - // Arrange - const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 3}; - const std::vector layouts{ - LayoutDecl{.kind = LayoutKind::kResult, .fields = facts_.add_fields({}), .location = location}, - }; - facts_.add_function(FunctionDecl{ - .name = facts_.add_string("prompp_fn"), - .return_type_spelling = facts_.add_string("void"), - .documentation = facts_.add_string(""), - .bridge_kind = BridgeKind::kFastCGo, - .params = facts_.add_params({}), - .layouts = facts_.add_layouts(layouts), - .location = location, - .has_c_linkage = true, - }); - - // Act - epgen::contract::validate_entrypoints(facts_, diagnostics_); - const auto diagnostics = diagnostics_.diagnostics(); - - // Assert - ASSERT_EQ(diagnostics.size(), 1); - EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kUnexpectedResultLayout); -} - -} // namespace diff --git a/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp b/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp index e0e08a3861..9a94e8bbbc 100644 --- a/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp +++ b/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp @@ -23,7 +23,7 @@ class FactArenaTest : public testing::Test { }; TEST_F(FactArenaTest, StoresStringsAndSourceFiles) { - // // entrypoint.cpp + // Parsed source: entrypoint.cpp // void prompp_fn(); // Act @@ -38,7 +38,7 @@ TEST_F(FactArenaTest, StoresStringsAndSourceFiles) { } TEST_F(FactArenaTest, ResolvesListIdsToStoredRecords) { - // // entrypoint.cpp + // Parsed source: entrypoint.cpp // void prompp_fn(void* args, void* res); // Arrange @@ -70,7 +70,7 @@ TEST_F(FactArenaTest, ResolvesListIdsToStoredRecords) { } TEST_F(FactArenaTest, ResolvesFunctionOwnedLists) { - // // entrypoint.cpp + // Parsed source: entrypoint.cpp // void prompp_fn(void* args); // Arrange @@ -105,12 +105,13 @@ TEST_F(FactArenaTest, ResolvesFunctionOwnedLists) { ASSERT_EQ(stored_params.size(), 1); EXPECT_EQ(facts_.string(stored_params[0].name), "args"); - + ASSERT_EQ(stored_layouts.size(), 1); EXPECT_EQ(stored_layouts[0].kind, LayoutKind::kArguments); } TEST_F(FactArenaTest, MoveTransfersStoredFacts) { + // Parsed source: entrypoint.cpp // void prompp_fn(); // Arrange From c475a3497e6b6ae1cd40d3889bad64abf1634e8f Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Wed, 8 Jul 2026 14:52:06 +0000 Subject: [PATCH 22/31] rules_cc fall back --- pp/MODULE.bazel | 2 +- pp/tools/entrypoint_codegen/MODULE.bazel | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pp/MODULE.bazel b/pp/MODULE.bazel index e19e132181..0c895ab49e 100644 --- a/pp/MODULE.bazel +++ b/pp/MODULE.bazel @@ -13,7 +13,7 @@ module( # --- Bazel rule sets (from BCR) ----------------------------------------------- bazel_dep(name = "bazel_skylib", version = "1.9.0") bazel_dep(name = "platforms", version = "0.0.11") -bazel_dep(name = "rules_cc", version = "0.2.16") +bazel_dep(name = "rules_cc", version = "0.1.1") bazel_dep(name = "rules_foreign_cc", version = "0.15.1") # Local standalone component. It can be built independently from diff --git a/pp/tools/entrypoint_codegen/MODULE.bazel b/pp/tools/entrypoint_codegen/MODULE.bazel index e5a826bb2b..65063dc51b 100644 --- a/pp/tools/entrypoint_codegen/MODULE.bazel +++ b/pp/tools/entrypoint_codegen/MODULE.bazel @@ -4,7 +4,7 @@ module( ) bazel_dep(name = "platforms", version = "0.0.11") -bazel_dep(name = "rules_cc", version = "0.2.16") +bazel_dep(name = "rules_cc", version = "0.1.1") bazel_dep(name = "googletest", version = "1.15.2") From e8e40000681464e1fcf450529b97991b703bbda9 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Thu, 9 Jul 2026 13:20:13 +0000 Subject: [PATCH 23/31] review diagnostics --- pp/tools/entrypoint_codegen/app/run.cpp | 2 +- .../entrypoint_codegen/app/runtime_debug.cpp | 9 +- .../entrypoint_codegen/app/runtime_debug.h | 6 +- .../app/runtime_debug_tests.cpp | 19 ++- .../clang_adapter/clang_runtime.cpp | 3 +- .../contract/entrypoint_contract.cpp | 1 - .../contract/entrypoint_contract_tests.cpp | 8 +- .../diagnostics/diagnostics.cpp | 8 +- .../diagnostics/diagnostics.h | 14 +- .../diagnostics/diagnostics_tests.cpp | 160 ++++++++++++------ pp/tools/entrypoint_codegen/emit/report.cpp | 16 +- .../entrypoint_codegen/emit/report_tests.cpp | 18 +- .../entrypoint_codegen/facts/fact_arena.cpp | 8 + .../facts/fact_arena_tests.cpp | 26 +++ pp/tools/entrypoint_codegen/facts/facts.h | 26 ++- .../entrypoint_codegen/facts/string_table.cpp | 2 +- .../entrypoint_codegen/facts/tagged_index.h | 11 +- 17 files changed, 218 insertions(+), 119 deletions(-) diff --git a/pp/tools/entrypoint_codegen/app/run.cpp b/pp/tools/entrypoint_codegen/app/run.cpp index 4f65a53d6a..93475c858e 100644 --- a/pp/tools/entrypoint_codegen/app/run.cpp +++ b/pp/tools/entrypoint_codegen/app/run.cpp @@ -49,7 +49,7 @@ RunReport run(const RunOptions& options) { contract::validate_contract(facts, diagnostic_set); if (options.runtime.debug_diagnostics) { - append_runtime_debug_diagnostics(diagnostic_set, memory_resource.snapshot()); + append_runtime_debug_diagnostics(diagnostic_set, facts, memory_resource.snapshot()); } switch (options.output.output_mode) { diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug.cpp index 752e86fe10..66cf2543c5 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug.cpp +++ b/pp/tools/entrypoint_codegen/app/runtime_debug.cpp @@ -1,20 +1,19 @@ #include "app/runtime_debug.h" -#include +#include "facts/fact_arena.h" + #include namespace epgen::app { -void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, MemoryUsageSnapshot snapshot) { +void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, facts::FactArena& facts, MemoryUsageSnapshot snapshot) { const std::string message = "App PMR allocations: allocated=" + std::to_string(snapshot.allocated_bytes) + " deallocated=" + std::to_string(snapshot.deallocated_bytes) + " peak_live=" + std::to_string(snapshot.peak_live_bytes) + " bytes"; diagnostic_set.add(diagnostics::Diagnostic{ .code = diagnostics::DiagnosticCode::kRuntimeMemoryUsage, - .message = message, + .message = facts.add_string(message), .severity = diagnostics::Severity::kInfo, - .function = std::nullopt, - .location = std::nullopt, }); } diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.h b/pp/tools/entrypoint_codegen/app/runtime_debug.h index a3d383f375..13a818b771 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug.h +++ b/pp/tools/entrypoint_codegen/app/runtime_debug.h @@ -3,8 +3,12 @@ #include "app/memory_usage.h" #include "diagnostics/diagnostics.h" +namespace epgen::facts { +class FactArena; +} + namespace epgen::app { -void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, MemoryUsageSnapshot snapshot); +void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, facts::FactArena& facts, MemoryUsageSnapshot snapshot); } // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp index 748fd6c07e..8a287b315d 100644 --- a/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp +++ b/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp @@ -3,6 +3,7 @@ #include #include "diagnostics/diagnostics.h" +#include "facts/fact_arena.h" namespace { @@ -13,23 +14,25 @@ using epgen::diagnostics::Severity; TEST(RuntimeDebugTest, AppendsMemoryUsageDiagnostic) { // Arrange + epgen::facts::FactArena facts; DiagnosticSet diagnostics; // Act - epgen::app::append_runtime_debug_diagnostics(diagnostics, MemoryUsageSnapshot{ - .allocated_bytes = 11, - .deallocated_bytes = 7, - .peak_live_bytes = 9, - }); + epgen::app::append_runtime_debug_diagnostics(diagnostics, facts, + MemoryUsageSnapshot{ + .allocated_bytes = 11, + .deallocated_bytes = 7, + .peak_live_bytes = 9, + }); const auto diagnostic_values = diagnostics.diagnostics(); // Assert ASSERT_EQ(diagnostic_values.size(), 1); EXPECT_EQ(diagnostic_values[0].code, DiagnosticCode::kRuntimeMemoryUsage); EXPECT_EQ(diagnostic_values[0].severity, Severity::kInfo); - ASSERT_TRUE(diagnostic_values[0].message.has_value()); - EXPECT_EQ(*diagnostic_values[0].message, "App PMR allocations: allocated=11 deallocated=7 peak_live=9 bytes"); - EXPECT_FALSE(diagnostic_values[0].location.has_value()); + ASSERT_TRUE(diagnostic_values[0].message.is_valid()); + EXPECT_EQ(facts.string(diagnostic_values[0].message), "App PMR allocations: allocated=11 deallocated=7 peak_live=9 bytes"); + EXPECT_FALSE(diagnostic_values[0].location.is_valid()); } } // namespace diff --git a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp index 626e45ee7f..c4cb410de1 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp @@ -264,9 +264,8 @@ class ParseSession::Impl { owner_.diagnostics_.add(diagnostics::Diagnostic{ .code = diagnostics::DiagnosticCode::kClangDiagnostic, - .message = diagnostic_message(diagnostic), + .message = owner_.facts().add_string(diagnostic_message(diagnostic)), .severity = diagnostic_severity_for(clang_getDiagnosticSeverity(diagnostic)), - .function = std::nullopt, .location = source_location_for(clang_getDiagnosticLocation(diagnostic)), }); diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp index e84fd10ba7..dcc23e7272 100644 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract.cpp @@ -18,7 +18,6 @@ void add_diagnostic(diagnostics::DiagnosticSet& diagnostic_set, diagnostic_set.add(diagnostics::Diagnostic{ .code = code, - .message = std::nullopt, .severity = Severity::kError, .function = function_id, .location = location, diff --git a/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp index 2e7295f6b9..c59349622e 100644 --- a/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp +++ b/pp/tools/entrypoint_codegen/contract/entrypoint_contract_tests.cpp @@ -186,8 +186,8 @@ TEST_F(ValidateContractTest, ReportsMissingEntrypointPrefix) { EXPECT_EQ(diagnostics[0].code, DiagnosticCode::kMissingNamePrefix); EXPECT_EQ(diagnostics[0].severity, Severity::kError); EXPECT_EQ(diagnostics[0].function, function_id); - ASSERT_TRUE(diagnostics[0].location.has_value()); - EXPECT_EQ(diagnostics[0].location->line, location.line); + ASSERT_TRUE(diagnostics[0].location.is_valid()); + EXPECT_EQ(diagnostics[0].location.line, location.line); } TEST_F(ValidateContractTest, ReportsMissingCLinkage) { @@ -270,7 +270,7 @@ TEST_F(ValidateContractTest, ReportsMultipleDiagnosticsForOneFunction) { const auto diagnostics = diagnostics_.diagnostics(); std::vector diagnostic_codes; - std::vector> diagnostic_functions; + std::vector diagnostic_functions; for (const auto& diagnostic : diagnostics) { diagnostic_codes.push_back(diagnostic.code); @@ -282,7 +282,7 @@ TEST_F(ValidateContractTest, ReportsMultipleDiagnosticsForOneFunction) { EXPECT_THAT(diagnostic_codes, testing::UnorderedElementsAre(DiagnosticCode::kMissingNamePrefix, DiagnosticCode::kMissingCLinkage, DiagnosticCode::kUnsupportedReturnType, DiagnosticCode::kUnsupportedParamType, DiagnosticCode::kMissingArgumentsLayout)); - EXPECT_THAT(diagnostic_functions, testing::Each(std::optional(function_id))); + EXPECT_THAT(diagnostic_functions, testing::Each(function_id)); } TEST_F(ValidateContractTest, ReportsUnsupportedFastCgoReturnType) { diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp index fce4623cba..e13a163c10 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.cpp @@ -1,5 +1,7 @@ #include "diagnostics/diagnostics.h" +#include "facts/fact_arena.h" + #include namespace epgen::diagnostics { @@ -150,9 +152,9 @@ std::string_view diagnostic_default_message(DiagnosticCode code) { return "unknown diagnostic"; } -std::string_view diagnostic_message(const Diagnostic& diagnostic) { - if (diagnostic.message.has_value()) { - return *diagnostic.message; +std::string_view diagnostic_message(const facts::FactArena& facts, const Diagnostic& diagnostic) { + if (diagnostic.message.is_valid()) { + return facts.string(diagnostic.message); } return diagnostic_default_message(diagnostic.code); } diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h index 45ed50ffca..149fff5aa9 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics.h @@ -4,12 +4,14 @@ #include #include -#include #include -#include #include #include +namespace epgen::facts { +class FactArena; +} + namespace epgen::diagnostics { enum class Severity : uint8_t { @@ -38,10 +40,10 @@ enum class DiagnosticCode : uint8_t { struct Diagnostic { DiagnosticCode code; - std::optional message; + facts::StringId message{}; Severity severity; - std::optional function; - std::optional location; + facts::FunctionId function{}; + facts::SourceLocation location{}; }; struct SeverityCounts { @@ -70,7 +72,7 @@ class DiagnosticSet { [[nodiscard]] SeverityCounts count_by_severity(const DiagnosticSet& diagnostic_set); [[nodiscard]] std::string_view diagnostic_code_name(DiagnosticCode code); [[nodiscard]] std::string_view diagnostic_default_message(DiagnosticCode code); -[[nodiscard]] std::string_view diagnostic_message(const Diagnostic& diagnostic); +[[nodiscard]] std::string_view diagnostic_message(const facts::FactArena& facts, const Diagnostic& diagnostic); [[nodiscard]] std::string_view severity_name(Severity severity); } // namespace epgen::diagnostics diff --git a/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp b/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp index 675ab48057..ed1ebbfcb8 100644 --- a/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp +++ b/pp/tools/entrypoint_codegen/diagnostics/diagnostics_tests.cpp @@ -2,7 +2,8 @@ #include -#include +#include "facts/fact_arena.h" + #include namespace { @@ -11,64 +12,95 @@ using epgen::diagnostics::Diagnostic; using epgen::diagnostics::DiagnosticCode; using epgen::diagnostics::DiagnosticSet; using epgen::diagnostics::Severity; -using epgen::facts::SourceFileId; +using epgen::facts::FactArena; using epgen::facts::SourceLocation; class DiagnosticSetTest : public testing::Test { protected: + FactArena facts_; DiagnosticSet diagnostics_; }; TEST_F(DiagnosticSetTest, StartsEmpty) { - EXPECT_TRUE(diagnostics_.empty()); - EXPECT_EQ(diagnostics_.count(), 0); + // Arrange + + // Act + const bool empty = diagnostics_.empty(); + const uint32_t count = diagnostics_.count(); + + // Assert + EXPECT_TRUE(empty); + EXPECT_EQ(count, 0); } -TEST_F(DiagnosticSetTest, StoresDiagnosticsAndCountsThem) { +TEST(DiagnosticsTest, DefaultDiagnosticUsesInvalidFactReferences) { // Arrange - const SourceLocation location{.file = SourceFileId(0), .line = 3, .column = 5}; + const Diagnostic diagnostic{ + .code = DiagnosticCode::kRuntimeMemoryUsage, + .severity = Severity::kInfo, + }; + + // Act + const bool message_is_valid = diagnostic.message.is_valid(); + const bool function_is_valid = diagnostic.function.is_valid(); + const bool location_is_valid = diagnostic.location.is_valid(); + + // Assert + EXPECT_FALSE(message_is_valid); + EXPECT_FALSE(function_is_valid); + EXPECT_FALSE(location_is_valid); +} + +TEST_F(DiagnosticSetTest, StoresDiagnosticsInInsertionOrder) { + // Arrange + const SourceLocation first_location{.file = facts_.add_source_file("first.cpp"), .line = 3, .column = 5}; + const SourceLocation second_location{.file = facts_.add_source_file("second.cpp"), .line = 8, .column = 13}; // Act diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kMissingNamePrefix, - .message = std::nullopt, .severity = Severity::kError, - .function = std::nullopt, - .location = location, + .location = first_location, + }); + diagnostics_.add(Diagnostic{ + .code = DiagnosticCode::kRuntimeMemoryUsage, + .message = facts_.add_string("memory diagnostic"), + .severity = Severity::kInfo, + .location = second_location, }); const auto stored = diagnostics_.diagnostics(); // Assert - EXPECT_EQ(diagnostics_.count(), 1); - ASSERT_EQ(stored.size(), 1); + EXPECT_EQ(diagnostics_.count(), 2); + ASSERT_EQ(stored.size(), 2); + EXPECT_EQ(stored[0].code, DiagnosticCode::kMissingNamePrefix); - ASSERT_TRUE(stored[0].location.has_value()); - EXPECT_EQ(stored[0].location->line, location.line); - EXPECT_EQ(stored[0].location->column, location.column); + ASSERT_TRUE(stored[0].location.is_valid()); + EXPECT_FALSE(stored[0].message.is_valid()); + EXPECT_EQ(stored[0].location.line, first_location.line); + EXPECT_EQ(stored[0].location.column, first_location.column); + + EXPECT_EQ(stored[1].code, DiagnosticCode::kRuntimeMemoryUsage); + ASSERT_TRUE(stored[1].message.is_valid()); + EXPECT_EQ(facts_.string(stored[1].message), "memory diagnostic"); + ASSERT_TRUE(stored[1].location.is_valid()); + EXPECT_EQ(stored[1].location.line, second_location.line); + EXPECT_EQ(stored[1].location.column, second_location.column); } TEST_F(DiagnosticSetTest, CountsDiagnosticsBySeverity) { // Arrange diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kRuntimeMemoryUsage, - .message = std::nullopt, .severity = Severity::kInfo, - .function = std::nullopt, - .location = std::nullopt, }); diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kClangDiagnostic, - .message = std::nullopt, .severity = Severity::kWarning, - .function = std::nullopt, - .location = std::nullopt, }); diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kMissingNamePrefix, - .message = std::nullopt, .severity = Severity::kError, - .function = std::nullopt, - .location = std::nullopt, }); // Act @@ -82,75 +114,93 @@ TEST_F(DiagnosticSetTest, CountsDiagnosticsBySeverity) { EXPECT_TRUE(counts.has_errors()); } -TEST(DiagnosticsTest, NamesDiagnosticCode) { +TEST(DiagnosticsTest, KnownDiagnosticCodeNameDoesNotUseUnknownFallback) { + // Arrange + constexpr DiagnosticCode code = DiagnosticCode::kMissingNamePrefix; + // Act - const std::string_view code = epgen::diagnostics::diagnostic_code_name(DiagnosticCode::kMissingNamePrefix); + const std::string_view name = epgen::diagnostics::diagnostic_code_name(code); // Assert - EXPECT_EQ(code, "missing_name_prefix"); + EXPECT_FALSE(name.empty()); + EXPECT_NE(name, "unknown_diagnostic"); } -TEST(DiagnosticsTest, NamesParameterRoleDiagnosticsWithDistinctCodes) { +TEST(DiagnosticsTest, UnknownDiagnosticCodeNameUsesUnknownFallback) { + // Arrange + const auto code = static_cast(255); + // Act - const std::string_view unknown_role = epgen::diagnostics::diagnostic_code_name(DiagnosticCode::kUnknownParamRole); - const std::string_view invalid_order = epgen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidTwoParamOrder); - const std::string_view invalid_second = epgen::diagnostics::diagnostic_code_name(DiagnosticCode::kInvalidSecondParamRole); + const std::string_view name = epgen::diagnostics::diagnostic_code_name(code); // Assert - EXPECT_EQ(unknown_role, "unknown_param_role"); - EXPECT_EQ(invalid_order, "invalid_two_param_order"); - EXPECT_EQ(invalid_second, "invalid_second_param_role"); + EXPECT_EQ(name, "unknown_diagnostic"); } -TEST(DiagnosticsTest, ProvidesDefaultMessageForDiagnosticCode) { +TEST(DiagnosticsTest, KnownDiagnosticCodeHasDefaultMessage) { + // Arrange + constexpr DiagnosticCode code = DiagnosticCode::kMissingNamePrefix; + + // Act + const std::string_view message = epgen::diagnostics::diagnostic_default_message(code); + + // Assert + EXPECT_FALSE(message.empty()); + EXPECT_NE(message, "unknown diagnostic"); +} + +TEST(DiagnosticsTest, UnknownDiagnosticCodeDefaultMessageUsesUnknownFallback) { + // Arrange + const auto code = static_cast(255); + // Act - const std::string_view message = epgen::diagnostics::diagnostic_default_message(DiagnosticCode::kMissingNamePrefix); + const std::string_view message = epgen::diagnostics::diagnostic_default_message(code); // Assert - EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); + EXPECT_EQ(message, "unknown diagnostic"); } -TEST(DiagnosticsTest, UsesDiagnosticMessageWhenPresent) { +TEST(DiagnosticsTest, FallsBackToDefaultMessageWhenDiagnosticMessageIsAbsent) { // Arrange - const SourceLocation location{.file = SourceFileId(0), .line = 1, .column = 1}; + FactArena facts; const Diagnostic diagnostic{ - .code = DiagnosticCode::kClangDiagnostic, - .message = "clang says no", + .code = DiagnosticCode::kMissingNamePrefix, .severity = Severity::kError, - .function = std::nullopt, - .location = location, }; // Act - const std::string_view message = epgen::diagnostics::diagnostic_message(diagnostic); + const std::string_view message = epgen::diagnostics::diagnostic_message(facts, diagnostic); // Assert - EXPECT_EQ(message, "clang says no"); + EXPECT_EQ(message, epgen::diagnostics::diagnostic_default_message(diagnostic.code)); } -TEST(DiagnosticsTest, FallsBackToDefaultMessageWhenDiagnosticMessageIsAbsent) { +TEST(DiagnosticsTest, UsesStoredDiagnosticMessageWhenPresent) { // Arrange - const SourceLocation location{.file = SourceFileId(0), .line = 1, .column = 1}; + FactArena facts; const Diagnostic diagnostic{ - .code = DiagnosticCode::kMissingNamePrefix, - .message = std::nullopt, + .code = DiagnosticCode::kClangDiagnostic, + .message = facts.add_string("clang says no"), .severity = Severity::kError, - .function = std::nullopt, - .location = location, }; // Act - const std::string_view message = epgen::diagnostics::diagnostic_message(diagnostic); + const std::string_view message = epgen::diagnostics::diagnostic_message(facts, diagnostic); // Assert - EXPECT_EQ(message, "entrypoint function must use prompp_ prefix"); + EXPECT_EQ(message, "clang says no"); } TEST(DiagnosticsTest, NamesSeverityValues) { + // Arrange + constexpr Severity info_severity = Severity::kInfo; + constexpr Severity warning_severity = Severity::kWarning; + constexpr Severity error_severity = Severity::kError; + // Act - const std::string_view info = epgen::diagnostics::severity_name(Severity::kInfo); - const std::string_view warning = epgen::diagnostics::severity_name(Severity::kWarning); - const std::string_view error = epgen::diagnostics::severity_name(Severity::kError); + const std::string_view info = epgen::diagnostics::severity_name(info_severity); + const std::string_view warning = epgen::diagnostics::severity_name(warning_severity); + const std::string_view error = epgen::diagnostics::severity_name(error_severity); // Assert EXPECT_EQ(info, "info"); diff --git a/pp/tools/entrypoint_codegen/emit/report.cpp b/pp/tools/entrypoint_codegen/emit/report.cpp index c43e1ffa8a..b05c7270ff 100644 --- a/pp/tools/entrypoint_codegen/emit/report.cpp +++ b/pp/tools/entrypoint_codegen/emit/report.cpp @@ -195,15 +195,15 @@ void write_functions(std::ostream& out, const facts::FactArena& facts) { } void write_diagnostic_function(std::ostream& out, const facts::FactArena& facts, const diagnostics::Diagnostic& diagnostic) { - if (diagnostic.function.has_value()) { - out << "\"" << json_escape(facts.string(facts.function(*diagnostic.function).name)) << "\""; + if (diagnostic.function.is_valid()) { + out << "\"" << json_escape(facts.string(facts.function(diagnostic.function).name)) << "\""; return; } out << "null"; } void write_diagnostic(std::ostream& out, const facts::FactArena& facts, const diagnostics::Diagnostic& diagnostic) { - const std::string_view message = diagnostics::diagnostic_message(diagnostic); + const std::string_view message = diagnostics::diagnostic_message(facts, diagnostic); out << " {" << "\"code\": \"" << json_escape(diagnostics::diagnostic_code_name(diagnostic.code)) << "\", " << "\"message\": \"" << json_escape(message) << "\", " @@ -211,8 +211,8 @@ void write_diagnostic(std::ostream& out, const facts::FactArena& facts, const di << "\"function\": "; write_diagnostic_function(out, facts, diagnostic); out << ", \"location\": "; - if (diagnostic.location.has_value()) { - write_location(out, facts, *diagnostic.location); + if (diagnostic.location.is_valid()) { + write_location(out, facts, diagnostic.location); } else { out << "null"; } @@ -249,11 +249,11 @@ void write_json_report(std::ostream& out, const facts::FactArena& facts, const d void write_compiler_diagnostics(std::ostream& out, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { for (const diagnostics::Diagnostic& diagnostic : diagnostic_set.diagnostics()) { - if (diagnostic.location.has_value()) { - const facts::SourceLocation location = *diagnostic.location; + if (diagnostic.location.is_valid()) { + const facts::SourceLocation location = diagnostic.location; out << facts.string(facts.source_file(location.file).path) << ":" << location.line << ":" << location.column << ": "; } - out << diagnostics::severity_name(diagnostic.severity) << ": " << diagnostics::diagnostic_message(diagnostic) << " [" + out << diagnostics::severity_name(diagnostic.severity) << ": " << diagnostics::diagnostic_message(facts, diagnostic) << " [" << diagnostics::diagnostic_code_name(diagnostic.code) << "]\n"; } } diff --git a/pp/tools/entrypoint_codegen/emit/report_tests.cpp b/pp/tools/entrypoint_codegen/emit/report_tests.cpp index 69663bd09a..08b973cddf 100644 --- a/pp/tools/entrypoint_codegen/emit/report_tests.cpp +++ b/pp/tools/entrypoint_codegen/emit/report_tests.cpp @@ -4,7 +4,6 @@ #include "diagnostics/diagnostics.h" -#include #include #include #include @@ -89,9 +88,8 @@ TEST_F(EmitReportTest, EscapesJsonStringFieldsInOutput) { const SourceLocation location{.file = facts_.add_source_file("entry\"point.cpp"), .line = 2, .column = 4}; diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kClangDiagnostic, - .message = "line\nmessage", + .message = facts_.add_string("line\nmessage"), .severity = Severity::kError, - .function = std::nullopt, .location = location, }); @@ -115,10 +113,8 @@ TEST_F(EmitReportTest, EscapesNonWhitespaceControlCharactersInJsonStringFields) message += "after"; diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kClangDiagnostic, - .message = message, + .message = facts_.add_string(message), .severity = Severity::kError, - .function = std::nullopt, - .location = std::nullopt, }); // Act @@ -137,9 +133,7 @@ TEST_F(EmitReportTest, WritesJsonDiagnosticLocationObjectWhenPresent) { const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 9}; diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kMissingNamePrefix, - .message = std::nullopt, .severity = Severity::kError, - .function = std::nullopt, .location = location, }); @@ -156,10 +150,7 @@ TEST_F(EmitReportTest, WritesNullJsonDiagnosticLocationWhenAbsent) { // Arrange diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kRuntimeMemoryUsage, - .message = std::nullopt, .severity = Severity::kInfo, - .function = std::nullopt, - .location = std::nullopt, }); // Act @@ -176,9 +167,7 @@ TEST_F(EmitReportTest, WritesCompilerStyleDiagnosticLine) { const SourceLocation location{.file = facts_.add_source_file("entrypoint.cpp"), .line = 7, .column = 9}; diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kMissingNamePrefix, - .message = std::nullopt, .severity = Severity::kError, - .function = std::nullopt, .location = location, }); @@ -193,10 +182,7 @@ TEST_F(EmitReportTest, WritesLocationlessDiagnosticLine) { // Arrange diagnostics_.add(Diagnostic{ .code = DiagnosticCode::kRuntimeMemoryUsage, - .message = std::nullopt, .severity = Severity::kInfo, - .function = std::nullopt, - .location = std::nullopt, }); // Act diff --git a/pp/tools/entrypoint_codegen/facts/fact_arena.cpp b/pp/tools/entrypoint_codegen/facts/fact_arena.cpp index 0dd5198afc..232c04762d 100644 --- a/pp/tools/entrypoint_codegen/facts/fact_arena.cpp +++ b/pp/tools/entrypoint_codegen/facts/fact_arena.cpp @@ -43,6 +43,7 @@ class FactArena::Impl { StringId add_string(std::string_view value) { return strings_.add(value); } SourceFileId add_source_file(std::string_view path) { + assert(source_files_.size() < SourceFileId::kInvalidValue); const auto id = SourceFileId(static_cast(source_files_.size())); source_files_.push_back(SourceFileDecl{ .path = strings_.add(path), @@ -51,6 +52,8 @@ class FactArena::Impl { } ParamListId add_params(std::span params) { + assert(params_.size() < ParamId::kInvalidValue); + assert(param_lists_.size() < ParamListId::kInvalidValue); const auto begin = ParamId(static_cast(params_.size())); const auto id = ParamListId(static_cast(param_lists_.size())); params_.insert(params_.end(), params.begin(), params.end()); @@ -62,6 +65,8 @@ class FactArena::Impl { } FieldListId add_fields(std::span fields) { + assert(fields_.size() < FieldId::kInvalidValue); + assert(field_lists_.size() < FieldListId::kInvalidValue); const auto begin = FieldId(static_cast(fields_.size())); const auto id = FieldListId(static_cast(field_lists_.size())); fields_.insert(fields_.end(), fields.begin(), fields.end()); @@ -73,6 +78,8 @@ class FactArena::Impl { } LayoutListId add_layouts(std::span layouts) { + assert(layouts_.size() < LayoutId::kInvalidValue); + assert(layout_lists_.size() < LayoutListId::kInvalidValue); const auto begin = LayoutId(static_cast(layouts_.size())); const auto id = LayoutListId(static_cast(layout_lists_.size())); layouts_.insert(layouts_.end(), layouts.begin(), layouts.end()); @@ -84,6 +91,7 @@ class FactArena::Impl { } FunctionId add_function(FunctionDecl function) { + assert(functions_.size() < FunctionId::kInvalidValue); const auto id = FunctionId(static_cast(functions_.size())); functions_.push_back(function); return id; diff --git a/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp b/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp index 9a94e8bbbc..a554d94e5f 100644 --- a/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp +++ b/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp @@ -16,12 +16,35 @@ using epgen::facts::LayoutKind; using epgen::facts::ParamDecl; using epgen::facts::ParamRole; using epgen::facts::SourceLocation; +using epgen::facts::StringId; class FactArenaTest : public testing::Test { protected: FactArena facts_; }; +TEST(FactsTest, DefaultIdsAndFactsAreInvalid) { + // Arrange + const StringId string_id; + const SourceLocation location; + const epgen::facts::SourceFileDecl source_file; + const ParamDecl param; + const FieldDecl field; + const LayoutDecl layout; + const FunctionDecl function; + + // Assert + EXPECT_FALSE(string_id.is_valid()); + EXPECT_FALSE(static_cast(string_id)); + EXPECT_EQ(string_id, StringId::invalid()); + EXPECT_FALSE(location.is_valid()); + EXPECT_FALSE(source_file.is_valid()); + EXPECT_FALSE(param.is_valid()); + EXPECT_FALSE(field.is_valid()); + EXPECT_FALSE(layout.is_valid()); + EXPECT_FALSE(function.is_valid()); +} + TEST_F(FactArenaTest, StoresStringsAndSourceFiles) { // Parsed source: entrypoint.cpp // void prompp_fn(); @@ -32,6 +55,9 @@ TEST_F(FactArenaTest, StoresStringsAndSourceFiles) { const auto source_files = facts_.source_files(); // Assert + EXPECT_TRUE(string_id.is_valid()); + EXPECT_TRUE(source_file_id.is_valid()); + EXPECT_TRUE(facts_.source_file(source_file_id).is_valid()); EXPECT_EQ(facts_.string(string_id), "prompp_fn"); ASSERT_EQ(source_files.size(), 1); EXPECT_EQ(facts_.string(facts_.source_file(source_file_id).path), "entrypoint.cpp"); diff --git a/pp/tools/entrypoint_codegen/facts/facts.h b/pp/tools/entrypoint_codegen/facts/facts.h index 47a4501ef1..a4021aec8d 100644 --- a/pp/tools/entrypoint_codegen/facts/facts.h +++ b/pp/tools/entrypoint_codegen/facts/facts.h @@ -36,42 +36,56 @@ enum class LayoutKind : uint8_t { struct SourceLocation { SourceFileId file; - uint32_t line; - uint32_t column; + uint32_t line = 0; + uint32_t column = 0; + + [[nodiscard]] bool is_valid() const noexcept { return file.is_valid(); } }; struct SourceFileDecl { StringId path; + + [[nodiscard]] bool is_valid() const noexcept { return path.is_valid(); } }; struct ParamDecl { StringId name; StringId type_spelling; - ParamRole role; + ParamRole role = ParamRole::kOther; SourceLocation location; + + [[nodiscard]] bool is_valid() const noexcept { return name.is_valid() && type_spelling.is_valid() && location.is_valid(); } }; struct FieldDecl { StringId name; StringId type_spelling; SourceLocation location; + + [[nodiscard]] bool is_valid() const noexcept { return name.is_valid() && type_spelling.is_valid() && location.is_valid(); } }; struct LayoutDecl { - LayoutKind kind; + LayoutKind kind = LayoutKind::kArguments; FieldListId fields; SourceLocation location; + + [[nodiscard]] bool is_valid() const noexcept { return fields.is_valid() && location.is_valid(); } }; struct FunctionDecl { StringId name; StringId return_type_spelling; StringId documentation; - BridgeKind bridge_kind; + BridgeKind bridge_kind = BridgeKind::kUnknown; ParamListId params; LayoutListId layouts; SourceLocation location; - bool has_c_linkage; + bool has_c_linkage = false; + + [[nodiscard]] bool is_valid() const noexcept { + return name.is_valid() && return_type_spelling.is_valid() && documentation.is_valid() && params.is_valid() && layouts.is_valid() && location.is_valid(); + } }; } // namespace epgen::facts diff --git a/pp/tools/entrypoint_codegen/facts/string_table.cpp b/pp/tools/entrypoint_codegen/facts/string_table.cpp index a0089e7027..03d0e58a8c 100644 --- a/pp/tools/entrypoint_codegen/facts/string_table.cpp +++ b/pp/tools/entrypoint_codegen/facts/string_table.cpp @@ -15,7 +15,7 @@ class StringTable::Impl { StringId add(std::string_view value) { assert(value.size() <= UINT32_MAX); - assert(strings_.size() <= UINT32_MAX); + assert(strings_.size() < StringId::kInvalidValue); const auto offset = static_cast(data_.size()); const auto length = static_cast(value.size()); diff --git a/pp/tools/entrypoint_codegen/facts/tagged_index.h b/pp/tools/entrypoint_codegen/facts/tagged_index.h index 99cf274ead..b879680e7d 100644 --- a/pp/tools/entrypoint_codegen/facts/tagged_index.h +++ b/pp/tools/entrypoint_codegen/facts/tagged_index.h @@ -3,6 +3,7 @@ #include #include #include +#include namespace epgen::facts { @@ -12,16 +13,22 @@ class TaggedIndex { using tag_type = Tag; using value_type = UInt; + static constexpr UInt kInvalidValue = std::numeric_limits::max(); + constexpr TaggedIndex() noexcept = default; constexpr explicit TaggedIndex(UInt value) noexcept : value_(value) {} + [[nodiscard]] static constexpr TaggedIndex invalid() noexcept { return TaggedIndex(); } + + [[nodiscard]] constexpr bool is_valid() const noexcept { return value_ != kInvalidValue; } [[nodiscard]] constexpr UInt get() const noexcept { return value_; } constexpr explicit operator UInt() const noexcept { return value_; } + constexpr explicit operator bool() const noexcept { return is_valid(); } constexpr auto operator<=>(const TaggedIndex&) const noexcept = default; private: - UInt value_{}; + UInt value_ = kInvalidValue; }; -} // namespace epgen::facts \ No newline at end of file +} // namespace epgen::facts From 93820db8eec2ef5265531f7b3ae9b5a875b9791f Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Thu, 9 Jul 2026 14:10:03 +0000 Subject: [PATCH 24/31] i hate libclang --- pp/tools/entrypoint_codegen/BUILD.bazel | 165 ++++++++++++++--- .../clang_adapter/aggregate_source.cpp | 19 -- .../clang_adapter/aggregate_source.h | 17 -- .../clang_adapter/clang_runtime.cpp | 156 ++++++++-------- .../clang_adapter/clang_runtime.h | 59 ++++-- .../clang_adapter/clang_runtime_tests.cpp | 42 +++++ .../clang_adapter/parse.cpp | 153 +++++++--------- .../entrypoint_codegen/clang_adapter/parse.h | 12 +- .../clang_adapter/parse_options.h | 16 ++ .../clang_adapter/parse_tests.cpp | 171 +++++++----------- .../testdata/annotated_without_prefix.cpp | 1 + .../clang_adapter/testdata/batch_first.cpp | 1 + .../clang_adapter/testdata/batch_second.cpp | 1 + .../clang_adapter/testdata/c_helper.cpp | 1 + .../testdata/collision_first.cpp | 7 + .../testdata/collision_second.cpp | 7 + .../testdata/fastcgo_entrypoint.cpp | 8 + .../clang_adapter/testdata/invalid_source.cpp | 1 + .../virtual_translation_unit.cpp | 32 ++++ .../clang_adapter/virtual_translation_unit.h | 18 ++ .../virtual_translation_unit_tests.cpp | 41 +++++ 21 files changed, 568 insertions(+), 360 deletions(-) delete mode 100644 pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp delete mode 100644 pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/clang_runtime_tests.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/parse_options.h create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/testdata/annotated_without_prefix.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/testdata/batch_first.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/testdata/batch_second.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/testdata/c_helper.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/testdata/collision_first.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/testdata/collision_second.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/testdata/fastcgo_entrypoint.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/testdata/invalid_source.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit.cpp create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit.h create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit_tests.cpp diff --git a/pp/tools/entrypoint_codegen/BUILD.bazel b/pp/tools/entrypoint_codegen/BUILD.bazel index f3c44f5c2b..da4038bafb 100644 --- a/pp/tools/entrypoint_codegen/BUILD.bazel +++ b/pp/tools/entrypoint_codegen/BUILD.bazel @@ -1,56 +1,169 @@ -package(default_visibility = ["//visibility:public"]) +package(default_visibility = ["//visibility:private"]) cc_library( - name = "entrypoint_codegen_lib", - srcs = glob( - include = [ - "app/*.cpp", - "clang_adapter/*.cpp", - "contract/*.cpp", - "diagnostics/*.cpp", - "emit/*.cpp", - "facts/*.cpp", - ], - exclude = [ - "app/main.cpp", - "**/*_tests.cpp", - ], - ), - hdrs = glob([ - "app/*.h", - "clang_adapter/*.h", - "contract/*.h", - "diagnostics/*.h", - "emit/*.h", - "facts/*.h", - ]), + name = "facts", + srcs = [ + "facts/fact_arena.cpp", + "facts/string_table.cpp", + ], + hdrs = [ + "facts/fact_arena.h", + "facts/facts.h", + "facts/string_table.h", + "facts/tagged_index.h", + ], + copts = [ + "-std=c++2b", + ], +) + +cc_library( + name = "diagnostics", + srcs = ["diagnostics/diagnostics.cpp"], + hdrs = ["diagnostics/diagnostics.h"], + copts = [ + "-std=c++2b", + ], + deps = [ + ":facts", + ], +) + +cc_library( + name = "contract", + srcs = ["contract/entrypoint_contract.cpp"], + hdrs = ["contract/entrypoint_contract.h"], + copts = [ + "-std=c++2b", + ], + deps = [ + ":diagnostics", + ":facts", + ], +) + +cc_library( + name = "clang_adapter_libclang", + srcs = ["clang_adapter/clang_runtime.cpp"], + hdrs = [ + "clang_adapter/clang_runtime.h", + "clang_adapter/parse_options.h", + ], copts = [ "-std=c++2b", ], deps = [ + ":diagnostics", + ":facts", "@system_libclang//:libclang", ], ) +cc_library( + name = "clang_adapter", + srcs = [ + "clang_adapter/parse.cpp", + "clang_adapter/virtual_translation_unit.cpp", + ], + hdrs = [ + "clang_adapter/parse.h", + "clang_adapter/virtual_translation_unit.h", + ], + copts = [ + "-std=c++2b", + ], + deps = [ + ":clang_adapter_libclang", + ":contract", + ":diagnostics", + ":facts", + ], +) + +cc_library( + name = "emit", + srcs = ["emit/report.cpp"], + hdrs = ["emit/report.h"], + copts = [ + "-std=c++2b", + ], + deps = [ + ":diagnostics", + ":facts", + ], +) + +cc_library( + name = "app", + srcs = [ + "app/argparse.cpp", + "app/memory_tracking.cpp", + "app/run.cpp", + "app/runtime_debug.cpp", + ], + hdrs = [ + "app/argparse.h", + "app/memory_tracking.h", + "app/memory_usage.h", + "app/options.h", + "app/run.h", + "app/runtime_debug.h", + ], + copts = [ + "-std=c++2b", + ], + deps = [ + ":clang_adapter", + ":contract", + ":diagnostics", + ":emit", + ":facts", + ], +) + +cc_library( + name = "entrypoint_codegen_lib", + copts = [ + "-std=c++2b", + ], + visibility = ["//visibility:public"], + deps = [ + ":app", + ":clang_adapter", + ":contract", + ":diagnostics", + ":emit", + ":facts", + ], +) + cc_binary( name = "entrypoint_codegen", srcs = ["app/main.cpp"], copts = [ "-std=c++2b", ], + visibility = ["//visibility:public"], deps = [ - ":entrypoint_codegen_lib", + ":app", ], ) cc_test( name = "entrypoint_codegen_test", + data = glob(["clang_adapter/testdata/*.cpp"]), srcs = glob(["**/*_tests.cpp"]), copts = [ "-std=c++2b", ], deps = [ - ":entrypoint_codegen_lib", + ":app", + ":clang_adapter", + ":clang_adapter_libclang", + ":contract", + ":diagnostics", + ":emit", + ":facts", "@googletest//:gtest_main", ], ) diff --git a/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp b/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp deleted file mode 100644 index 7201bb5bb2..0000000000 --- a/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "clang_adapter/aggregate_source.h" - -namespace epgen::clang_adapter { - -AggregateSource build_aggregate_source(std::span source_files, std::pmr::memory_resource* memory_resource) { - AggregateSource source{ - .path = std::pmr::string("/tmp/entrypoint_codegen_aggregate.cpp", memory_resource), - .contents = std::pmr::string(memory_resource), - }; - - for (const std::filesystem::path& file : source_files) { - source.contents += "#include \""; - source.contents += file.string(); - source.contents += "\"\n"; - } - return source; -} - -} // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h b/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h deleted file mode 100644 index 82ff29fe9d..0000000000 --- a/pp/tools/entrypoint_codegen/clang_adapter/aggregate_source.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace epgen::clang_adapter { - -struct AggregateSource { - std::pmr::string path; - std::pmr::string contents; -}; - -[[nodiscard]] AggregateSource build_aggregate_source(std::span source_files, std::pmr::memory_resource* memory_resource); - -} // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp index c4cb410de1..f1e7b8df06 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp @@ -1,5 +1,7 @@ #include "clang_adapter/clang_runtime.h" +#include + #include #include #include @@ -134,6 +136,39 @@ struct SourceFile { } // namespace +class AstContext { + public: + explicit AstContext(std::pmr::memory_resource* memory_resource) : cursors_(memory_resource), types_(memory_resource) {} + + CursorView make_cursor(CXCursor cursor) { + const auto index = static_cast(cursors_.size()); + cursors_.push_back(cursor); + return CursorView(this, index); + } + + TypeView make_type(CXType type) { + const auto index = static_cast(types_.size()); + types_.push_back(type); + return TypeView(this, index); + } + + [[nodiscard]] CXCursor cx_cursor(CursorView cursor) const { + assert(cursor.context_ == this); + assert(cursor.index_ < cursors_.size()); + return cursors_[cursor.index_]; + } + + [[nodiscard]] CXType cx_type(TypeView type) const { + assert(type.context_ == this); + assert(type.index_ < types_.size()); + return types_[type.index_]; + } + + private: + std::pmr::vector cursors_; + std::pmr::vector types_; +}; + std::string normalize_path(std::string path) { if (path.empty()) { return path; @@ -163,86 +198,80 @@ std::string normalize_path(std::string path) { } std::string TypeView::spelling() const { - return clang_string_to_string(clang_getTypeSpelling(type_)); + return clang_string_to_string(clang_getTypeSpelling(context_->cx_type(*this))); } CursorView TypeView::canonical_declaration() const { - return CursorView(clang_getTypeDeclaration(clang_getCanonicalType(type_))); + return context_->make_cursor(clang_getTypeDeclaration(clang_getCanonicalType(context_->cx_type(*this)))); } std::string CursorView::spelling() const { - return clang_string_to_string(clang_getCursorSpelling(cursor_)); + return clang_string_to_string(clang_getCursorSpelling(context_->cx_cursor(*this))); } std::string CursorView::raw_comment() const { - return clang_string_to_string(clang_Cursor_getRawCommentText(cursor_)); + return clang_string_to_string(clang_Cursor_getRawCommentText(context_->cx_cursor(*this))); } TypeView CursorView::type() const { - return TypeView(clang_getCursorType(cursor_)); + return context_->make_type(clang_getCursorType(context_->cx_cursor(*this))); } TypeView CursorView::result_type() const { - return TypeView(clang_getResultType(clang_getCursorType(cursor_))); + return context_->make_type(clang_getResultType(clang_getCursorType(context_->cx_cursor(*this)))); } CursorKind CursorView::kind() const { - return cursor_kind_for(clang_getCursorKind(cursor_)); + return cursor_kind_for(clang_getCursorKind(context_->cx_cursor(*this))); } bool CursorView::is_null() const { - return clang_Cursor_isNull(cursor_); + return clang_Cursor_isNull(context_->cx_cursor(*this)); } bool CursorView::is_definition() const { - return clang_isCursorDefinition(cursor_); + return clang_isCursorDefinition(context_->cx_cursor(*this)); } bool CursorView::has_c_language() const { - return clang_getCursorLanguage(cursor_) == CXLanguage_C; + return clang_getCursorLanguage(context_->cx_cursor(*this)) == CXLanguage_C; } int CursorView::argument_count() const { - return clang_Cursor_getNumArguments(cursor_); + return clang_Cursor_getNumArguments(context_->cx_cursor(*this)); } CursorView CursorView::argument(int index) const { - return CursorView(clang_Cursor_getArgument(cursor_, index)); + return context_->make_cursor(clang_Cursor_getArgument(context_->cx_cursor(*this), index)); } -void visit_children(CursorView cursor, VisitResult (*visitor)(CursorView cursor, CursorView parent, void* data), void* data) { - std::pair state{visitor, data}; +namespace detail { + +void visit_children_impl(CursorView cursor, const ChildVisitor& visitor) { + struct VisitState { + AstContext& context; + const ChildVisitor& visitor; + } state{ + .context = *cursor.context_, + .visitor = visitor, + }; + clang_visitChildren( - cursor.cursor_, + cursor.context_->cx_cursor(cursor), [](CXCursor cursor, CXCursor parent, CXClientData data) { - auto& state = *static_cast*>(data); - return child_visit_result_for(state.first(CursorView(cursor), CursorView(parent), state.second)); + auto& state = *static_cast(data); + return child_visit_result_for(state.visitor(state.context.make_cursor(cursor), state.context.make_cursor(parent))); }, &state); } -class ParseSession::Impl { - public: - Impl(ParseSession& owner, const ParseOptions& options, const std::filesystem::path& source_file) - : owner_(owner), - source_files_(options.memory_resource), - source_file_by_handle_(options.memory_resource), - source_file_by_path_(options.memory_resource), - args_(options.memory_resource) { - if (index_.get() == nullptr) { - throw std::runtime_error("failed to create libclang index"); - } +} // namespace detail - register_input_file(source_file); - parse_translation_unit(options, source_file); - } - - Impl(ParseSession& owner, - const ParseOptions& options, - std::span source_files, - std::string_view virtual_source_path, - std::string_view virtual_source) - : owner_(owner), +class ParseSession::Impl : public AstContext { + public: + Impl(ParseSession& owner, const ParseOptions& options, VirtualParseInput input) + : AstContext(options.memory_resource), + owner_(owner), source_files_(options.memory_resource), source_file_by_handle_(options.memory_resource), source_file_by_path_(options.memory_resource), @@ -251,8 +280,8 @@ class ParseSession::Impl { throw std::runtime_error("failed to create libclang index"); } - register_input_files(source_files); - parse_translation_unit(options, virtual_source_path, virtual_source); + register_input_files(input.source_files); + parse_translation_unit(options, input.virtual_source_path, input.virtual_source); } [[nodiscard]] CXTranslationUnit translation_unit() const { return translation_unit_.get(); } @@ -294,7 +323,7 @@ class ParseSession::Impl { unsigned column = 0; unsigned offset = 0; - clang_getSpellingLocation(clang_getCursorLocation(cursor.cursor_), &file, &line, &column, &offset); + clang_getSpellingLocation(clang_getCursorLocation(cx_cursor(cursor)), &file, &line, &column, &offset); if (file == nullptr) { return false; @@ -323,22 +352,6 @@ class ParseSession::Impl { } } - void parse_translation_unit(const ParseOptions& options, const std::filesystem::path& source_file) { - args_.reserve(options.clang_args.size()); - for (const std::string& arg : options.clang_args) { - args_.push_back(arg.c_str()); - } - - const std::string source_path = std::filesystem::absolute(source_file).lexically_normal().string(); - CXTranslationUnit raw_tu = nullptr; - const CXErrorCode parse_result = clang_parseTranslationUnit2(index_.get(), source_path.c_str(), args_.data(), static_cast(args_.size()), nullptr, 0, - CXTranslationUnit_KeepGoing, &raw_tu); - if (parse_result != CXError_Success || raw_tu == nullptr) { - throw std::runtime_error("failed to parse libclang translation unit"); - } - translation_unit_.reset(raw_tu); - } - void parse_translation_unit(const ParseOptions& options, std::string_view virtual_source_path, std::string_view virtual_source) { args_.reserve(options.clang_args.size()); for (const std::string& arg : options.clang_args) { @@ -437,33 +450,12 @@ class ParseSession::Impl { ClangTranslationUnitWrapper translation_unit_; }; -ParseSession::ParseSession(const ParseOptions& options, - diagnostics::DiagnosticSet& diagnostic_set, - facts::FactArena& facts, - const std::filesystem::path& source_file) - : memory_resource_(options.memory_resource), diagnostics_(diagnostic_set), facts_(facts) { - std::pmr::polymorphic_allocator allocator(memory_resource_); - impl_ = allocator.allocate(1); - try { - allocator.construct(impl_, *this, options, source_file); - } catch (...) { - allocator.deallocate(impl_, 1); - impl_ = nullptr; - throw; - } -} - -ParseSession::ParseSession(const ParseOptions& options, - diagnostics::DiagnosticSet& diagnostic_set, - facts::FactArena& facts, - std::span source_files, - std::string_view virtual_source_path, - std::string_view virtual_source) +ParseSession::ParseSession(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set, facts::FactArena& facts, VirtualParseInput input) : memory_resource_(options.memory_resource), diagnostics_(diagnostic_set), facts_(facts) { std::pmr::polymorphic_allocator allocator(memory_resource_); impl_ = allocator.allocate(1); try { - allocator.construct(impl_, *this, options, source_files, virtual_source_path, virtual_source); + allocator.construct(impl_, *this, options, input); } catch (...) { allocator.deallocate(impl_, 1); impl_ = nullptr; @@ -481,7 +473,7 @@ ParseSession::~ParseSession() { } CursorView ParseSession::root_cursor() const { - return CursorView(clang_getTranslationUnitCursor(impl_->translation_unit())); + return impl_->make_cursor(clang_getTranslationUnitCursor(impl_->translation_unit())); } void ParseSession::add_clang_diagnostics() { @@ -489,7 +481,7 @@ void ParseSession::add_clang_diagnostics() { } facts::SourceLocation ParseSession::source_location_for(CursorView cursor) { - return impl_->source_location_for(clang_getCursorLocation(cursor.cursor_)); + return impl_->source_location_for(clang_getCursorLocation(impl_->cx_cursor(cursor))); } bool ParseSession::is_input_source_file(CursorView cursor) { diff --git a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h index 54e2595e63..c2f1f65b77 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h +++ b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.h @@ -1,22 +1,25 @@ #pragma once -#include "clang_adapter/parse.h" +#include "clang_adapter/parse_options.h" #include "diagnostics/diagnostics.h" #include "facts/fact_arena.h" #include "facts/facts.h" -#include - #include #include +#include #include #include #include #include +#include #include namespace epgen::clang_adapter { +class AstContext; +class CursorView; + enum class CursorKind : uint8_t { kOther, kFunctionDecl, @@ -32,26 +35,36 @@ enum class VisitResult : uint8_t { kRecurse, }; -[[nodiscard]] std::string normalize_path(std::string path); +namespace detail { -class CursorView; +using ChildVisitor = std::function; + +void visit_children_impl(CursorView cursor, const ChildVisitor& visitor); + +} // namespace detail + +[[nodiscard]] std::string normalize_path(std::string path); class TypeView { public: - explicit TypeView(CXType type) : type_(type) {} + TypeView() = default; [[nodiscard]] std::string spelling() const; [[nodiscard]] CursorView canonical_declaration() const; private: + friend class AstContext; friend class CursorView; - CXType type_; + TypeView(AstContext* context, uint32_t index) : context_(context), index_(index) {} + + AstContext* context_ = nullptr; + uint32_t index_ = 0; }; class CursorView { public: - explicit CursorView(CXCursor cursor) : cursor_(cursor) {} + CursorView() = default; [[nodiscard]] std::string spelling() const; [[nodiscard]] std::string raw_comment() const; @@ -65,23 +78,33 @@ class CursorView { [[nodiscard]] CursorView argument(int index) const; private: + friend class AstContext; friend class ParseSession; - friend void visit_children(CursorView cursor, VisitResult (*visitor)(CursorView cursor, CursorView parent, void* data), void* data); + friend void detail::visit_children_impl(CursorView cursor, const detail::ChildVisitor& visitor); + + CursorView(AstContext* context, uint32_t index) : context_(context), index_(index) {} - CXCursor cursor_; + AstContext* context_ = nullptr; + uint32_t index_ = 0; }; -void visit_children(CursorView cursor, VisitResult (*visitor)(CursorView cursor, CursorView parent, void* data), void* data); +template +void visit_children(CursorView cursor, Payload& payload, Callable&& callable) { + detail::ChildVisitor visitor = [&payload, fn = std::forward(callable)](CursorView child, CursorView parent) mutable { + return fn(payload, child, parent); + }; + detail::visit_children_impl(cursor, visitor); +} + +struct VirtualParseInput { + std::span source_files; + std::string_view virtual_source_path; + std::string_view virtual_source; +}; class ParseSession { public: - ParseSession(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set, facts::FactArena& facts, const std::filesystem::path& source_file); - ParseSession(const ParseOptions& options, - diagnostics::DiagnosticSet& diagnostic_set, - facts::FactArena& facts, - std::span source_files, - std::string_view virtual_source_path, - std::string_view virtual_source); + ParseSession(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set, facts::FactArena& facts, VirtualParseInput input); ~ParseSession(); ParseSession(const ParseSession&) = delete; diff --git a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime_tests.cpp b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime_tests.cpp new file mode 100644 index 0000000000..227272ef85 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime_tests.cpp @@ -0,0 +1,42 @@ +#include "clang_adapter/clang_runtime.h" + +#include + +#include + +namespace { + +TEST(ClangRuntimeTest, NormalizesExecrootPathToWorkspacePath) { + // Arrange + const std::string path = "/tmp/bazel/execroot/_main/tools/entrypoint_codegen/input.cpp"; + + // Act + const std::string normalized = epgen::clang_adapter::normalize_path(path); + + // Assert + EXPECT_EQ(normalized, "tools/entrypoint_codegen/input.cpp"); +} + +TEST(ClangRuntimeTest, KeepsExternalAbsolutePathUnchanged) { + // Arrange + const std::filesystem::path path = "/outside/workspace/input.cpp"; + + // Act + const std::string normalized = epgen::clang_adapter::normalize_path(path.string()); + + // Assert + EXPECT_EQ(normalized, path.string()); +} + +TEST(ClangRuntimeTest, KeepsRelativePathUnchanged) { + // Arrange + const std::string path = "tools/entrypoint_codegen/input.cpp"; + + // Act + const std::string normalized = epgen::clang_adapter::normalize_path(path); + + // Assert + EXPECT_EQ(normalized, path); +} + +} // namespace diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp b/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp index e7d7891c22..9fe4b846d0 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp @@ -1,7 +1,7 @@ #include "clang_adapter/parse.h" -#include "clang_adapter/aggregate_source.h" #include "clang_adapter/clang_runtime.h" +#include "clang_adapter/virtual_translation_unit.h" #include "contract/entrypoint_contract.h" #include @@ -66,22 +66,18 @@ class FunctionExtractor { facts::BridgeKind bridge_kind = facts::BridgeKind::kUnknown; } visitor_state; - visit_children( - function_cursor, - [](CursorView cursor, CursorView /*parent*/, void* data) { - if (cursor.kind() != CursorKind::kAnnotateAttr) { - return VisitResult::kContinue; - } - - auto& state = *static_cast(data); - const facts::BridgeKind next = bridge_kind_from_annotation_cursor(cursor); - if (next != facts::BridgeKind::kUnknown) { - state.bridge_kind = next; - return VisitResult::kBreak; - } - return VisitResult::kContinue; - }, - &visitor_state); + visit_children(function_cursor, visitor_state, [](BridgeVisitorState& state, CursorView cursor, CursorView /*parent*/) { + if (cursor.kind() != CursorKind::kAnnotateAttr) { + return VisitResult::kContinue; + } + + const facts::BridgeKind next = bridge_kind_from_annotation_cursor(cursor); + if (next != facts::BridgeKind::kUnknown) { + state.bridge_kind = next; + return VisitResult::kBreak; + } + return VisitResult::kContinue; + }); return visitor_state.bridge_kind; } @@ -91,21 +87,17 @@ class FunctionExtractor { bool found = false; } visitor_state; - visit_children( - function_cursor, - [](CursorView cursor, CursorView /*parent*/, void* data) { - if (cursor.kind() != CursorKind::kAnnotateAttr) { - return VisitResult::kContinue; - } - - auto& state = *static_cast(data); - if (bridge_kind_from_annotation_cursor(cursor) != facts::BridgeKind::kUnknown) { - state.found = true; - return VisitResult::kBreak; - } - return VisitResult::kContinue; - }, - &visitor_state); + visit_children(function_cursor, visitor_state, [](AnnotationVisitorState& state, CursorView cursor, CursorView /*parent*/) { + if (cursor.kind() != CursorKind::kAnnotateAttr) { + return VisitResult::kContinue; + } + + if (bridge_kind_from_annotation_cursor(cursor) != facts::BridgeKind::kUnknown) { + state.found = true; + return VisitResult::kBreak; + } + return VisitResult::kContinue; + }); return visitor_state.found; } @@ -141,16 +133,12 @@ class FunctionExtractor { .layouts = layouts, }; - visit_children( - function_cursor, - [](CursorView cursor, CursorView /*parent*/, void* data) { - auto& state = *static_cast(data); - if (state.extractor.try_append_layout(cursor, state.layouts)) { - return VisitResult::kContinue; - } - return VisitResult::kRecurse; - }, - &visitor_state); + visit_children(function_cursor, visitor_state, [](LayoutVisitorState& state, CursorView cursor, CursorView /*parent*/) { + if (state.extractor.try_append_layout(cursor, state.layouts)) { + return VisitResult::kContinue; + } + return VisitResult::kRecurse; + }); return layouts; } @@ -164,22 +152,18 @@ class FunctionExtractor { .fields = fields, }; - visit_children( - struct_cursor, - [](CursorView field, CursorView /*parent*/, void* data) { - if (field.kind() != CursorKind::kFieldDecl) { - return VisitResult::kContinue; - } - - auto& state = *static_cast(data); - state.fields.push_back(facts::FieldDecl{ - .name = state.extractor.session_.facts().add_string(field.spelling()), - .type_spelling = state.extractor.session_.facts().add_string(field.type().spelling()), - .location = state.extractor.session_.source_location_for(field), - }); - return VisitResult::kContinue; - }, - &visitor_state); + visit_children(struct_cursor, visitor_state, [](FieldVisitorState& state, CursorView field, CursorView /*parent*/) { + if (field.kind() != CursorKind::kFieldDecl) { + return VisitResult::kContinue; + } + + state.fields.push_back(facts::FieldDecl{ + .name = state.extractor.session_.facts().add_string(field.spelling()), + .type_spelling = state.extractor.session_.facts().add_string(field.type().spelling()), + .location = state.extractor.session_.source_location_for(field), + }); + return VisitResult::kContinue; + }); return fields; } @@ -194,18 +178,14 @@ class FunctionExtractor { .fields = fields, }; - visit_children( - alias_cursor, - [](CursorView child, CursorView /*parent*/, void* data) { - auto& state = *static_cast(data); - if (child.kind() == CursorKind::kStructDecl) { - state.fields = state.extractor.extract_fields(child); - state.found = true; - return VisitResult::kBreak; - } - return VisitResult::kRecurse; - }, - &visitor_state); + visit_children(alias_cursor, visitor_state, [](AliasVisitorState& state, CursorView child, CursorView /*parent*/) { + if (child.kind() == CursorKind::kStructDecl) { + state.fields = state.extractor.extract_fields(child); + state.found = true; + return VisitResult::kBreak; + } + return VisitResult::kRecurse; + }); if (visitor_state.found) { return fields; @@ -277,20 +257,16 @@ void scan_translation_unit(ParseSession& session) { .extractor = extractor, }; - visit_children( - session.root_cursor(), - [](CursorView function, CursorView /*parent*/, void* data) { - auto& state = *static_cast(data); - if (function.kind() != CursorKind::kFunctionDecl || !function.is_definition()) { - return VisitResult::kRecurse; - } - if (!state.session.is_input_source_file(function) || !state.extractor.is_candidate_function(function)) { - return VisitResult::kContinue; - } - state.extractor.add_function(function); - return VisitResult::kContinue; - }, - &scan_state); + visit_children(session.root_cursor(), scan_state, [](TranslationUnitScanState& state, CursorView function, CursorView /*parent*/) { + if (function.kind() != CursorKind::kFunctionDecl || !function.is_definition()) { + return VisitResult::kRecurse; + } + if (!state.session.is_input_source_file(function) || !state.extractor.is_candidate_function(function)) { + return VisitResult::kContinue; + } + state.extractor.add_function(function); + return VisitResult::kContinue; + }); } } // namespace @@ -301,8 +277,13 @@ facts::FactArena parse_files(const ParseOptions& options, diagnostics::Diagnosti } facts::FactArena facts(options.memory_resource); - const AggregateSource aggregate_source = build_aggregate_source(options.source_files, options.memory_resource); - ParseSession session(options, diagnostic_set, facts, options.source_files, aggregate_source.path, aggregate_source.contents); + const VirtualTranslationUnit aggregate_source = build_virtual_translation_unit(options.source_files, options.memory_resource); + ParseSession session(options, diagnostic_set, facts, + VirtualParseInput{ + .source_files = options.source_files, + .virtual_source_path = aggregate_source.path, + .virtual_source = aggregate_source.contents, + }); session.add_clang_diagnostics(); scan_translation_unit(session); diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse.h b/pp/tools/entrypoint_codegen/clang_adapter/parse.h index a9212fe822..072c4ecbd6 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse.h +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse.h @@ -1,21 +1,11 @@ #pragma once +#include "clang_adapter/parse_options.h" #include "diagnostics/diagnostics.h" #include "facts/fact_arena.h" -#include -#include -#include -#include - namespace epgen::clang_adapter { -struct ParseOptions { - std::vector source_files; - std::vector clang_args; - std::pmr::memory_resource* memory_resource = std::pmr::get_default_resource(); -}; - facts::FactArena parse_files(const ParseOptions& options, diagnostics::DiagnosticSet& diagnostic_set); } // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse_options.h b/pp/tools/entrypoint_codegen/clang_adapter/parse_options.h new file mode 100644 index 0000000000..d52fc93d9d --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse_options.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include + +namespace epgen::clang_adapter { + +struct ParseOptions { + std::vector source_files; + std::vector clang_args; + std::pmr::memory_resource* memory_resource = std::pmr::get_default_resource(); +}; + +} // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp index f5fc33ca9c..b87f053955 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -14,68 +13,83 @@ namespace { -std::filesystem::path test_tmp_dir() { - if (const char* test_tmpdir = std::getenv("TEST_TMPDIR"); test_tmpdir != nullptr) { - return test_tmpdir; +constexpr std::string_view kRunfilesRepo = "entrypoint_codegen~"; +constexpr std::string_view kRunfilesTestDataDir = "clang_adapter/testdata"; +constexpr std::string_view kSourceTreeTestDataDir = "tools/entrypoint_codegen/clang_adapter/testdata"; + +std::filesystem::path testdata_path(std::string_view name) { + const std::filesystem::path source_tree_path = std::filesystem::path(kSourceTreeTestDataDir) / name; + const char* test_srcdir = std::getenv("TEST_SRCDIR"); + if (test_srcdir == nullptr) { + return source_tree_path; + } + + const std::filesystem::path apparent_repo_path = std::filesystem::path(test_srcdir) / kRunfilesRepo / kRunfilesTestDataDir / name; + if (std::filesystem::exists(apparent_repo_path)) { + return apparent_repo_path; } - return std::filesystem::temp_directory_path(); + + const std::filesystem::path canonical_repo_path = std::filesystem::path(test_srcdir) / "_main" / "external" / kRunfilesRepo / kRunfilesTestDataDir / name; + if (std::filesystem::exists(canonical_repo_path)) { + return canonical_repo_path; + } + + return apparent_repo_path; } -std::filesystem::path write_source_file(std::string_view name, std::string_view source) { - const std::filesystem::path path = test_tmp_dir() / name; - std::ofstream out(path, std::ios::trunc); - out << source; - return path; +epgen::facts::FactArena parse_one_file(const std::filesystem::path& source_file, epgen::diagnostics::DiagnosticSet& diagnostics) { + return epgen::clang_adapter::parse_files( + epgen::clang_adapter::ParseOptions{ + .source_files = {source_file}, + .clang_args = {"-std=c++2b"}, + }, + diagnostics); +} + +epgen::facts::FactArena parse_two_files(const std::filesystem::path& first_source_file, + const std::filesystem::path& second_source_file, + epgen::diagnostics::DiagnosticSet& diagnostics) { + return epgen::clang_adapter::parse_files( + epgen::clang_adapter::ParseOptions{ + .source_files = {first_source_file, second_source_file}, + .clang_args = {"-std=c++2b"}, + }, + diagnostics); +} + +void parse_empty_input(epgen::diagnostics::DiagnosticSet& diagnostics) { + const epgen::clang_adapter::ParseOptions options; + static_cast(epgen::clang_adapter::parse_files(options, diagnostics)); } TEST(ClangAdapterParseTest, RejectsEmptyInputList) { // Arrange - const epgen::clang_adapter::ParseOptions options; epgen::diagnostics::DiagnosticSet diagnostics; // Act - - // Assert - EXPECT_THROW(epgen::clang_adapter::parse_files(options, diagnostics), std::invalid_argument); + const auto parse = [&diagnostics] { parse_empty_input(diagnostics); }; + + // Assert + EXPECT_THROW(parse(), std::invalid_argument); } TEST(ClangAdapterParseTest, ExtractsFastCgoFunctionFactsFromSourceFile) { // Arrange - const std::filesystem::path source_file = write_source_file("entrypoint_codegen_parse_test.cpp", R"cpp( - extern "C" __attribute__((annotate("prompp.entrypoint.fastcgo"))) void prompp_store(void* args, void* res) { - struct Arguments { - int series; - }; - struct Result { - double value; - }; - } - )cpp"); + const std::filesystem::path source_file = testdata_path("fastcgo_entrypoint.cpp"); epgen::diagnostics::DiagnosticSet diagnostics; // Act - epgen::facts::FactArena facts = epgen::clang_adapter::parse_files( - epgen::clang_adapter::ParseOptions{ - .source_files = {source_file}, - .clang_args = {"-std=c++2b"}, - }, - diagnostics); + epgen::facts::FactArena facts = parse_one_file(source_file, diagnostics); const auto functions = facts.functions(); - const epgen::facts::FunctionDecl* function = functions.empty() ? nullptr : &functions[0]; - const std::span params = function == nullptr ? std::span() : facts.params(function->params); - const std::span layouts = - function == nullptr ? std::span() : facts.layouts(function->layouts); - const std::span argument_fields = - layouts.empty() ? std::span() : facts.fields(layouts[0].fields); - const std::span result_fields = - layouts.size() < 2 ? std::span() : facts.fields(layouts[1].fields); // Assert ASSERT_EQ(functions.size(), 1); - ASSERT_NE(function, nullptr); - EXPECT_EQ(facts.string(function->name), "prompp_store"); - EXPECT_EQ(function->bridge_kind, epgen::facts::BridgeKind::kFastCGo); - EXPECT_TRUE(function->has_c_linkage); + const epgen::facts::FunctionDecl& function = functions[0]; + const std::span params = facts.params(function.params); + const std::span layouts = facts.layouts(function.layouts); + EXPECT_EQ(facts.string(function.name), "prompp_store"); + EXPECT_EQ(function.bridge_kind, epgen::facts::BridgeKind::kFastCGo); + EXPECT_TRUE(function.has_c_linkage); ASSERT_EQ(params.size(), 2); EXPECT_EQ(facts.string(params[0].name), "args"); EXPECT_EQ(params[0].role, epgen::facts::ParamRole::kArgs); @@ -84,6 +98,8 @@ TEST(ClangAdapterParseTest, ExtractsFastCgoFunctionFactsFromSourceFile) { ASSERT_EQ(layouts.size(), 2); EXPECT_EQ(layouts[0].kind, epgen::facts::LayoutKind::kArguments); EXPECT_EQ(layouts[1].kind, epgen::facts::LayoutKind::kResult); + const std::span argument_fields = facts.fields(layouts[0].fields); + const std::span result_fields = facts.fields(layouts[1].fields); ASSERT_EQ(argument_fields.size(), 1); EXPECT_EQ(facts.string(argument_fields[0].name), "series"); EXPECT_EQ(facts.string(argument_fields[0].type_spelling), "int"); @@ -94,16 +110,11 @@ TEST(ClangAdapterParseTest, ExtractsFastCgoFunctionFactsFromSourceFile) { TEST(ClangAdapterParseTest, RecordsClangDiagnosticsSeparately) { // Arrange - const std::filesystem::path source_file = write_source_file("entrypoint_codegen_invalid_parse_test.cpp", "int broken = ;\n"); + const std::filesystem::path source_file = testdata_path("invalid_source.cpp"); epgen::diagnostics::DiagnosticSet diagnostics; // Act - epgen::clang_adapter::parse_files( - epgen::clang_adapter::ParseOptions{ - .source_files = {source_file}, - .clang_args = {"-std=c++2b"}, - }, - diagnostics); + static_cast(parse_one_file(source_file, diagnostics)); const auto diagnostic_values = diagnostics.diagnostics(); // Assert @@ -114,21 +125,12 @@ TEST(ClangAdapterParseTest, RecordsClangDiagnosticsSeparately) { TEST(ClangAdapterParseTest, ExtractsFunctionsFromMultipleInputFiles) { // Arrange - const std::filesystem::path first_source_file = write_source_file("entrypoint_codegen_batch_first.cpp", R"cpp( - extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_first() {} - )cpp"); - const std::filesystem::path second_source_file = write_source_file("entrypoint_codegen_batch_second.cpp", R"cpp( - extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_second() {} - )cpp"); + const std::filesystem::path first_source_file = testdata_path("batch_first.cpp"); + const std::filesystem::path second_source_file = testdata_path("batch_second.cpp"); epgen::diagnostics::DiagnosticSet diagnostics; // Act - epgen::facts::FactArena facts = epgen::clang_adapter::parse_files( - epgen::clang_adapter::ParseOptions{ - .source_files = {first_source_file, second_source_file}, - .clang_args = {"-std=c++2b"}, - }, - diagnostics); + epgen::facts::FactArena facts = parse_two_files(first_source_file, second_source_file, diagnostics); const auto functions = facts.functions(); // Assert @@ -140,31 +142,12 @@ TEST(ClangAdapterParseTest, ExtractsFunctionsFromMultipleInputFiles) { TEST(ClangAdapterParseTest, ReportsAggregateTranslationUnitInternalLinkageCollisions) { // Arrange - const std::filesystem::path first_source_file = write_source_file("entrypoint_codegen_collision_first.cpp", R"cpp( - static int helper() { - return 1; - } - extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_first() { - (void)helper(); - } - )cpp"); - const std::filesystem::path second_source_file = write_source_file("entrypoint_codegen_collision_second.cpp", R"cpp( - static int helper() { - return 2; - } - extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_second() { - (void)helper(); - } - )cpp"); + const std::filesystem::path first_source_file = testdata_path("collision_first.cpp"); + const std::filesystem::path second_source_file = testdata_path("collision_second.cpp"); epgen::diagnostics::DiagnosticSet diagnostics; // Act - epgen::clang_adapter::parse_files( - epgen::clang_adapter::ParseOptions{ - .source_files = {first_source_file, second_source_file}, - .clang_args = {"-std=c++2b"}, - }, - diagnostics); + static_cast(parse_two_files(first_source_file, second_source_file, diagnostics)); const auto diagnostic_values = diagnostics.diagnostics(); // Assert @@ -175,18 +158,11 @@ TEST(ClangAdapterParseTest, ReportsAggregateTranslationUnitInternalLinkageCollis TEST(ClangAdapterParseTest, IgnoresUnannotatedExternCFunctionWithoutEntrypointPrefix) { // Arrange - const std::filesystem::path source_file = write_source_file("entrypoint_codegen_c_helper.cpp", R"cpp( - extern "C" void helper_for_c_abi() {} - )cpp"); + const std::filesystem::path source_file = testdata_path("c_helper.cpp"); epgen::diagnostics::DiagnosticSet diagnostics; // Act - epgen::facts::FactArena facts = epgen::clang_adapter::parse_files( - epgen::clang_adapter::ParseOptions{ - .source_files = {source_file}, - .clang_args = {"-std=c++2b"}, - }, - diagnostics); + epgen::facts::FactArena facts = parse_one_file(source_file, diagnostics); // Assert EXPECT_TRUE(facts.functions().empty()); @@ -194,18 +170,11 @@ TEST(ClangAdapterParseTest, IgnoresUnannotatedExternCFunctionWithoutEntrypointPr TEST(ClangAdapterParseTest, ExtractsAnnotatedFunctionWithoutEntrypointPrefix) { // Arrange - const std::filesystem::path source_file = write_source_file("entrypoint_codegen_annotated_without_prefix.cpp", R"cpp( - extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void store() {} - )cpp"); + const std::filesystem::path source_file = testdata_path("annotated_without_prefix.cpp"); epgen::diagnostics::DiagnosticSet diagnostics; // Act - epgen::facts::FactArena facts = epgen::clang_adapter::parse_files( - epgen::clang_adapter::ParseOptions{ - .source_files = {source_file}, - .clang_args = {"-std=c++2b"}, - }, - diagnostics); + epgen::facts::FactArena facts = parse_one_file(source_file, diagnostics); const auto functions = facts.functions(); // Assert diff --git a/pp/tools/entrypoint_codegen/clang_adapter/testdata/annotated_without_prefix.cpp b/pp/tools/entrypoint_codegen/clang_adapter/testdata/annotated_without_prefix.cpp new file mode 100644 index 0000000000..72c4576a30 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/testdata/annotated_without_prefix.cpp @@ -0,0 +1 @@ +extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void store() {} diff --git a/pp/tools/entrypoint_codegen/clang_adapter/testdata/batch_first.cpp b/pp/tools/entrypoint_codegen/clang_adapter/testdata/batch_first.cpp new file mode 100644 index 0000000000..ba79cda78e --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/testdata/batch_first.cpp @@ -0,0 +1 @@ +extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_first() {} diff --git a/pp/tools/entrypoint_codegen/clang_adapter/testdata/batch_second.cpp b/pp/tools/entrypoint_codegen/clang_adapter/testdata/batch_second.cpp new file mode 100644 index 0000000000..18338a7eb9 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/testdata/batch_second.cpp @@ -0,0 +1 @@ +extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_second() {} diff --git a/pp/tools/entrypoint_codegen/clang_adapter/testdata/c_helper.cpp b/pp/tools/entrypoint_codegen/clang_adapter/testdata/c_helper.cpp new file mode 100644 index 0000000000..638f5643ee --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/testdata/c_helper.cpp @@ -0,0 +1 @@ +extern "C" void helper_for_c_abi() {} diff --git a/pp/tools/entrypoint_codegen/clang_adapter/testdata/collision_first.cpp b/pp/tools/entrypoint_codegen/clang_adapter/testdata/collision_first.cpp new file mode 100644 index 0000000000..5aaaf0b2db --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/testdata/collision_first.cpp @@ -0,0 +1,7 @@ +static int helper() { + return 1; +} + +extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_first() { + (void)helper(); +} diff --git a/pp/tools/entrypoint_codegen/clang_adapter/testdata/collision_second.cpp b/pp/tools/entrypoint_codegen/clang_adapter/testdata/collision_second.cpp new file mode 100644 index 0000000000..a5fddb4885 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/testdata/collision_second.cpp @@ -0,0 +1,7 @@ +static int helper() { + return 2; +} + +extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_second() { + (void)helper(); +} diff --git a/pp/tools/entrypoint_codegen/clang_adapter/testdata/fastcgo_entrypoint.cpp b/pp/tools/entrypoint_codegen/clang_adapter/testdata/fastcgo_entrypoint.cpp new file mode 100644 index 0000000000..fced20d023 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/testdata/fastcgo_entrypoint.cpp @@ -0,0 +1,8 @@ +extern "C" __attribute__((annotate("prompp.entrypoint.fastcgo"))) void prompp_store(void* args, void* res) { + struct Arguments { + int series; + }; + struct Result { + double value; + }; +} diff --git a/pp/tools/entrypoint_codegen/clang_adapter/testdata/invalid_source.cpp b/pp/tools/entrypoint_codegen/clang_adapter/testdata/invalid_source.cpp new file mode 100644 index 0000000000..b789919871 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/testdata/invalid_source.cpp @@ -0,0 +1 @@ +int broken = ; diff --git a/pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit.cpp b/pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit.cpp new file mode 100644 index 0000000000..f770e0f677 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit.cpp @@ -0,0 +1,32 @@ +#include "clang_adapter/virtual_translation_unit.h" + +namespace epgen::clang_adapter { + +namespace { + +void append_escaped_include_path(std::pmr::string& out, std::string_view path) { + for (const char ch : path) { + if (ch == '\\' || ch == '"') { + out += '\\'; + } + out += ch; + } +} + +} // namespace + +VirtualTranslationUnit build_virtual_translation_unit(std::span source_files, std::pmr::memory_resource* memory_resource) { + VirtualTranslationUnit source{ + .path = std::pmr::string("/tmp/entrypoint_codegen_aggregate.cpp", memory_resource), + .contents = std::pmr::string(memory_resource), + }; + + for (const std::filesystem::path& file : source_files) { + source.contents += "#include \""; + append_escaped_include_path(source.contents, file.string()); + source.contents += "\"\n"; + } + return source; +} + +} // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit.h b/pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit.h new file mode 100644 index 0000000000..c56e613911 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include +#include + +namespace epgen::clang_adapter { + +struct VirtualTranslationUnit { + std::pmr::string path; + std::pmr::string contents; +}; + +[[nodiscard]] VirtualTranslationUnit build_virtual_translation_unit(std::span source_files, + std::pmr::memory_resource* memory_resource); + +} // namespace epgen::clang_adapter diff --git a/pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit_tests.cpp b/pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit_tests.cpp new file mode 100644 index 0000000000..e5470377b8 --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/virtual_translation_unit_tests.cpp @@ -0,0 +1,41 @@ +#include "clang_adapter/virtual_translation_unit.h" + +#include + +#include +#include +#include + +namespace { + +TEST(VirtualTranslationUnitTest, BuildsIncludesForSourceFilesInInputOrder) { + // Arrange + std::pmr::monotonic_buffer_resource memory_resource; + const std::array source_files{ + std::filesystem::path("first.cpp"), + std::filesystem::path("second.cpp"), + }; + + // Act + const epgen::clang_adapter::VirtualTranslationUnit unit = epgen::clang_adapter::build_virtual_translation_unit(source_files, &memory_resource); + + // Assert + EXPECT_EQ(unit.path, "/tmp/entrypoint_codegen_aggregate.cpp"); + EXPECT_EQ(unit.contents, "#include \"first.cpp\"\n#include \"second.cpp\"\n"); +} + +TEST(VirtualTranslationUnitTest, EscapesIncludePathCharacters) { + // Arrange + std::pmr::monotonic_buffer_resource memory_resource; + const std::array source_files{ + std::filesystem::path("dir/quote\"and\\slash.cpp"), + }; + + // Act + const epgen::clang_adapter::VirtualTranslationUnit unit = epgen::clang_adapter::build_virtual_translation_unit(source_files, &memory_resource); + + // Assert + EXPECT_EQ(unit.contents, "#include \"dir/quote\\\"and\\\\slash.cpp\"\n"); +} + +} // namespace From 47c5973bba558a0946818f0436e480011959a5e8 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Thu, 9 Jul 2026 14:22:51 +0000 Subject: [PATCH 25/31] emit review --- .../clang_adapter/clang_runtime.cpp | 2 +- .../entrypoint_codegen/emit/report_tests.cpp | 23 ++++++++++++++++ .../entrypoint_codegen/facts/fact_arena.cpp | 27 ++++++++++++++----- .../facts/fact_arena_tests.cpp | 21 +++++++++++++++ pp/tools/entrypoint_codegen/facts/facts.h | 3 +++ .../entrypoint_codegen/facts/string_table.cpp | 4 ++- .../facts/string_table_tests.cpp | 13 +++++++++ 7 files changed, 85 insertions(+), 8 deletions(-) diff --git a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp index f1e7b8df06..affad15c7b 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp @@ -381,7 +381,7 @@ class ParseSession::Impl : public AstContext { std::string path = path_for_file(file); if (path.empty()) { - path = ""; + path = std::string(facts::kInvalidValuePlaceholder); } if (SourceFile* source_file = find_source_file_by_path(file, path); source_file != nullptr) { return source_file->id; diff --git a/pp/tools/entrypoint_codegen/emit/report_tests.cpp b/pp/tools/entrypoint_codegen/emit/report_tests.cpp index 08b973cddf..f218f685f8 100644 --- a/pp/tools/entrypoint_codegen/emit/report_tests.cpp +++ b/pp/tools/entrypoint_codegen/emit/report_tests.cpp @@ -146,6 +146,29 @@ TEST_F(EmitReportTest, WritesJsonDiagnosticLocationObjectWhenPresent) { EXPECT_TRUE(has_location); } +TEST_F(EmitReportTest, WritesInvalidJsonLocationFileWhenSourceFileIsAbsent) { + // Arrange + const SourceLocation location{.file = epgen::facts::SourceFileId{}, .line = 7, .column = 9}; + facts_.add_function(FunctionDecl{ + .name = facts_.add_string("prompp_store"), + .return_type_spelling = facts_.add_string("void"), + .documentation = facts_.add_string(""), + .bridge_kind = BridgeKind::kCGo, + .params = facts_.add_params({}), + .layouts = facts_.add_layouts({}), + .location = location, + .has_c_linkage = true, + }); + + // Act + epgen::emit::write_report(output_, epgen::emit::ReportFormat::kJson, facts_, diagnostics_); + const std::string json = output_.str(); + const bool has_location = json.find("\"location\": {\"file\": \"\", \"line\": 7, \"column\": 9}") != std::string::npos; + + // Assert + EXPECT_TRUE(has_location); +} + TEST_F(EmitReportTest, WritesNullJsonDiagnosticLocationWhenAbsent) { // Arrange diagnostics_.add(Diagnostic{ diff --git a/pp/tools/entrypoint_codegen/facts/fact_arena.cpp b/pp/tools/entrypoint_codegen/facts/fact_arena.cpp index 232c04762d..c82444e845 100644 --- a/pp/tools/entrypoint_codegen/facts/fact_arena.cpp +++ b/pp/tools/entrypoint_codegen/facts/fact_arena.cpp @@ -31,6 +31,7 @@ class FactArena::Impl { public: explicit Impl(std::pmr::memory_resource* memory_resource) : strings_(memory_resource), + invalid_source_file_(SourceFileDecl{.path = strings_.add(kInvalidValuePlaceholder)}), source_files_(memory_resource), functions_(memory_resource), params_(memory_resource), @@ -100,12 +101,16 @@ class FactArena::Impl { [[nodiscard]] std::string_view string(StringId id) const { return strings_.get(id); } [[nodiscard]] const SourceFileDecl& source_file(SourceFileId id) const { - assert(id.get() < source_files_.size()); + if (!id.is_valid() || id.get() >= source_files_.size()) { + return invalid_source_file_; + } return source_files_[id.get()]; } [[nodiscard]] const FunctionDecl& function(FunctionId id) const { - assert(id.get() < functions_.size()); + if (!id.is_valid() || id.get() >= functions_.size()) { + return invalid_function_; + } return functions_[id.get()]; } @@ -116,7 +121,9 @@ class FactArena::Impl { [[nodiscard]] std::span params(FunctionId id) const { return params(function(id).params); } [[nodiscard]] std::span params(ParamListId id) const { - assert(id.get() < param_lists_.size()); + if (!id.is_valid() || id.get() >= param_lists_.size()) { + return {}; + } const StoredList list = param_lists_[id.get()]; return range_span(params_, list.begin.get(), list.count); } @@ -124,24 +131,32 @@ class FactArena::Impl { [[nodiscard]] std::span layouts(FunctionId id) const { return layouts(function(id).layouts); } [[nodiscard]] std::span layouts(LayoutListId id) const { - assert(id.get() < layout_lists_.size()); + if (!id.is_valid() || id.get() >= layout_lists_.size()) { + return {}; + } const StoredList list = layout_lists_[id.get()]; return range_span(layouts_, list.begin.get(), list.count); } [[nodiscard]] std::span fields(LayoutId id) const { - assert(id.get() < layouts_.size()); + if (!id.is_valid() || id.get() >= layouts_.size()) { + return {}; + } return fields(layouts_[id.get()].fields); } [[nodiscard]] std::span fields(FieldListId id) const { - assert(id.get() < field_lists_.size()); + if (!id.is_valid() || id.get() >= field_lists_.size()) { + return {}; + } const StoredList list = field_lists_[id.get()]; return range_span(fields_, list.begin.get(), list.count); } private: StringTable strings_; + SourceFileDecl invalid_source_file_; + FunctionDecl invalid_function_; std::pmr::vector source_files_; std::pmr::vector functions_; diff --git a/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp b/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp index a554d94e5f..2840bf4b46 100644 --- a/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp +++ b/pp/tools/entrypoint_codegen/facts/fact_arena_tests.cpp @@ -2,6 +2,8 @@ #include +#include +#include #include #include @@ -14,7 +16,9 @@ using epgen::facts::FunctionDecl; using epgen::facts::LayoutDecl; using epgen::facts::LayoutKind; using epgen::facts::ParamDecl; +using epgen::facts::ParamListId; using epgen::facts::ParamRole; +using epgen::facts::SourceFileId; using epgen::facts::SourceLocation; using epgen::facts::StringId; @@ -136,6 +140,23 @@ TEST_F(FactArenaTest, ResolvesFunctionOwnedLists) { EXPECT_EQ(stored_layouts[0].kind, LayoutKind::kArguments); } +TEST_F(FactArenaTest, ResolvesInvalidIdsToSafeFallbackValues) { + // Arrange + const StringId string_id; + const SourceFileId source_file_id; + const ParamListId param_list_id; + + // Act + const std::string_view string = facts_.string(string_id); + const epgen::facts::SourceFileDecl& source_file = facts_.source_file(source_file_id); + const std::span params = facts_.params(param_list_id); + + // Assert + EXPECT_EQ(string, epgen::facts::kInvalidValuePlaceholder); + EXPECT_EQ(facts_.string(source_file.path), epgen::facts::kInvalidValuePlaceholder); + EXPECT_TRUE(params.empty()); +} + TEST_F(FactArenaTest, MoveTransfersStoredFacts) { // Parsed source: entrypoint.cpp // void prompp_fn(); diff --git a/pp/tools/entrypoint_codegen/facts/facts.h b/pp/tools/entrypoint_codegen/facts/facts.h index a4021aec8d..b0568b5c70 100644 --- a/pp/tools/entrypoint_codegen/facts/facts.h +++ b/pp/tools/entrypoint_codegen/facts/facts.h @@ -3,9 +3,12 @@ #include "tagged_index.h" #include +#include namespace epgen::facts { +inline constexpr std::string_view kInvalidValuePlaceholder = ""; + using SourceFileId = TaggedIndex; using FunctionId = TaggedIndex; using LayoutId = TaggedIndex; diff --git a/pp/tools/entrypoint_codegen/facts/string_table.cpp b/pp/tools/entrypoint_codegen/facts/string_table.cpp index 03d0e58a8c..d4e373f138 100644 --- a/pp/tools/entrypoint_codegen/facts/string_table.cpp +++ b/pp/tools/entrypoint_codegen/facts/string_table.cpp @@ -35,7 +35,9 @@ class StringTable::Impl { [[nodiscard]] std::string_view get(StringId id) const { const uint32_t index = id.get(); - assert(index < strings_.size()); + if (!id.is_valid() || index >= strings_.size()) { + return kInvalidValuePlaceholder; + } return string_view_for(strings_[index]); } diff --git a/pp/tools/entrypoint_codegen/facts/string_table_tests.cpp b/pp/tools/entrypoint_codegen/facts/string_table_tests.cpp index ab5e8188a8..7e33bafa7e 100644 --- a/pp/tools/entrypoint_codegen/facts/string_table_tests.cpp +++ b/pp/tools/entrypoint_codegen/facts/string_table_tests.cpp @@ -7,6 +7,7 @@ namespace { +using epgen::facts::StringId; using epgen::facts::StringTable; TEST(StringTableTest, StartsEmpty) { @@ -62,6 +63,18 @@ TEST(StringTableTest, PreservesEmbeddedNullBytes) { EXPECT_EQ(table.get(id), std::string_view(value, sizeof(value))); } +TEST(StringTableTest, ResolvesInvalidStringIdToPlaceholder) { + // Arrange + const StringTable table; + const StringId id; + + // Act + const std::string_view value = table.get(id); + + // Assert + EXPECT_EQ(value, epgen::facts::kInvalidValuePlaceholder); +} + TEST(StringTableTest, MoveTransfersStoredStrings) { // Arrange StringTable table; From 67b6004f0366f324ad7d105119feeecf0a8df2bb Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Thu, 9 Jul 2026 15:02:05 +0000 Subject: [PATCH 26/31] app review --- pp/tools/entrypoint_codegen/BUILD.bazel | 6 +- pp/tools/entrypoint_codegen/README.md | 20 +-- pp/tools/entrypoint_codegen/app/analysis.cpp | 48 +++++++ pp/tools/entrypoint_codegen/app/analysis.h | 21 ++++ pp/tools/entrypoint_codegen/app/argparse.cpp | 110 ++++------------ .../entrypoint_codegen/app/argparse_tests.cpp | 117 ++++++------------ pp/tools/entrypoint_codegen/app/options.h | 1 - pp/tools/entrypoint_codegen/app/output.cpp | 48 +++++++ pp/tools/entrypoint_codegen/app/output.h | 17 +++ pp/tools/entrypoint_codegen/app/run.cpp | 62 +--------- pp/tools/entrypoint_codegen/app/run_tests.cpp | 75 ----------- .../entrypoint_codegen/app/runtime_debug.cpp | 20 --- .../entrypoint_codegen/app/runtime_debug.h | 14 --- .../app/runtime_debug_tests.cpp | 38 ------ 14 files changed, 212 insertions(+), 385 deletions(-) create mode 100644 pp/tools/entrypoint_codegen/app/analysis.cpp create mode 100644 pp/tools/entrypoint_codegen/app/analysis.h create mode 100644 pp/tools/entrypoint_codegen/app/output.cpp create mode 100644 pp/tools/entrypoint_codegen/app/output.h delete mode 100644 pp/tools/entrypoint_codegen/app/run_tests.cpp delete mode 100644 pp/tools/entrypoint_codegen/app/runtime_debug.cpp delete mode 100644 pp/tools/entrypoint_codegen/app/runtime_debug.h delete mode 100644 pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp diff --git a/pp/tools/entrypoint_codegen/BUILD.bazel b/pp/tools/entrypoint_codegen/BUILD.bazel index da4038bafb..f164524de8 100644 --- a/pp/tools/entrypoint_codegen/BUILD.bazel +++ b/pp/tools/entrypoint_codegen/BUILD.bazel @@ -96,18 +96,20 @@ cc_library( cc_library( name = "app", srcs = [ + "app/analysis.cpp", "app/argparse.cpp", "app/memory_tracking.cpp", + "app/output.cpp", "app/run.cpp", - "app/runtime_debug.cpp", ], hdrs = [ + "app/analysis.h", "app/argparse.h", "app/memory_tracking.h", "app/memory_usage.h", "app/options.h", + "app/output.h", "app/run.h", - "app/runtime_debug.h", ], copts = [ "-std=c++2b", diff --git a/pp/tools/entrypoint_codegen/README.md b/pp/tools/entrypoint_codegen/README.md index 36ad4eb8b4..d0bdf9580f 100644 --- a/pp/tools/entrypoint_codegen/README.md +++ b/pp/tools/entrypoint_codegen/README.md @@ -90,19 +90,7 @@ bazel run //:entrypoint_codegen -- \ -I/workspaces/prompp/pp ``` -Run as check-only: - -```bash -bazel run //:entrypoint_codegen -- \ - --mode=check \ - /workspaces/prompp/pp/entrypoint/common.cpp \ - -- \ - -std=c++2b \ - -I/workspaces/prompp/pp -``` - -Inputs may be files or directories. Directory inputs are scanned recursively for -`.cpp`, `.cc`, and `.cxx` files. +Inputs are explicit `.cpp`, `.cc`, or `.cxx` source files. ## pp Report Target @@ -132,14 +120,10 @@ the JSON output and the text log. - `--mode=json`: write structured facts and diagnostics to JSON. - `--mode=lint`: print compiler-style diagnostics to stdout. -- `--mode=check`: run analysis without writing facts or diagnostics. Useful options: -- `--output=PATH`: JSON output path. -- `--output-dir=PATH`: write `entrypoint_facts.json` in a directory. -- `--clang-arg=ARG` / `--extra-arg=ARG`: add one libclang parser argument. -- `--clang-args-file=PATH`: read parser arguments, one per line. +- `--output=PATH`: JSON output path. Required when `--mode=json`. - `--runtime-debug`: append tool-owned runtime diagnostics. - `--`: treat remaining arguments as libclang parser arguments. diff --git a/pp/tools/entrypoint_codegen/app/analysis.cpp b/pp/tools/entrypoint_codegen/app/analysis.cpp new file mode 100644 index 0000000000..183a812cfc --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/analysis.cpp @@ -0,0 +1,48 @@ +#include "app/analysis.h" + +#include "clang_adapter/parse.h" +#include "contract/entrypoint_contract.h" + +#include +#include + +namespace epgen::app { + +namespace { + +void append_runtime_memory_diagnostic(diagnostics::DiagnosticSet& diagnostic_set, facts::FactArena& facts, MemoryUsageSnapshot snapshot) { + const std::string message = "App PMR allocations: allocated=" + std::to_string(snapshot.allocated_bytes) + + " deallocated=" + std::to_string(snapshot.deallocated_bytes) + " peak_live=" + std::to_string(snapshot.peak_live_bytes) + + " bytes"; + diagnostic_set.add(diagnostics::Diagnostic{ + .code = diagnostics::DiagnosticCode::kRuntimeMemoryUsage, + .message = facts.add_string(message), + .severity = diagnostics::Severity::kInfo, + }); +} + +} // namespace + +AnalysisResult analyze_entrypoints(const AnalysisOptions& options, std::pmr::memory_resource* memory_resource) { + diagnostics::DiagnosticSet diagnostics(memory_resource); + facts::FactArena facts = clang_adapter::parse_files( + clang_adapter::ParseOptions{ + .source_files = options.source_files, + .clang_args = options.clang_args, + .memory_resource = memory_resource, + }, + diagnostics); + + contract::validate_contract(facts, diagnostics); + + return AnalysisResult{ + .facts = std::move(facts), + .diagnostics = std::move(diagnostics), + }; +} + +void append_runtime_diagnostics(AnalysisResult& result, MemoryUsageSnapshot memory_usage) { + append_runtime_memory_diagnostic(result.diagnostics, result.facts, memory_usage); +} + +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/analysis.h b/pp/tools/entrypoint_codegen/app/analysis.h new file mode 100644 index 0000000000..0637f289e4 --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/analysis.h @@ -0,0 +1,21 @@ +#pragma once + +#include "app/memory_usage.h" +#include "app/options.h" +#include "diagnostics/diagnostics.h" +#include "facts/fact_arena.h" + +#include + +namespace epgen::app { + +struct AnalysisResult { + facts::FactArena facts; + diagnostics::DiagnosticSet diagnostics; +}; + +AnalysisResult analyze_entrypoints(const AnalysisOptions& options, std::pmr::memory_resource* memory_resource); + +void append_runtime_diagnostics(AnalysisResult& result, MemoryUsageSnapshot memory_usage); + +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/argparse.cpp b/pp/tools/entrypoint_codegen/app/argparse.cpp index ad8db5e161..65163f2869 100644 --- a/pp/tools/entrypoint_codegen/app/argparse.cpp +++ b/pp/tools/entrypoint_codegen/app/argparse.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -17,25 +16,29 @@ bool has_cpp_extension(const std::filesystem::path& path) { return extension == ".cpp" || extension == ".cc" || extension == ".cxx"; } -std::vector collect_input_files(const std::vector& inputs) { +OutputMode parse_output_mode(std::string_view value) { + if (value == "json") { + return OutputMode::kJson; + } + if (value == "lint") { + return OutputMode::kLint; + } + throw std::runtime_error("unknown output mode: " + std::string(value)); +} + +std::vector collect_source_files(const std::vector& inputs) { std::vector files; for (const std::filesystem::path& input : inputs) { if (!std::filesystem::exists(input)) { throw std::runtime_error("input path does not exist: " + input.string()); } - if (std::filesystem::is_regular_file(input)) { - if (has_cpp_extension(input)) { - files.push_back(std::filesystem::absolute(input).lexically_normal()); - } - continue; + if (!std::filesystem::is_regular_file(input)) { + throw std::runtime_error("input path is not a source file: " + input.string()); } - if (std::filesystem::is_directory(input)) { - for (const auto& entry : std::filesystem::recursive_directory_iterator(input)) { - if (entry.is_regular_file() && has_cpp_extension(entry.path())) { - files.push_back(std::filesystem::absolute(entry.path()).lexically_normal()); - } - } + if (!has_cpp_extension(input)) { + throw std::runtime_error("input path is not a supported C++ source file: " + input.string()); } + files.push_back(std::filesystem::absolute(input).lexically_normal()); } std::sort(files.begin(), files.end()); @@ -43,53 +46,19 @@ std::vector collect_input_files(const std::vector& clang_args) { - std::ifstream file(path); - if (!file) { - throw std::runtime_error("unable to open clang args file: " + path.string()); - } - for (std::string line; std::getline(file, line);) { - if (!line.empty()) { - clang_args.push_back(std::move(line)); - } - } -} - -OutputMode parse_output_mode(std::string_view value) { - if (value == "json") { - return OutputMode::kJson; - } - if (value == "lint") { - return OutputMode::kLint; - } - if (value == "check" || value == "none") { - return OutputMode::kCheck; - } - throw std::runtime_error("unknown output mode: " + std::string(value)); -} - } // namespace void write_help(std::ostream& out) { - out << "entrypoint_codegen [options] [...] -- [...]\n"; - out << " --source=PATH Additional source path, file or recursive directory.\n"; - out << " --input=PATH Alias for --source=PATH.\n"; - out << " --output=PATH JSON output path. Defaults to ./entrypoint_facts.json.\n"; - out << " --output-dir=PATH Directory for entrypoint_facts.json.\n"; - out << " --mode=json|lint|check Output mode. Defaults to json.\n"; - out << " --format=json|lint|none Alias for --mode; none means check.\n"; - out << " --no-output Alias for --mode=check.\n"; + out << "entrypoint_codegen [options] [...] -- [...]\n"; + out << " --mode=json|lint Output mode. Defaults to json.\n"; + out << " --output=PATH Required JSON output path when mode is json.\n"; out << " --runtime-debug Append runtime debug diagnostics.\n"; - out << " --clang-arg=ARG Additional clang parser argument, repeatable.\n"; - out << " --extra-arg=ARG Alias for --clang-arg=ARG.\n"; - out << " --clang-args-file=PATH File with one clang parser argument per line.\n"; out << " -- Treat remaining arguments as clang parser arguments.\n"; } CliOptions parse_arguments(int argc, char** argv) { std::vector inputs; CliOptions options; - options.run_options.output.output_path = std::filesystem::current_path() / "entrypoint_facts.json"; for (int i = 1; i < argc; ++i) { const std::string arg = argv[i]; @@ -101,56 +70,31 @@ CliOptions parse_arguments(int argc, char** argv) { options.run_options.output.output_path = arg.substr(std::string("--output=").size()); continue; } - if (arg.rfind("--output-dir=", 0) == 0) { - options.run_options.output.output_path = std::filesystem::path(arg.substr(std::string("--output-dir=").size())) / "entrypoint_facts.json"; - continue; - } if (arg.rfind("--mode=", 0) == 0) { options.run_options.output.output_mode = parse_output_mode(arg.substr(std::string("--mode=").size())); continue; } - if (arg.rfind("--format=", 0) == 0) { - options.run_options.output.output_mode = parse_output_mode(arg.substr(std::string("--format=").size())); - continue; - } - if (arg == "--no-output") { - options.run_options.output.output_mode = OutputMode::kCheck; - continue; - } if (arg == "--runtime-debug") { options.run_options.runtime.debug_diagnostics = true; continue; } - if (arg.rfind("--clang-arg=", 0) == 0) { - options.run_options.analysis.clang_args.push_back(arg.substr(std::string("--clang-arg=").size())); - continue; - } - if (arg.rfind("--extra-arg=", 0) == 0) { - options.run_options.analysis.clang_args.push_back(arg.substr(std::string("--extra-arg=").size())); - continue; - } - if (arg.rfind("--clang-args-file=", 0) == 0) { - append_clang_args_file(arg.substr(std::string("--clang-args-file=").size()), options.run_options.analysis.clang_args); - continue; - } - if (arg.rfind("--source=", 0) == 0) { - inputs.emplace_back(arg.substr(std::string("--source=").size())); - continue; - } - if (arg.rfind("--input=", 0) == 0) { - inputs.emplace_back(arg.substr(std::string("--input=").size())); - continue; - } if (arg == "--") { for (++i; i < argc; ++i) { options.run_options.analysis.clang_args.emplace_back(argv[i]); } break; } + if (arg.rfind("--", 0) == 0) { + throw std::runtime_error("unknown option: " + arg); + } inputs.emplace_back(arg); } - options.run_options.analysis.source_files = collect_input_files(inputs); + options.run_options.analysis.source_files = collect_source_files(inputs); + if (options.run_options.output.output_mode == OutputMode::kJson && !options.run_options.analysis.source_files.empty() && + options.run_options.output.output_path.empty()) { + throw std::runtime_error("missing required --output for json mode"); + } return options; } diff --git a/pp/tools/entrypoint_codegen/app/argparse_tests.cpp b/pp/tools/entrypoint_codegen/app/argparse_tests.cpp index 821cc3b822..a490f4dddd 100644 --- a/pp/tools/entrypoint_codegen/app/argparse_tests.cpp +++ b/pp/tools/entrypoint_codegen/app/argparse_tests.cpp @@ -2,9 +2,7 @@ #include -#include #include -#include #include #include #include @@ -12,36 +10,28 @@ namespace { -epgen::app::CliOptions parse_args(std::initializer_list args) { - std::vector argv_storage; - argv_storage.reserve(args.size()); - for (std::string_view arg : args) { - argv_storage.emplace_back(arg); +class ArgparseTest : public testing::Test { + protected: + static epgen::app::CliOptions parse_args(std::initializer_list args) { + std::vector argv_storage; + argv_storage.reserve(args.size()); + for (std::string_view arg : args) { + argv_storage.emplace_back(arg); + } + + std::vector argv; + argv.reserve(argv_storage.size()); + for (std::string& arg : argv_storage) { + argv.push_back(arg.data()); + } + + return epgen::app::parse_arguments(static_cast(argv.size()), argv.data()); } +}; - std::vector argv; - argv.reserve(argv_storage.size()); - for (std::string& arg : argv_storage) { - argv.push_back(arg.data()); - } - - return epgen::app::parse_arguments(static_cast(argv.size()), argv.data()); -} - -std::filesystem::path test_tmp_dir() { - if (const char* test_tmpdir = std::getenv("TEST_TMPDIR"); test_tmpdir != nullptr) { - return test_tmpdir; - } - return std::filesystem::temp_directory_path(); -} - -std::filesystem::path touch_file(std::string_view name) { - const std::filesystem::path path = test_tmp_dir() / name; - std::ofstream out(path, std::ios::trunc); - return path; -} +TEST_F(ArgparseTest, ReturnsHelpForHelpFlag) { + // Arrange -TEST(ArgparseTest, ReturnsHelpForHelpFlag) { // Act const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--help"}); @@ -50,84 +40,55 @@ TEST(ArgparseTest, ReturnsHelpForHelpFlag) { EXPECT_TRUE(options.run_options.analysis.source_files.empty()); } -TEST(ArgparseTest, CollectsExistingCppInputFiles) { +TEST_F(ArgparseTest, ParsesJsonOutputPath) { // Arrange - const std::filesystem::path source_file = touch_file("entrypoint_argparse.cpp"); - touch_file("entrypoint_argparse.txt"); + const std::filesystem::path output_path = "facts.json"; // Act - const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", source_file.string()}); - const auto source_files = options.run_options.analysis.source_files; + const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--output=" + output_path.string()}); // Assert - ASSERT_EQ(source_files.size(), 1); - EXPECT_EQ(source_files[0], std::filesystem::absolute(source_file).lexically_normal()); + EXPECT_EQ(options.run_options.output.output_path, output_path); } -TEST(ArgparseTest, CollectsCppInputFilesRecursivelyFromDirectory) { +TEST_F(ArgparseTest, ParsesLintModeWithoutOutputPath) { // Arrange - const std::filesystem::path root = test_tmp_dir() / "entrypoint_argparse_recursive"; - const std::filesystem::path nested = root / "nested"; - std::filesystem::create_directories(nested); - const std::filesystem::path source_file = nested / "entrypoint_argparse_nested.cpp"; - std::ofstream out(source_file, std::ios::trunc); // Act - const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", root.string()}); - const auto source_files = options.run_options.analysis.source_files; + const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--mode=lint"}); // Assert - ASSERT_EQ(source_files.size(), 1); - EXPECT_EQ(source_files[0], std::filesystem::absolute(source_file).lexically_normal()); -} - -TEST(ArgparseTest, RejectsMissingInputPath) { - // Arrange - const std::filesystem::path missing = test_tmp_dir() / "entrypoint_argparse_missing.cpp"; - std::filesystem::remove(missing); - - // Act / Assert - EXPECT_THROW(parse_args({"entrypoint_codegen", missing.string()}), std::runtime_error); + EXPECT_EQ(options.run_options.output.output_mode, epgen::app::OutputMode::kLint); + EXPECT_TRUE(options.run_options.output.output_path.empty()); } -TEST(ArgparseTest, ParsesOutputDirectoryAsFactsFilePath) { +TEST_F(ArgparseTest, CollectsClangArgsAfterSeparator) { // Arrange - const std::filesystem::path output_dir = test_tmp_dir() / "facts"; // Act - const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--output-dir=" + output_dir.string()}); + const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--mode=lint", "--", "-std=c++2b"}); + const auto clang_args = options.run_options.analysis.clang_args; // Assert - EXPECT_EQ(options.run_options.output.output_path, output_dir / "entrypoint_facts.json"); + ASSERT_EQ(clang_args.size(), 1); + EXPECT_EQ(clang_args[0], "-std=c++2b"); } -TEST(ArgparseTest, ParsesCheckModeAlias) { - // Act - const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--no-output"}); - - // Assert - EXPECT_EQ(options.run_options.output.output_mode, epgen::app::OutputMode::kCheck); -} +TEST_F(ArgparseTest, RejectsUnknownOutputMode) { + // Arrange -TEST(ArgparseTest, CollectsClangArgsFromFlagAndSeparator) { // Act - const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--clang-arg=-I.", "--", "-std=c++2b"}); - const auto clang_args = options.run_options.analysis.clang_args; + const auto parse = [] { return parse_args({"entrypoint_codegen", "--mode=xml"}); }; // Assert - ASSERT_EQ(clang_args.size(), 2); - EXPECT_EQ(clang_args[0], "-I."); - EXPECT_EQ(clang_args[1], "-std=c++2b"); + EXPECT_THROW(parse(), std::runtime_error); } -TEST(ArgparseTest, RejectsUnknownOutputMode) { - // Act / Assert - EXPECT_THROW(parse_args({"entrypoint_codegen", "--mode=xml"}), std::runtime_error); -} +TEST_F(ArgparseTest, ParsesRuntimeDebugPolicy) { + // Arrange -TEST(ArgparseTest, ParsesRuntimeDebugPolicy) { // Act - const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--runtime-debug"}); + const epgen::app::CliOptions options = parse_args({"entrypoint_codegen", "--mode=lint", "--runtime-debug"}); // Assert EXPECT_TRUE(options.run_options.runtime.debug_diagnostics); diff --git a/pp/tools/entrypoint_codegen/app/options.h b/pp/tools/entrypoint_codegen/app/options.h index 86f252ae15..1a90990c82 100644 --- a/pp/tools/entrypoint_codegen/app/options.h +++ b/pp/tools/entrypoint_codegen/app/options.h @@ -11,7 +11,6 @@ namespace epgen::app { enum class OutputMode : uint8_t { kJson, kLint, - kCheck, }; struct AnalysisOptions { diff --git a/pp/tools/entrypoint_codegen/app/output.cpp b/pp/tools/entrypoint_codegen/app/output.cpp new file mode 100644 index 0000000000..65fc9f0281 --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/output.cpp @@ -0,0 +1,48 @@ +#include "app/output.h" + +#include "diagnostics/diagnostics.h" +#include "emit/report.h" +#include "facts/fact_arena.h" + +#include +#include +#include +#include + +namespace epgen::app { + +namespace { + +void write_json_output(const OutputOptions& options, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { + if (options.output_path.has_parent_path()) { + std::filesystem::create_directories(options.output_path.parent_path()); + } + + std::ofstream output(options.output_path, std::ios::trunc); + if (!output) { + throw std::runtime_error("failed to open output file: " + options.output_path.string()); + } + emit::write_report(output, emit::ReportFormat::kJson, facts, diagnostic_set); +} + +void write_lint_output(const OutputOptions& options, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { + std::ostream& output = options.diagnostics_output == nullptr ? std::cout : *options.diagnostics_output; + emit::write_report(output, emit::ReportFormat::kCompilerDiagnostics, facts, diagnostic_set); +} + +} // namespace + +void write_optional_output(const OutputOptions& options, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { + switch (options.output_mode) { + case OutputMode::kJson: { + write_json_output(options, facts, diagnostic_set); + break; + } + case OutputMode::kLint: { + write_lint_output(options, facts, diagnostic_set); + break; + } + } +} + +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/output.h b/pp/tools/entrypoint_codegen/app/output.h new file mode 100644 index 0000000000..e613bfed11 --- /dev/null +++ b/pp/tools/entrypoint_codegen/app/output.h @@ -0,0 +1,17 @@ +#pragma once + +#include "app/options.h" + +namespace epgen::diagnostics { +class DiagnosticSet; +} + +namespace epgen::facts { +class FactArena; +} + +namespace epgen::app { + +void write_optional_output(const OutputOptions& options, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set); + +} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/run.cpp b/pp/tools/entrypoint_codegen/app/run.cpp index 93475c858e..f985c2aab8 100644 --- a/pp/tools/entrypoint_codegen/app/run.cpp +++ b/pp/tools/entrypoint_codegen/app/run.cpp @@ -1,72 +1,22 @@ #include "app/run.h" +#include "app/analysis.h" #include "app/memory_tracking.h" -#include "app/runtime_debug.h" -#include "clang_adapter/parse.h" -#include "contract/entrypoint_contract.h" +#include "app/output.h" #include "diagnostics/diagnostics.h" -#include "emit/report.h" - -#include -#include -#include -#include namespace epgen::app { -namespace { - -void write_json_output(const OutputOptions& options, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { - if (options.output_path.has_parent_path()) { - std::filesystem::create_directories(options.output_path.parent_path()); - } - - std::ofstream output(options.output_path, std::ios::trunc); - if (!output) { - throw std::runtime_error("failed to open output file: " + options.output_path.string()); - } - emit::write_report(output, emit::ReportFormat::kJson, facts, diagnostic_set); -} - -void write_lint_output(const OutputOptions& options, const facts::FactArena& facts, const diagnostics::DiagnosticSet& diagnostic_set) { - std::ostream& output = options.diagnostics_output == nullptr ? std::cout : *options.diagnostics_output; - emit::write_report(output, emit::ReportFormat::kCompilerDiagnostics, facts, diagnostic_set); -} - -} // namespace - RunReport run(const RunOptions& options) { TrackingMemoryResource memory_resource; - diagnostics::DiagnosticSet diagnostic_set(&memory_resource); - facts::FactArena facts = clang_adapter::parse_files( - clang_adapter::ParseOptions{ - .source_files = options.analysis.source_files, - .clang_args = options.analysis.clang_args, - .memory_resource = &memory_resource, - }, - diagnostic_set); - - contract::validate_contract(facts, diagnostic_set); - + AnalysisResult analysis = analyze_entrypoints(options.analysis, &memory_resource); if (options.runtime.debug_diagnostics) { - append_runtime_debug_diagnostics(diagnostic_set, facts, memory_resource.snapshot()); + append_runtime_diagnostics(analysis, memory_resource.snapshot()); } - switch (options.output.output_mode) { - case OutputMode::kJson: { - write_json_output(options.output, facts, diagnostic_set); - break; - } - case OutputMode::kLint: { - write_lint_output(options.output, facts, diagnostic_set); - break; - } - case OutputMode::kCheck: { - break; - } - } + write_optional_output(options.output, analysis.facts, analysis.diagnostics); - const diagnostics::SeverityCounts diagnostic_counts = diagnostics::count_by_severity(diagnostic_set); + const diagnostics::SeverityCounts diagnostic_counts = diagnostics::count_by_severity(analysis.diagnostics); const MemoryUsageSnapshot memory_usage = memory_resource.snapshot(); return RunReport{ .decision = diagnostic_counts.has_errors() ? ExitDecision::kAnalysisFailed : ExitDecision::kSuccess, diff --git a/pp/tools/entrypoint_codegen/app/run_tests.cpp b/pp/tools/entrypoint_codegen/app/run_tests.cpp deleted file mode 100644 index 8f86f43784..0000000000 --- a/pp/tools/entrypoint_codegen/app/run_tests.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "app/run.h" - -#include - -#include -#include -#include -#include -#include - -namespace { - -std::filesystem::path test_tmp_dir() { - if (const char* test_tmpdir = std::getenv("TEST_TMPDIR"); test_tmpdir != nullptr) { - return test_tmpdir; - } - return std::filesystem::temp_directory_path(); -} - -std::filesystem::path write_source_file(std::string_view name, std::string_view source) { - const std::filesystem::path path = test_tmp_dir() / name; - std::ofstream out(path, std::ios::trunc); - out << source; - return path; -} - -epgen::app::RunOptions check_options_for(std::filesystem::path source_file) { - return epgen::app::RunOptions{ - .analysis = - epgen::app::AnalysisOptions{ - .source_files = {std::move(source_file)}, - .clang_args = {"-std=c++2b"}, - }, - .output = - epgen::app::OutputOptions{ - .output_path = {}, - .output_mode = epgen::app::OutputMode::kCheck, - }, - .runtime = epgen::app::RuntimeOptions{}, - }; -} - -TEST(RunTest, CheckModeSucceedsForValidAnnotatedEntrypoint) { - // Arrange - const std::filesystem::path source_file = write_source_file("entrypoint_codegen_run_valid.cpp", R"cpp( - extern "C" __attribute__((annotate("prompp.entrypoint.cgo"))) void prompp_store() {} - )cpp"); - const epgen::app::RunOptions options = check_options_for(source_file); - - // Act - const epgen::app::RunReport report = epgen::app::run(options); - - // Assert - EXPECT_EQ(report.decision, epgen::app::ExitDecision::kSuccess); - EXPECT_EQ(report.diagnostics.errors, 0); - EXPECT_EQ(report.diagnostics.total, 0); -} - -TEST(RunTest, CheckModeFailsWhenValidationReportsErrors) { - // Arrange - const std::filesystem::path source_file = write_source_file("entrypoint_codegen_run_invalid.cpp", R"cpp( - extern "C" void prompp_store() {} - )cpp"); - const epgen::app::RunOptions options = check_options_for(source_file); - - // Act - const epgen::app::RunReport report = epgen::app::run(options); - - // Assert - EXPECT_EQ(report.decision, epgen::app::ExitDecision::kAnalysisFailed); - EXPECT_EQ(report.diagnostics.errors, 1); - EXPECT_EQ(report.diagnostics.total, 1); -} - -} // namespace diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug.cpp deleted file mode 100644 index 66cf2543c5..0000000000 --- a/pp/tools/entrypoint_codegen/app/runtime_debug.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "app/runtime_debug.h" - -#include "facts/fact_arena.h" - -#include - -namespace epgen::app { - -void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, facts::FactArena& facts, MemoryUsageSnapshot snapshot) { - const std::string message = "App PMR allocations: allocated=" + std::to_string(snapshot.allocated_bytes) + - " deallocated=" + std::to_string(snapshot.deallocated_bytes) + " peak_live=" + std::to_string(snapshot.peak_live_bytes) + - " bytes"; - diagnostic_set.add(diagnostics::Diagnostic{ - .code = diagnostics::DiagnosticCode::kRuntimeMemoryUsage, - .message = facts.add_string(message), - .severity = diagnostics::Severity::kInfo, - }); -} - -} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug.h b/pp/tools/entrypoint_codegen/app/runtime_debug.h deleted file mode 100644 index 13a818b771..0000000000 --- a/pp/tools/entrypoint_codegen/app/runtime_debug.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "app/memory_usage.h" -#include "diagnostics/diagnostics.h" - -namespace epgen::facts { -class FactArena; -} - -namespace epgen::app { - -void append_runtime_debug_diagnostics(diagnostics::DiagnosticSet& diagnostic_set, facts::FactArena& facts, MemoryUsageSnapshot snapshot); - -} // namespace epgen::app diff --git a/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp b/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp deleted file mode 100644 index 8a287b315d..0000000000 --- a/pp/tools/entrypoint_codegen/app/runtime_debug_tests.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "app/runtime_debug.h" - -#include - -#include "diagnostics/diagnostics.h" -#include "facts/fact_arena.h" - -namespace { - -using epgen::app::MemoryUsageSnapshot; -using epgen::diagnostics::DiagnosticCode; -using epgen::diagnostics::DiagnosticSet; -using epgen::diagnostics::Severity; - -TEST(RuntimeDebugTest, AppendsMemoryUsageDiagnostic) { - // Arrange - epgen::facts::FactArena facts; - DiagnosticSet diagnostics; - - // Act - epgen::app::append_runtime_debug_diagnostics(diagnostics, facts, - MemoryUsageSnapshot{ - .allocated_bytes = 11, - .deallocated_bytes = 7, - .peak_live_bytes = 9, - }); - const auto diagnostic_values = diagnostics.diagnostics(); - - // Assert - ASSERT_EQ(diagnostic_values.size(), 1); - EXPECT_EQ(diagnostic_values[0].code, DiagnosticCode::kRuntimeMemoryUsage); - EXPECT_EQ(diagnostic_values[0].severity, Severity::kInfo); - ASSERT_TRUE(diagnostic_values[0].message.is_valid()); - EXPECT_EQ(facts.string(diagnostic_values[0].message), "App PMR allocations: allocated=11 deallocated=7 peak_live=9 bytes"); - EXPECT_FALSE(diagnostic_values[0].location.is_valid()); -} - -} // namespace From 6a03f125faf4a010932d25df1b37d3302f550829 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Thu, 9 Jul 2026 15:14:38 +0000 Subject: [PATCH 27/31] fix entrypoint discovery --- pp/BUILD | 13 +----- pp/bazel/rules/entrypoint_codegen.bzl | 46 ++++++++++++------- .../clang_adapter/clang_runtime.cpp | 11 ++++- .../clang_adapter/parse_tests.cpp | 17 +++++++ .../clang_adapter/testdata/warning_source.cpp | 1 + 5 files changed, 59 insertions(+), 29 deletions(-) create mode 100644 pp/tools/entrypoint_codegen/clang_adapter/testdata/warning_source.cpp diff --git a/pp/BUILD b/pp/BUILD index 7fe2bc7dd9..479fc067b1 100644 --- a/pp/BUILD +++ b/pp/BUILD @@ -211,18 +211,6 @@ cc_library( entrypoint_fact_checking( name = "entrypoint_fact_checking", - inputs = glob([ - "bare_bones/**/*.h", - "entrypoint/**/*.h", - "entrypoint/**/*.hpp", - "head/**/*.h", - "metrics/**/*.h", - "primitives/**/*.h", - "prometheus/**/*.h", - "series_data/**/*.h", - "series_index/**/*.h", - "wal/**/*.h", - ]), srcs = glob( include = ["entrypoint/**/*.cpp"], exclude = [ @@ -230,6 +218,7 @@ entrypoint_fact_checking( "entrypoint/**/*_tests.cpp", ], ), + target = ":entrypoint", ) cc_static_library( diff --git a/pp/bazel/rules/entrypoint_codegen.bzl b/pp/bazel/rules/entrypoint_codegen.bzl index 5adfbd3cf4..5e676ae1ab 100644 --- a/pp/bazel/rules/entrypoint_codegen.bzl +++ b/pp/bazel/rules/entrypoint_codegen.bzl @@ -1,31 +1,49 @@ +def _prefixed_args(flag, values): + return [flag + value for value in values] + def _path_args(flag, paths): - return [flag + path for path in paths] + return _prefixed_args(flag, [path for path in paths]) + +def _compilation_args(ctx): + target = ctx.attr.target + compilation_context = target[CcInfo].compilation_context + args = [] + args.extend(_prefixed_args("-D", compilation_context.defines.to_list())) + args.extend(_path_args("-I", compilation_context.includes.to_list())) + args.extend(_path_args("-iquote", compilation_context.quote_includes.to_list())) + args.extend(_path_args("-isystem", compilation_context.system_includes.to_list())) + args.append("-iquote.") + args.append("-iquote" + ctx.bin_dir.path) + return args def _entrypoint_fact_checking_impl(ctx): output = ctx.actions.declare_file(ctx.label.name + ".json") log = ctx.actions.declare_file(ctx.label.name + ".log") - clang_args = [] - clang_args.extend(_path_args("-I", ctx.attr.includes)) - clang_args.extend(_path_args("-iquote", ctx.attr.quote_includes)) - clang_args.extend(_path_args("-isystem", ctx.attr.system_includes)) + compilation_context = ctx.attr.target[CcInfo].compilation_context + clang_args = list(ctx.attr.clang_args) + clang_args.extend(_compilation_args(ctx)) json_args = ctx.actions.args() json_args.add("--mode=json") json_args.add("--output=" + output.path) json_args.add_all(ctx.files.srcs) json_args.add("--") - json_args.add_all(ctx.attr.clang_args) json_args.add_all(clang_args) lint_args = ctx.actions.args() lint_args.add("--mode=lint") lint_args.add_all(ctx.files.srcs) lint_args.add("--") - lint_args.add_all(ctx.attr.clang_args) lint_args.add_all(clang_args) - inputs = depset(ctx.files.srcs + ctx.files.inputs) + inputs = depset( + direct = ctx.files.srcs, + transitive = [ + compilation_context.headers, + ctx.attr.target[DefaultInfo].files, + ], + ) ctx.actions.run_shell( inputs = inputs, @@ -109,18 +127,14 @@ entrypoint_fact_checking = rule( "clang_args": attr.string_list( default = ["-std=c++2b"], ), - "includes": attr.string_list( - default = ["."], - ), - "inputs": attr.label_list( - allow_files = True, - ), - "quote_includes": attr.string_list(), - "system_includes": attr.string_list(), "srcs": attr.label_list( allow_files = [".cc", ".cpp", ".cxx"], mandatory = True, ), + "target": attr.label( + mandatory = True, + providers = [CcInfo], + ), "tool": attr.label( default = Label("@entrypoint_codegen//:entrypoint_codegen"), executable = True, diff --git a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp index affad15c7b..efdac1c865 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/clang_runtime.cpp @@ -123,6 +123,10 @@ diagnostics::Severity diagnostic_severity_for(CXDiagnosticSeverity severity) { return diagnostics::Severity::kInfo; } +bool should_report_clang_diagnostic(CXDiagnosticSeverity severity) { + return severity == CXDiagnostic_Error || severity == CXDiagnostic_Fatal; +} + enum class SourceFileOrigin : uint8_t { kInput, kDiscovered, @@ -290,11 +294,16 @@ class ParseSession::Impl : public AstContext { const unsigned count = clang_getNumDiagnostics(translation_unit_.get()); for (unsigned i = 0; i < count; ++i) { CXDiagnostic diagnostic = clang_getDiagnostic(translation_unit_.get(), i); + const CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(diagnostic); + if (!should_report_clang_diagnostic(severity)) { + clang_disposeDiagnostic(diagnostic); + continue; + } owner_.diagnostics_.add(diagnostics::Diagnostic{ .code = diagnostics::DiagnosticCode::kClangDiagnostic, .message = owner_.facts().add_string(diagnostic_message(diagnostic)), - .severity = diagnostic_severity_for(clang_getDiagnosticSeverity(diagnostic)), + .severity = diagnostic_severity_for(severity), .location = source_location_for(clang_getDiagnosticLocation(diagnostic)), }); diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp index b87f053955..58aa28b10f 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp @@ -123,6 +123,23 @@ TEST(ClangAdapterParseTest, RecordsClangDiagnosticsSeparately) { EXPECT_EQ(diagnostic_values[0].severity, epgen::diagnostics::Severity::kError); } +TEST(ClangAdapterParseTest, IgnoresClangWarningsFromSourceFile) { + // Arrange + const std::filesystem::path source_file = testdata_path("warning_source.cpp"); + epgen::diagnostics::DiagnosticSet diagnostics; + + // Act + static_cast(epgen::clang_adapter::parse_files( + epgen::clang_adapter::ParseOptions{ + .source_files = {source_file}, + .clang_args = {"-std=c++2b", "-Wall"}, + }, + diagnostics)); + + // Assert + EXPECT_TRUE(diagnostics.empty()); +} + TEST(ClangAdapterParseTest, ExtractsFunctionsFromMultipleInputFiles) { // Arrange const std::filesystem::path first_source_file = testdata_path("batch_first.cpp"); diff --git a/pp/tools/entrypoint_codegen/clang_adapter/testdata/warning_source.cpp b/pp/tools/entrypoint_codegen/clang_adapter/testdata/warning_source.cpp new file mode 100644 index 0000000000..4647754ead --- /dev/null +++ b/pp/tools/entrypoint_codegen/clang_adapter/testdata/warning_source.cpp @@ -0,0 +1 @@ +static int unused_value = 42; From 77c165dcb456012e9c47fe30f62933414e352e86 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Mon, 13 Jul 2026 12:01:26 +0000 Subject: [PATCH 28/31] fix tool --- pp/tools/entrypoint_codegen/clang_adapter/parse.cpp | 4 ---- pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp b/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp index 9fe4b846d0..3d136b2945 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse.cpp @@ -209,10 +209,6 @@ class FunctionExtractor { void append_alias_layout(CursorView alias_cursor, facts::LayoutKind kind, std::pmr::vector& layouts) { std::pmr::vector fields = extract_fields_from_alias(alias_cursor); - if (fields.empty()) { - return; - } - layouts.push_back(facts::LayoutDecl{ .kind = kind, .fields = session_.facts().add_fields(fields), diff --git a/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp index 58aa28b10f..2bb37034ee 100644 --- a/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp +++ b/pp/tools/entrypoint_codegen/clang_adapter/parse_tests.cpp @@ -29,6 +29,11 @@ std::filesystem::path testdata_path(std::string_view name) { return apparent_repo_path; } + const std::filesystem::path main_repo_path = std::filesystem::path(test_srcdir) / "_main" / kRunfilesTestDataDir / name; + if (std::filesystem::exists(main_repo_path)) { + return main_repo_path; + } + const std::filesystem::path canonical_repo_path = std::filesystem::path(test_srcdir) / "_main" / "external" / kRunfilesRepo / kRunfilesTestDataDir / name; if (std::filesystem::exists(canonical_repo_path)) { return canonical_repo_path; From 95f4592c6fcf6c4c2ffe14308dd371aac36310d6 Mon Sep 17 00:00:00 2001 From: Gleb Shigin Date: Mon, 13 Jul 2026 12:27:58 +0000 Subject: [PATCH 29/31] annotate entrypoint --- pp/BUILD | 7 +- pp/bare_bones/preprocess.h | 3 + pp/entrypoint/bridge/annotations.h | 8 + pp/entrypoint/bridge/common.cpp | 34 +- pp/entrypoint/bridge/head_status.cpp | 74 ++- pp/entrypoint/bridge/head_wal.cpp | 136 +++++- pp/entrypoint/bridge/index_writer.cpp | 152 +++++- pp/entrypoint/bridge/label_set.cpp | 122 ++++- pp/entrypoint/bridge/metrics.cpp | 54 ++- pp/entrypoint/bridge/primitives_lss.cpp | 311 ++++++++++-- pp/entrypoint/bridge/prometheus_relabeler.cpp | 454 ++++++++++++++++-- pp/entrypoint/bridge/remote_write.cpp | 46 +- .../bridge/series_data_data_storage.cpp | 340 +++++++++++-- pp/entrypoint/bridge/series_data_encoder.cpp | 54 ++- ...ies_data_serialization_serialized_data.cpp | 72 ++- pp/entrypoint/bridge/wal_decoder.cpp | 248 +++++++++- pp/entrypoint/bridge/wal_encoder.cpp | 176 ++++++- pp/entrypoint/bridge/wal_hashdex.cpp | 244 +++++++++- 18 files changed, 2332 insertions(+), 203 deletions(-) create mode 100644 pp/entrypoint/bridge/annotations.h diff --git a/pp/BUILD b/pp/BUILD index 89032e4e35..46d522dc03 100644 --- a/pp/BUILD +++ b/pp/BUILD @@ -223,6 +223,11 @@ cc_library( "entrypoint/bridge/**/*.hpp", ]), linkstatic = True, + # GCC warns about the libclang-only annotate attribute; allow it to ignore + # that unknown attribute while preserving the annotation for fact checking. + copts = [ + "-Wno-attributes", + ], deps = [ ":bare_bones", ":entrypoint_types", @@ -245,7 +250,7 @@ entrypoint_fact_checking( "entrypoint/**/*_tests.cpp", ], ), - target = ":entrypoint", + target = ":entrypoint_bridge", ) cc_static_library( diff --git a/pp/bare_bones/preprocess.h b/pp/bare_bones/preprocess.h index b9d21a1801..1eff58af95 100644 --- a/pp/bare_bones/preprocess.h +++ b/pp/bare_bones/preprocess.h @@ -1,5 +1,8 @@ #pragma once +// Dispatch a PromPP macro category to a function-like implementation. +#define PROMPP(category, ...) PROMPP_DETAIL_##category(__VA_ARGS__) + #define PROMPP_ATTRIBUTE_INLINE __attribute__((always_inline)) #define PROMPP_ATTRIBUTE_NOINLINE __attribute__((noinline)) diff --git a/pp/entrypoint/bridge/annotations.h b/pp/entrypoint/bridge/annotations.h new file mode 100644 index 0000000000..89ce24b34a --- /dev/null +++ b/pp/entrypoint/bridge/annotations.h @@ -0,0 +1,8 @@ +#pragma once + +#include "bare_bones/preprocess.h" + +#define PROMPP_DETAIL_entrypoint(kind) PROMPP_DETAIL_entrypoint_##kind() + +#define PROMPP_DETAIL_entrypoint_cgo() __attribute__((annotate("prompp.entrypoint.cgo"))) +#define PROMPP_DETAIL_entrypoint_fastcgo() __attribute__((annotate("prompp.entrypoint.fastcgo"))) diff --git a/pp/entrypoint/bridge/common.cpp b/pp/entrypoint/bridge/common.cpp index 5641e408c8..a380cbf3e3 100644 --- a/pp/entrypoint/bridge/common.cpp +++ b/pp/entrypoint/bridge/common.cpp @@ -1,4 +1,5 @@ #include "common.h" +#include "annotations.h" #include "bare_bones/jemalloc.h" #if !JEMALLOC_AVAILABLE @@ -7,21 +8,34 @@ #include "primitives/go_slice.h" -extern "C" void prompp_free_bytes(void* args) { +/** + * @brief Free memory allocated for response as []byte + * + * @param args *[]byte + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_free_bytes(void* args) { using Slice = PromPP::Primitives::Go::Slice; + using Arguments = Slice; - static_cast(args)->~Slice(); + static_cast(args)->~Slice(); } extern "C" void je_jemalloc_constructor(void); -extern "C" void prompp_jemalloc_init() { +extern "C" PROMPP(entrypoint, fastcgo) void prompp_jemalloc_init() { #if JEMALLOC_AVAILABLE je_jemalloc_constructor(); #endif } -extern "C" void prompp_mem_info(void* res) { +/** + * @brief Return information about using memory by core + * + * @param res { + * in_use uint64 // bytes in use + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_mem_info(void* res) { struct Result { int64_t in_use; int64_t allocated; @@ -47,7 +61,17 @@ extern "C" void prompp_mem_info(void* res) { #endif } -extern "C" void prompp_dump_memory_profile([[maybe_unused]] void* args, void* res) { +/** + * @brief Dump jemalloc memory profile to file + * + * @param args { + * filename string + * } + * @param res { + * int error + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_dump_memory_profile([[maybe_unused]] void* args, void* res) { struct Arguments { PromPP::Primitives::Go::String filename; }; diff --git a/pp/entrypoint/bridge/head_status.cpp b/pp/entrypoint/bridge/head_status.cpp index e672911845..bd6b18ebfa 100644 --- a/pp/entrypoint/bridge/head_status.cpp +++ b/pp/entrypoint/bridge/head_status.cpp @@ -1,4 +1,5 @@ #include "head_status.h" +#include "annotations.h" #include "entrypoint/types/data_storage.h" #include "entrypoint/types/lss.h" @@ -10,30 +11,91 @@ using entrypoint::types::LssVariantPtr; using Status = head::Status; -extern "C" void prompp_get_head_status_lss(void* args, void* res) { +/** + * @brief Return head status from lss. + * + * @param args { + * lss uintptr // pointer to constructed lss + * limit int // statistics limit + * } + * + * @param res { + * status struct { // head status + * label_value_count_by_label_name []struct { + * name string + * count uint32 + * } + * series_count_by_metric_name []struct { + * name string + * count uint32 + * } + * memory_in_bytes_by_label_name []struct { + * name string + * size uint32 + * } + * series_count_by_label_value_pair [] struct { + * name string + * value string + * count uint32 + * } + * num_series uint32 + * num_label_pairs uint32 + * } + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_get_head_status_lss(void* args, void* res) { struct Arguments { LssVariantPtr lss; size_t limit; }; + using Result = Status; const auto in = static_cast(args); const auto& lss = std::get(*in->lss); - head::StatusGetterLSS{lss, in->limit}.get(*static_cast(res)); + head::StatusGetterLSS{lss, in->limit}.get(*static_cast(res)); } -extern "C" void prompp_get_head_status_data_storage(void* args, void* res) { +/** + * @brief Return head status from lss. + * + * @param args { + * dataStorage uintptr // pointer to constructed data storage + * } + * + * @param res { + * status struct { // head status + * time_interval struct { + * min int64 + * max int64 + * } + * chunk_count uint32 + * } + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_get_head_status_data_storage(void* args, void* res) { struct Arguments { DataStoragePtr data_storage; }; + using Result = Status; const auto in = static_cast(args); - auto* status = static_cast(res); + auto* status = static_cast(res); status->min_max_timestamp = series_data::Decoder::get_time_interval(*in->data_storage); status->chunk_count = in->data_storage->chunks().non_empty_chunk_count(); } -extern "C" void prompp_free_head_status(void* args) { - static_cast(args)->~Status(); +/** + * @brief Return head status + * + * @param args { + * status struct {...} // status returned by prompp_get_head_status + * } + * + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_free_head_status(void* args) { + using Arguments = Status; + + static_cast(args)->~Status(); } diff --git a/pp/entrypoint/bridge/head_wal.cpp b/pp/entrypoint/bridge/head_wal.cpp index 6e40be0877..ab6b33d83e 100644 --- a/pp/entrypoint/bridge/head_wal.cpp +++ b/pp/entrypoint/bridge/head_wal.cpp @@ -1,4 +1,5 @@ #include "head_wal.h" +#include "annotations.h" #include @@ -18,7 +19,19 @@ using DecoderPtr = std::unique_ptr; static_assert(sizeof(EncoderPtr) == sizeof(void*)); static_assert(sizeof(DecoderPtr) == sizeof(void*)); -extern "C" void prompp_head_wal_encoder_ctor(void* args, void* res) { +/** + * @brief Construct a new Head WAL encoder + * + * @param args { + * shardID uint16 // shard number + * logShards uint8 // logarithm to the base 2 of total shards count + *. lss uintptr // pointer to lss + * } + * @param res { + * encoder uintptr // pointer to constructed encoder + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_head_wal_encoder_ctor(void* args, void* res) { using entrypoint::types::LssVariantPtr; struct Arguments { @@ -36,7 +49,17 @@ extern "C" void prompp_head_wal_encoder_ctor(void* args, void* res) { new (res) Result{.encoder = std::make_unique(lss, in->shard_id, in->log_shards)}; } -extern "C" void prompp_head_wal_encoder_ctor_from_decoder(void* args, void* res) { +/** + * @brief Create encoder from decoder + * + * @param args { + * decoder uintptr // pointer to decoder + * } + * @param res { + * encoder uintptr // pointer to constructed encoder + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_head_wal_encoder_ctor_from_decoder(void* args, void* res) { struct Arguments { DecoderPtr decoder; }; @@ -60,7 +83,14 @@ extern "C" void prompp_head_wal_encoder_ctor_from_decoder(void* args, void* res) decoder.pow_two_of_total_shards(), decoder.last_processed_segment() + 1, decoder.sample_decoder().timestamp_base); } -extern "C" void prompp_head_wal_encoder_dtor(void* args) { +/** + * @brief Destroy Encoder + * + * @param args { + * encoder uintptr // pointer to constructed encoder + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_head_wal_encoder_dtor(void* args) { struct Arguments { EncoderPtr encoder; }; @@ -68,7 +98,19 @@ extern "C" void prompp_head_wal_encoder_dtor(void* args) { static_cast(args)->~Arguments(); } -extern "C" void prompp_head_wal_encoder_add_inner_series(void* args, void* res) { +/** + * @brief Add inner series to current segment + * + * @param args { + * incomingInnerSeries []InnerSeries // go slice with inner series; + * encoder uintptr // pointer to constructed encoder; + * } + * @param res { + * error []byte // error string if thrown + * samples uint32 // number of samples in segment + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_head_wal_encoder_add_inner_series(void* args, void* res) { struct Arguments { PromPP::Primitives::Go::SliceView incoming_inner_series; EncoderPtr encoder; @@ -90,7 +132,19 @@ extern "C" void prompp_head_wal_encoder_add_inner_series(void* args, void* res) } } -extern "C" void prompp_head_wal_encoder_finalize(void* args, void* res) { +/** + * @brief Flush segment + * + * @param args { + * encoder uintptr // pointer to constructed encoder + * } + * @param res { + * segment []byte // segment content + * error []byte // error string if thrown + * samples uint32 // number of samples in segment + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_head_wal_encoder_finalize(void* args, void* res) { struct Arguments { EncoderPtr encoder; }; @@ -114,7 +168,17 @@ extern "C" void prompp_head_wal_encoder_finalize(void* args, void* res) { } } -extern "C" void prompp_head_wal_encoder_max_written_item_index(void* args, void* res) { +/** + * @brief Exclusive upper bound of series item indices written to WAL. + * + * @param args { + * encoder uintptr // pointer to constructed encoder + * } + * @param res { + * max_written_item_index uint32 + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_head_wal_encoder_max_written_item_index(void* args, void* res) { struct Arguments { EncoderPtr encoder; }; @@ -128,7 +192,19 @@ extern "C" void prompp_head_wal_encoder_max_written_item_index(void* args, void* out->max_written_item_index = in->encoder->max_written_item_index(); } -extern "C" void prompp_head_wal_decoder_ctor(void* args, void* res) { +/** + * @brief Construct a new Head WAL Decoder + * + * @param args { + * lss uintptr // pointer to lss + * encoder_version uint8_t // basic encoder version + * } + * + * @param res { + * decoder uintptr // pointer to constructed decoder + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_head_wal_decoder_ctor(void* args, void* res) { using entrypoint::types::LssVariantPtr; struct Arguments { @@ -145,7 +221,14 @@ extern "C" void prompp_head_wal_decoder_ctor(void* args, void* res) { new (res) Result{.decoder = std::make_unique(lss, in->encoder_version)}; } -extern "C" void prompp_head_wal_decoder_dtor(void* args) { +/** + * @brief Destroy decoder + * + * @param args { + * decoder uintptr // pointer to constructed decoder + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_head_wal_decoder_dtor(void* args) { struct Arguments { DecoderPtr decoder; }; @@ -153,7 +236,26 @@ extern "C" void prompp_head_wal_decoder_dtor(void* args) { static_cast(args)->~Arguments(); } -extern "C" void prompp_head_wal_decoder_decode(void* args, void* res) { +/** + * @brief Decode WAL-segment into protobuf message + * + * @param args { + * decoder uintptr // pointer to constructed decoder + * segment []byte // segment content + * inner_series *InnerSeries // decoded content + * } + * @param res { + * created_at int64 // timestamp in ns when data was start writed to encoder + * encoded_at int64 // timestamp in ns when segment was encoded + * samples uint32 // number of samples in segment + * series uint32 // number of series in segment + * segment_id uint32 // processed segment id + * earliest_block_sample int64 // min timestamp in block + * latest_block_sample int64 // max timestamp in block + * error []byte // error string if thrown + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_head_wal_decoder_decode(void* args, void* res) { struct Arguments { DecoderPtr decoder; PromPP::Primitives::Go::SliceView segment; @@ -186,7 +288,21 @@ extern "C" void prompp_head_wal_decoder_decode(void* args, void* res) { } } -extern "C" void prompp_head_wal_decoder_decode_to_data_storage(void* args, void* res) { +/** + * @brief Decode WAL-segment into DataStorage + * + * @param args { + * decoder uintptr // pointer to constructed decoder + * segment []byte // segment content + * encoder uintptr // pointer to constructed data_storage encoder + * } + * @param res { + * createTimestamp int64 // timestamp of earliest sample in wal + * encodeTimestamp int64 // timestamp of latest sample in wal + * error []byte // error string if thrown + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_head_wal_decoder_decode_to_data_storage(void* args, void* res) { struct Arguments { DecoderPtr decoder; PromPP::Primitives::Go::SliceView segment; diff --git a/pp/entrypoint/bridge/index_writer.cpp b/pp/entrypoint/bridge/index_writer.cpp index 115ad1ab78..44140b8d05 100644 --- a/pp/entrypoint/bridge/index_writer.cpp +++ b/pp/entrypoint/bridge/index_writer.cpp @@ -1,4 +1,5 @@ #include "index_writer.h" +#include "annotations.h" #include @@ -34,7 +35,26 @@ using IndexWriterHandlePtr = std::unique_ptr; } // namespace -extern "C" void prompp_index_writer_ctor(void* args, void* res) { +/** + * @brief Construct index writer + * + * The writer owns an internal output buffer that every write_* method resets + * and fills, so the buffer is never threaded through the cgo boundary. Besides + * the writer pointer the constructor returns a stable pointer to that buffer + * (a Go []byte header: {data, len, cap}); Go reads the produced bytes from it + * after each call. The buffer is released together with the writer in the + * destructor. + * + * @param args { + * lss uintptr // pointer to constructed lss + * } + * @param res { + * writer uintptr // pointer to constructed index writer + * buffer uintptr // pointer to the writer's internal output buffer ([]byte header) + * has_more_postings uintptr // pointer to a uint8 set by write_postings (1 = more batches remain) + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_index_writer_ctor(void* args, void* res) { struct Arguments { entrypoint::types::LssVariantPtr lss; }; @@ -52,7 +72,14 @@ extern "C" void prompp_index_writer_ctor(void* args, void* res) { new (res) Result{.writer = std::move(handle), .buffer = buffer, .has_more_postings = has_more_postings}; } -extern "C" void prompp_index_writer_dtor(void* args) { +/** + * @brief Destroy index writer + * + * @param args { + * writer uintptr + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_index_writer_dtor(void* args) { struct Arguments { IndexWriterHandlePtr writer; }; @@ -60,19 +87,57 @@ extern "C" void prompp_index_writer_dtor(void* args) { static_cast(args)->~Arguments(); } -extern "C" void prompp_index_writer_write_header(void* writer) { - auto* handle = static_cast(writer); +/** + * @brief Write header + * + * Writes into the writer's internal buffer; read the result from the buffer + * pointer returned by the constructor. + * + * @param writer uintptr // pointer to constructed index writer + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_index_writer_write_header(void* args) { + using Arguments = IndexWriterHandle; + + auto* handle = static_cast(args); auto stream = handle->reset_buffer(); handle->writer.write_header(stream); } -extern "C" void prompp_index_writer_write_symbols(void* writer) { +/** + * @brief Write symbols + * + * Long-running single call: invoked as a regular cgo call (not fastcgo) so the + * goroutine parks in _Gsyscall and frees its P for the duration. The writer + * pointer is a stable prompp-arena address passed by value, so C runs on its + * own stack frame and never dereferences a goroutine stack address that a + * concurrent GC stack move could invalidate. The result is written into the + * writer's internal buffer. + * + * @param writer uintptr // pointer to constructed index writer + */ +extern "C" PROMPP(entrypoint, cgo) void prompp_index_writer_write_symbols(void* writer) { auto* handle = static_cast(writer); auto stream = handle->reset_buffer(); handle->writer.write_symbols(stream); } -extern "C" void prompp_index_writer_write_next_series_batch(void* args) { +/** + * @brief Write next series batch + * + * Writes into the writer's internal buffer; read the result from the buffer + * pointer returned by the constructor. + * + * @param args { + * writer uintptr + * chunks_meta []struct{ // chunks metadata slice + * min_t int64 + * max_t int64 + * reference uint64 + * } + * ls_id uint32 + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_index_writer_write_next_series_batch(void* args) { struct Arguments { IndexWriterHandle* writer; SliceView chunk_metadata_list; @@ -84,33 +149,92 @@ extern "C" void prompp_index_writer_write_next_series_batch(void* args) { in->writer->writer.write_series(in->ls_id, in->chunk_metadata_list, stream); } -extern "C" void prompp_index_writer_write_label_indices(void* writer) { +/** + * @brief Write label indices + * + * Long-running single call: it walks the whole name/value trie index, so like + * write_symbols/write_postings it is invoked as a regular cgo call (not fastcgo) + * to park the goroutine in _Gsyscall and free its P for the duration. The writer + * pointer is a stable prompp-arena address passed by value. The result is + * written into the writer's internal buffer. + * + * @param writer uintptr // pointer to constructed index writer + */ +extern "C" PROMPP(entrypoint, cgo) void prompp_index_writer_write_label_indices(void* writer) { auto* handle = static_cast(writer); auto stream = handle->reset_buffer(); handle->writer.write_label_indices(stream); } -extern "C" void prompp_index_writer_write_postings(void* writer, uint64_t max_batch_size) { +/** + * @brief Write one batch of postings + * + * Writes postings into the writer's internal buffer until the bytes produced in + * this call reach max_batch_size, then returns; call repeatedly while the + * has_more_postings flag (returned by the constructor) is non-zero to drain the + * whole section. Batching bounds the transient buffer size: a single unbatched + * call buffers the entire postings section (tens of MiB), so Go flushes each + * batch and reuses the buffer instead. The byte bound is checked only between + * whole postings, so the all-series posting and hot label values can overshoot + * it. Each batch is a regular cgo call (not fastcgo) so the goroutine parks in + * _Gsyscall and frees its P for the duration; the writer pointer is a stable + * prompp-arena address passed by value, so no goroutine stack pointer is handed + * to C. + * + * @param writer uintptr // pointer to constructed index writer + * @param max_batch_size uint64 // soft upper bound on bytes emitted per call + */ +extern "C" PROMPP(entrypoint, cgo) void prompp_index_writer_write_postings(void* writer, uint64_t max_batch_size) { auto* handle = static_cast(writer); auto stream = handle->reset_buffer(); handle->writer.write_postings(stream, static_cast(max_batch_size)); handle->has_more_postings = handle->writer.has_more_postings_data() ? 1 : 0; } -extern "C" void prompp_index_writer_write_label_indices_table(void* writer) { - auto* handle = static_cast(writer); +/** + * @brief Write label indeces table + * + * Writes into the writer's internal buffer; read the result from the buffer + * pointer returned by the constructor. + * + * @param writer uintptr // pointer to constructed index writer + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_index_writer_write_label_indices_table(void* args) { + using Arguments = IndexWriterHandle; + + auto* handle = static_cast(args); auto stream = handle->reset_buffer(); handle->writer.write_label_indices_table(stream); } -extern "C" void prompp_index_writer_write_postings_table_offsets(void* writer) { - auto* handle = static_cast(writer); +/** + * @brief Write postings offset table + * + * Writes into the writer's internal buffer; read the result from the buffer + * pointer returned by the constructor. + * + * @param writer uintptr // pointer to constructed index writer + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_index_writer_write_postings_table_offsets(void* args) { + using Arguments = IndexWriterHandle; + + auto* handle = static_cast(args); auto stream = handle->reset_buffer(); handle->writer.write_postings_table_offsets(stream); } -extern "C" void prompp_index_writer_write_table_of_contents(void* writer) { - auto* handle = static_cast(writer); +/** + * @brief Write table of contents + * + * Writes into the writer's internal buffer; read the result from the buffer + * pointer returned by the constructor. + * + * @param writer uintptr // pointer to constructed index writer + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_index_writer_write_table_of_contents(void* args) { + using Arguments = IndexWriterHandle; + + auto* handle = static_cast(args); auto stream = handle->reset_buffer(); handle->writer.write_toc(stream); } diff --git a/pp/entrypoint/bridge/label_set.cpp b/pp/entrypoint/bridge/label_set.cpp index ef7b5282fe..164f467f0d 100644 --- a/pp/entrypoint/bridge/label_set.cpp +++ b/pp/entrypoint/bridge/label_set.cpp @@ -1,4 +1,5 @@ #include "label_set.h" +#include "annotations.h" #include "bare_bones/algorithm.h" #include "bare_bones/iterator.h" @@ -12,7 +13,19 @@ using entrypoint::types::SnapshotLSSVariantPtr; using PromPP::Primitives::Go::Slice; using PromPP::Primitives::Go::SliceView; -void prompp_label_set_length(void* args, void* res) { +/** + * @brief get length label set by series id + * + * @param args { + * lss uintptr // pointer to constructed lss; + * ls_id uint32 // series id + * } + * + * @param res { + * length int // length of label set + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_label_set_length(void* args, void* res) { struct Arguments { LssVariantPtr lss; uint32_t series_id; @@ -26,7 +39,19 @@ void prompp_label_set_length(void* args, void* res) { std::visit([in, res](auto& lss) { new (res) Result{.length = lss[in->series_id].size()}; }, *in->lss); } -extern "C" void prompp_label_set_serialize_from_snapshot(void* args, void* res) { +/** + * @brief get label set by series id + * + * @param args { + * snapshot uintptr // pointer to constructed snapshot; + * ls_id uint32 // series id + * } + * + * @param res { + * label_set []struct{key, value String} // label sets + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_label_set_serialize_from_snapshot(void* args, void* res) { using PromPP::Primitives::Go::Label; using PromPP::Primitives::Go::String; @@ -52,7 +77,19 @@ extern "C" void prompp_label_set_serialize_from_snapshot(void* args, void* res) *in->snapshot); } -extern "C" void prompp_label_set_serialize_from_snapshot_length(void* args, void* res) { +/** + * @brief get serialized label set buffer length by series id + * + * @param args { + * snapshot uintptr // pointer to constructed snapshot + * labelSetID uint32 // series id + * } + * + * @param res { + * length uint32 // serialized buffer length + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_label_set_serialize_from_snapshot_length(void* args, void* res) { using BareBones::Encoding::VarInt; struct Arguments { @@ -78,7 +115,17 @@ extern "C" void prompp_label_set_serialize_from_snapshot_length(void* args, void *in->snapshot); } -extern "C" void prompp_label_set_serialize_from_snapshot_to_buffer(void* args) { +/** + * @brief serialize label set into buffer by series id + * + * @param args { + * snapshot uintptr // pointer to constructed snapshot + * buffer [] byte // allocated buffer + * labelSetID uint32 // series id + * } + * + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_label_set_serialize_from_snapshot_to_buffer(void* args) { using BareBones::Encoding::VarInt; struct Arguments { @@ -105,7 +152,14 @@ extern "C" void prompp_label_set_serialize_from_snapshot_to_buffer(void* args) { *in->snapshot); } -extern "C" void prompp_label_set_free(void* args) { +/** + * @brief free label set returned by prompp_label_set_serialize_from_snapshot + * + * @param args { + * label_set []struct{key, value String} // label set + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_label_set_free(void* args) { using PromPP::Primitives::Go::Label; using PromPP::Primitives::Go::Slice; @@ -196,7 +250,19 @@ struct LabelNameLess { PROMPP_ALWAYS_INLINE bool operator()(const PromPP::Primitives::Go::String& a, const PromPP::Primitives::Go::String& b) const noexcept { return a < b; } }; -extern "C" void prompp_label_set_bytes_size(void* args, void* res) { +/** + * @brief get size in bytes needed for Bytes method + * + * @param args { + * lss uintptr // pointer to constructed lss; + * ls_id uint32 // series id + * } + * + * @param res { + * size uint32 + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_label_set_bytes_size(void* args, void* res) { struct Arguments { LssVariantPtr lss; uint32_t series_id; @@ -217,7 +283,19 @@ extern "C" void prompp_label_set_bytes_size(void* args, void* res) { *in->lss); } -extern "C" void prompp_label_set_bytes(void* args, void* res) { +/** + * @brief implementation of Bytes method + * + * @param args { + * lss uintptr // pointer to constructed lss; + * ls_id uint32 // series id + * } + * + * @param res { + * bytes []byte + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_label_set_bytes(void* args, void* res) { struct Arguments { LssVariantPtr lss; uint32_t series_id; @@ -238,7 +316,20 @@ extern "C" void prompp_label_set_bytes(void* args, void* res) { *in->lss); } -extern "C" void prompp_label_set_bytes_with_labels(void* args, void* res) { +/** + * @brief implementation of BytesWithLabels method + * + * @param args { + * lss uintptr // pointer to constructed lss; + * ls_id uint32 // series id + * names []string // names slice + * } + * + * @param res { + * bytes []byte + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_label_set_bytes_with_labels(void* args, void* res) { struct Arguments { LssVariantPtr lss; uint32_t series_id; @@ -260,7 +351,20 @@ extern "C" void prompp_label_set_bytes_with_labels(void* args, void* res) { *in->lss); } -extern "C" void prompp_label_set_bytes_without_labels(void* args, void* res) { +/** + * @brief implementation of BytesWithoutLabels method + * + * @param args { + * lss uintptr // pointer to constructed lss; + * ls_id uint32 // series id + * names []string // names slice + * } + * + * @param res { + * bytes []byte + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_label_set_bytes_without_labels(void* args, void* res) { struct Arguments { LssVariantPtr lss; uint32_t series_id; diff --git a/pp/entrypoint/bridge/metrics.cpp b/pp/entrypoint/bridge/metrics.cpp index 98c4df6564..4526308e92 100644 --- a/pp/entrypoint/bridge/metrics.cpp +++ b/pp/entrypoint/bridge/metrics.cpp @@ -1,4 +1,5 @@ #include "metrics.h" +#include "annotations.h" #include "metrics/jemalloc_metrics.h" #include "metrics/storage.h" @@ -9,7 +10,10 @@ using PromPP::Primitives::Go::Label; using PromPP::Primitives::Go::SliceView; using PromPP::Primitives::Go::String; -extern "C" void prompp_metrics_register() { +/** + * @brief Register cpp metrics + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_metrics_register() { [[maybe_unused]] static auto _ = [] { #if JEMALLOC_AVAILABLE metrics::CreateMetricsPage(); @@ -18,13 +22,31 @@ extern "C" void prompp_metrics_register() { }(); } -extern "C" void prompp_metrics_iterator_ctor(void* args) { +/** + * @brief Initialize metrics iterator + * + * @param args *MetricIterator + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_metrics_iterator_ctor(void* args) { + using Arguments = metrics::Storage::Iterator; + metrics::storage.remove_unused_pages(); - std::construct_at(static_cast(args), metrics::storage.begin()); + std::construct_at(static_cast(args), metrics::storage.begin()); } -extern "C" void prompp_metrics_iterator_next(void* args, void* res) { +/** + * @brief Serialize metric into protobuf and advance iterator to next metric + * + * @param args { + * iterator *MetricIterator // Pointer to constructed iterator + * } + * + * @param res { + * metric *cppbridge.CppMetric // Pointer to go metric + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_metrics_iterator_next(void* args, void* res) { struct Arguments { metrics::Storage::Iterator* iterator; }; @@ -54,7 +76,20 @@ struct MetricsPageForTest final : metrics::MetricsPage { metrics::Gauge emplace_gauge; }; -extern "C" void prompp_metrics_page_for_test_ctor(void* args, void* res) { +/** + * @brief Create metrics page for test + * + * @param args { + * labels []cppbridge.Label // metric page label set + * counterName string // label name for uint64 counter + * counterValue uint64 // value for for uint64 counter + * } + * + * @param res { + * page uintptr // Pointer to constructed page + * } + */ +extern "C" PROMPP(entrypoint, fastcgo) void prompp_metrics_page_for_test_ctor(void* args, void* res) { struct Arguments { SliceView