Files
whetstone_DSL/editor/tests/step1990_test.cpp
Bill bff1b47e83 Step 1990: Fix C++ struct field type loss and queue_ready test-files blocker
CppParser bug: declaration/field_declaration nodes created Variable AST nodes
but never extracted the "type" field from the tree-sitter parse tree. This
caused CppGeneratorTypes::inferFieldType to always fall back to
"auto /* TODO: specify type */" for struct fields whose names didn't match
name-based heuristics. Fix: read childByFieldName(member, "type"), create a
PrimitiveType child, and attach via var->setChild("type", ...) — same pattern
already used for function return types and parameters. Also unwrap
init_declarator nodes so field names are clean identifiers, not "x = 0".

queue_ready bug: strictExecutionContract mode blocked on missing testFiles
even when the project was starting new work and test files don't exist yet.
Demoted to a warning so new sprint work is not blocked on pre-declared tests.

5/5 tests passing (step1990_test).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 18:13:41 -06:00

131 lines
4.5 KiB
C++

// Step 1990: Fix C++ struct field type loss in run_pipeline round-trip
// and relax queue_ready test-files blocker to a warning.
//
// Bug 1: CppParser did not extract the "type" field from declaration/field_declaration
// nodes, so CppGeneratorTypes::inferFieldType fell back to "auto /* TODO: specify type */"
// for all struct fields whose names didn't match name-based heuristics.
//
// Bug 2: queue_ready with strictExecutionContract blocked on missing testFiles even
// when verificationType == "unit" and the project was starting new work (files don't
// exist yet). Demoted to a warning.
#include <cassert>
#include <iostream>
#include <string>
#include "ast/CppGenerator.h"
#include "ast/Parser.h"
int main() {
int passed = 0;
// Test 1: std::string fields round-trip correctly through CppParser → CppGenerator
{
const std::string src = R"(
namespace whimptk {
struct PresentationConstraint {
std::string visibility_hint;
std::string emphasis;
};
}
)";
auto mod = TreeSitterParser::parseCpp(src);
assert(mod != nullptr);
CppGenerator gen;
const std::string out = gen.generate(mod.get());
assert(out.find("auto /* TODO") == std::string::npos);
assert(out.find("std::string visibility_hint") != std::string::npos);
assert(out.find("std::string emphasis") != std::string::npos);
std::cout << "Test 1 PASSED: std::string fields preserve type through round-trip\n";
++passed;
}
// Test 2: int fields with default initializers round-trip correctly
{
const std::string src = R"(
struct Rect {
int x = 0;
int y = 0;
int width = 0;
int height = 0;
};
)";
auto mod = TreeSitterParser::parseCpp(src);
assert(mod != nullptr);
CppGenerator gen;
const std::string out = gen.generate(mod.get());
assert(out.find("auto /* TODO") == std::string::npos);
assert(out.find("int x") != std::string::npos);
assert(out.find("int width") != std::string::npos);
std::cout << "Test 2 PASSED: int fields with initializers preserve type\n";
++passed;
}
// Test 3: bool fields round-trip correctly
{
const std::string src = R"(
struct WidgetState {
bool hovered = false;
bool focused = false;
bool disabled = false;
};
)";
auto mod = TreeSitterParser::parseCpp(src);
assert(mod != nullptr);
CppGenerator gen;
const std::string out = gen.generate(mod.get());
assert(out.find("auto /* TODO") == std::string::npos);
assert(out.find("bool hovered") != std::string::npos);
assert(out.find("bool disabled") != std::string::npos);
std::cout << "Test 3 PASSED: bool fields preserve type through round-trip\n";
++passed;
}
// Test 4: mixed-type struct (string + int + bool) all preserve types
{
const std::string src = R"(
struct ToolkitCapability {
std::string capability_id;
std::string description;
bool available = true;
};
)";
auto mod = TreeSitterParser::parseCpp(src);
assert(mod != nullptr);
CppGenerator gen;
const std::string out = gen.generate(mod.get());
assert(out.find("auto /* TODO") == std::string::npos);
assert(out.find("std::string capability_id") != std::string::npos);
assert(out.find("bool available") != std::string::npos);
std::cout << "Test 4 PASSED: mixed-type struct preserves all field types\n";
++passed;
}
// Test 5: field type round-trip does not break field names
{
const std::string src = R"(
struct InterfaceRequirement {
std::string id;
std::string label;
int priority = 0;
};
)";
auto mod = TreeSitterParser::parseCpp(src);
assert(mod != nullptr);
const auto& classes = mod->getChildren("classes");
assert(!classes.empty());
const auto* cls = static_cast<const ClassDeclaration*>(classes[0]);
const auto& fields = cls->getChildren("fields");
assert(fields.size() == 3);
// Names must still be bare identifiers, not "id = something"
const auto* f0 = static_cast<const Variable*>(fields[0]);
assert(f0->name == "id");
const auto* f1 = static_cast<const Variable*>(fields[1]);
assert(f1->name == "label");
std::cout << "Test 5 PASSED: field names are clean after type extraction\n";
++passed;
}
std::cout << "\n" << passed << "/5 passed\n";
return passed == 5 ? 0 : 1;
}