Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ jobs:
with:
python-version: "3.x"

# test.py verifies the emitted C++ corpus's doc links with doxygen;
# graphviz supplies the dot binary its graph generation needs.
- run: sudo apt-get update && sudo apt-get install -y doxygen graphviz

# test.py lints the viewer (tsc/eslint/prettier) but does not install;
# provide the workspace deps here.
- name: Set up Node.js
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ When you change the language (new attributes, syntax, types, etc.), you must aud
python test.py
```

runs the full test suite: clippy, fmt, the parser/semantic unit tests, `cargo run --example codegen_tests` (which emits the test corpus through every backend and rebuilds the emitted output), and `cargo doc --no-deps -p codegen_tests` (which catches unresolved doc link references in the generated Rust output). The cpp test corpus uses a regular host C++17 compiler (no MSVC ABI required) so CI doesn't need xwin.
runs the full test suite: clippy, fmt, the parser/semantic unit tests, `cargo run --example codegen_tests` (which emits the test corpus through every backend and rebuilds the emitted output), `cargo doc --no-deps -p codegen_tests` (which catches unresolved doc link references in the generated Rust output), and a doxygen pass over the emitted C++ corpus (which catches unresolvable `@ref`s in the rewritten C++ doc links — skipped with a warning if doxygen isn't installed; `nix-shell` provides it via `shell.nix`). The cpp test corpus uses a regular host C++17 compiler (no MSVC ABI required) so CI doesn't need xwin.

Formatting relies on a nightly-only rustfmt feature (`imports_granularity`, configured in `rustfmt.toml`), so check it with nightly:

Expand Down
24 changes: 24 additions & 0 deletions codegen_tests/input/doc_self_links.pyxis
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Self-link testing module. Its own doc links [`Container`] — the module's
//! doc block must keep its links separate from its first item's, since the
//! module's source location is a proxy borrowed from that item.

/// Test `Self::` links in type docs.
///
/// Link to a field as [`Self::field`](Self::field), to a method as
/// [`Self::method`](Self::method), to a nested type as
/// [`Self::Nested`](Self::Nested), and to a nested type's member as
/// [`Self::Nested::nested_field`](Self::Nested::nested_field).
pub type Container {
/// A nested type. Its field is [`Self::nested_field`](Self::nested_field).
pub type Nested {
pub nested_field: u16,
},

/// A field.
pub field: u32,
}
impl Container {
/// A method.
#[address(0x10)]
pub fn method(&self);
}
7 changes: 7 additions & 0 deletions codegen_tests/input/qualified_links_consumer.pyxis
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// Test module-qualified doc-links to functions and extern values.
///
/// See [`shared_function`](qualified_links_provider::shared_function) and
/// [`shared_extern`](qualified_links_provider::shared_extern).
pub type Consumer {
pub _marker: u8,
}
7 changes: 7 additions & 0 deletions codegen_tests/input/qualified_links_provider.pyxis
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// A module with a freestanding function.
#[address(0x100)]
pub fn shared_function();

/// An extern value.
#[address(0x200)]
pub extern shared_extern: *mut u32;
10 changes: 5 additions & 5 deletions codegen_tests/output/cpp/include/consts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace consts {
enum class Color : ::std::uint8_t;
struct Player;

/// The permissive baseline is [`DEFAULT_MASK`](AccessFlags::DEFAULT_MASK).
/// The permissive baseline is [`DEFAULT_MASK`](@ref consts::AccessFlags_DEFAULT_MASK).
enum class AccessFlags : ::std::uint32_t {
READ = 0x1,
WRITE = 0x2,
Expand All @@ -30,7 +30,7 @@ namespace consts {
static_assert(sizeof(AccessFlags) == 0x4);
constexpr ::std::uint32_t AccessFlags_DEFAULT_MASK = 3;

/// Defaults to [`DEFAULT`](Color::DEFAULT).
/// Defaults to [`DEFAULT`](@ref consts::Color_DEFAULT).
enum class Color : ::std::uint8_t {
Red = 0,
Green = 1,
Expand All @@ -40,9 +40,9 @@ namespace consts {
constexpr Color Color_DEFAULT = Color::Red;

struct alignas(4) Player {
/// New players begin with [`STARTING_GOLD`](Player::STARTING_GOLD) gold,
/// spawn at x=[`SPAWN_X`](Player::SPAWN_X), and can hold up to
/// [`MAX_SLOTS`](Player::Inventory::MAX_SLOTS) items.
/// New players begin with [`STARTING_GOLD`](@ref consts::Player::STARTING_GOLD) gold,
/// spawn at x=[`SPAWN_X`](@ref consts::Player::SPAWN_X), and can hold up to
/// [`MAX_SLOTS`](@ref consts::Player::Inventory::MAX_SLOTS) items.
::std::int32_t health;

static constexpr ::std::uint32_t STARTING_GOLD = 500;
Expand Down
30 changes: 30 additions & 0 deletions codegen_tests/output/cpp/include/doc_self_links.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// @generated by pyxis — do not edit
#pragma once

#include <cstdint>
#include <cstddef>
#include "pyxis_runtime.hpp"

namespace doc_self_links {
struct Container;

/// Test `Self::` links in type docs.
///
/// Link to a field as [`Self::field`](@ref doc_self_links::Container::field), to a method as
/// [`Self::method`](@ref doc_self_links::Container::method), to a nested type as
/// [`Self::Nested`](@ref doc_self_links::Container::Nested), and to a nested type's member as
/// [`Self::Nested::nested_field`](@ref doc_self_links::Container::Nested::nested_field).
struct alignas(4) Container {
/// A field.
::std::uint32_t field;
/// A method.
void method() const;

/// A nested type. Its field is [`Self::nested_field`](@ref doc_self_links::Container::Nested::nested_field).
struct Nested {
::std::uint16_t nested_field;
};
};
static_assert(sizeof(Container) == 0x4);
static_assert(alignof(Container) == 4);
} // namespace doc_self_links
8 changes: 4 additions & 4 deletions codegen_tests/output/cpp/include/extern_values.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace extern_values {
enum class RenderMode : ::std::uint8_t;
struct Engine;

/// The active flag set is [`g_active`](DebugFlags::g_active).
/// The active flag set is [`g_active`](@ref extern_values::DebugFlags_get_g_active).
enum class DebugFlags : ::std::uint32_t {
WIREFRAME = 0x1,
OVERDRAW = 0x2,
Expand All @@ -31,23 +31,23 @@ namespace extern_values {
DebugFlags*& DebugFlags_get_g_active();

struct alignas(4) Engine {
/// The live instance is [`g_instance`](Engine::g_instance).
/// The live instance is [`g_instance`](@ref extern_values::Engine::get_g_instance).
::std::uint32_t frame;

static Engine*& get_g_instance();
};
static_assert(sizeof(Engine) == 0x4);
static_assert(alignof(Engine) == 4);

/// The active mode is [`g_current`](RenderMode::g_current).
/// The active mode is [`g_current`](@ref extern_values::RenderMode_get_g_current).
enum class RenderMode : ::std::uint8_t {
Forward = 0,
Deferred = 1,
};
static_assert(sizeof(RenderMode) == 0x1);
RenderMode*& RenderMode_get_g_current();

/// The engine singleton, of type [`Engine`]. Advances with [`g_frame_count`].
/// The engine singleton, of type [`Engine`](@ref extern_values::Engine). Advances with [`g_frame_count`](@ref extern_values::get_g_frame_count).
Engine*& get_g_engine();

::std::uint32_t& get_g_frame_count();
Expand Down
18 changes: 9 additions & 9 deletions codegen_tests/output/cpp/include/nested_items.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,37 @@ namespace nested_items {

/// A type with nested declarations.
///
/// See [`InnerEnum`], [`InnerType`], [`InnerFlags`], and [`InnerAlias`].
/// See [`InnerEnum`](@ref nested_items::Outer::InnerEnum), [`InnerType`](@ref nested_items::Outer::InnerType), [`InnerFlags`](@ref nested_items::Outer::InnerFlags), and [`InnerAlias`](@ref nested_items::Outer::InnerAlias).
///
/// You can also qualify them: [`Outer::InnerEnum`], [`Outer::InnerType`].
/// You can also qualify them: [`Outer::InnerEnum`](@ref nested_items::Outer::InnerEnum), [`Outer::InnerType`](@ref nested_items::Outer::InnerType).
struct alignas(4) Outer {
::std::uint32_t field;

/// An enum nested inside [`Outer`].
/// An enum nested inside [`Outer`](@ref nested_items::Outer).
///
/// Variants: [`InnerEnum::A`], [`InnerEnum::B`], [`InnerEnum::C`].
/// Variants: [`InnerEnum::A`](@ref nested_items::Outer::InnerEnum::A), [`InnerEnum::B`](@ref nested_items::Outer::InnerEnum::B), [`InnerEnum::C`](@ref nested_items::Outer::InnerEnum::C).
enum class InnerEnum : ::std::uint8_t {
A = 0,
B = 1,
C = 2,
};

/// A type nested inside [`Outer`].
/// A type nested inside [`Outer`](@ref nested_items::Outer).
///
/// Its field is [`InnerType::inner_field`].
/// Its field is [`InnerType::inner_field`](@ref nested_items::Outer::InnerType::inner_field).
struct InnerType {
::std::uint16_t inner_field;
};

/// Bitflags nested inside [`Outer`].
/// Bitflags nested inside [`Outer`](@ref nested_items::Outer).
///
/// Members: [`InnerFlags::FLAG_A`], [`InnerFlags::FLAG_B`].
/// Members: [`InnerFlags::FLAG_A`](@ref nested_items::Outer::InnerFlags::FLAG_A), [`InnerFlags::FLAG_B`](@ref nested_items::Outer::InnerFlags::FLAG_B).
struct InnerFlags {
static constexpr ::std::uint32_t FLAG_A = 1;
static constexpr ::std::uint32_t FLAG_B = 2;
};

/// A type alias nested inside [`Outer`].
/// A type alias nested inside [`Outer`](@ref nested_items::Outer).
using InnerAlias = ::std::uint32_t;
};
static_assert(sizeof(Outer) == 0x4);
Expand Down
20 changes: 20 additions & 0 deletions codegen_tests/output/cpp/include/qualified_links_consumer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// @generated by pyxis — do not edit
#pragma once

#include <cstdint>
#include <cstddef>
#include "pyxis_runtime.hpp"

namespace qualified_links_consumer {
struct Consumer;

/// Test module-qualified doc-links to functions and extern values.
///
/// See [`shared_function`](@ref qualified_links_provider::shared_function) and
/// [`shared_extern`](@ref qualified_links_provider::get_shared_extern).
struct alignas(1) Consumer {
::std::uint8_t _marker;
};
static_assert(sizeof(Consumer) == 0x1);
static_assert(alignof(Consumer) == 1);
} // namespace qualified_links_consumer
15 changes: 15 additions & 0 deletions codegen_tests/output/cpp/include/qualified_links_provider.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// @generated by pyxis — do not edit
#pragma once

#include <cstdint>
#include <cstddef>
#include "pyxis_runtime.hpp"

namespace qualified_links_provider {
/// An extern value.
::std::uint32_t*& get_shared_extern();

/// A module with a freestanding function.
using shared_function_t = void (*)();
extern const shared_function_t shared_function;
} // namespace qualified_links_provider
2 changes: 1 addition & 1 deletion codegen_tests/output/cpp/include/world_consumer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace world_consumer {
struct WorldConsumer;

/// Consumes the world. Doc-links a type from a deeper, un-imported module
/// ([`DeepMarker`](world::deep::marker::DeepMarker)) to exercise cross-module
/// ([`DeepMarker`](@ref world::deep::marker::DeepMarker)) to exercise cross-module
/// doc-link resolution: the link destination must be rewritten to an absolute
/// crate path so rustdoc resolves it without a `use` import.
struct alignas(8) WorldConsumer {
Expand Down
3 changes: 3 additions & 0 deletions codegen_tests/output/cpp/src/_all_headers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "definition_body.hpp"
#include "diamond_inheritance.hpp"
#include "doc_comments.hpp"
#include "doc_self_links.hpp"
#include "enums.hpp"
#include "extern_bindings.hpp"
#include "extern_values.hpp"
Expand All @@ -23,6 +24,8 @@
#include "nested_items.hpp"
#include "pinned.hpp"
#include "pyxis_runtime.hpp"
#include "qualified_links_consumer.hpp"
#include "qualified_links_provider.hpp"
#include "reexport.hpp"
#include "reexport_consumer.hpp"
#include "self_referential_generics.hpp"
Expand Down
10 changes: 10 additions & 0 deletions codegen_tests/output/cpp/src/doc_self_links.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @generated by pyxis — do not edit

#include "doc_self_links.hpp"

namespace doc_self_links {
void Container::method() const {
using fn_t = void (*)(const void*);
reinterpret_cast<fn_t>(0x10)(this);
}
} // namespace doc_self_links
12 changes: 12 additions & 0 deletions codegen_tests/output/cpp/src/qualified_links_provider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @generated by pyxis — do not edit

#include "qualified_links_provider.hpp"

namespace qualified_links_provider {
using shared_function_t = void (*)();
const shared_function_t shared_function = reinterpret_cast<shared_function_t>(0x100);

::std::uint32_t*& get_shared_extern() {
return *reinterpret_cast<::std::uint32_t**>(0x200);
}
} // namespace qualified_links_provider
Loading
Loading