The integration tests that shipped with #1158 use inputs too small to trigger
the tree-sitter class_specifier collapse, so they pass with OR without the fix
(verified: 0/3 reproduce on the pre-fix build) — only the offset-preserving unit
tests actually guard the behavior. This adds a real guard: a ~240-in-body-macro
reflected class (the density of the real CharacterMovementComponent.h) plus a
UENUM whose values carry mid-line UMETA. Asserting the decorated members and the
enum are extracted flips false->true across the three blank passes — verified the
same assertions FAIL on the pre-fix source (2a06d9a) and PASS on the fix.
The full class collapse is emergent from real engine-header content that can't be
shipped (Unreal source is EULA-licensed); this reproduces the recoverable-member
signal the collapse leaves, which regresses if any of blankCppAnnotationMacroCalls
/ blankCppApiPrefixMacros / blankCppInlineAnnotationMacros is reverted.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
8e697a0d09
commit
8db8ad5e90
@@ -3285,6 +3285,75 @@ public:
|
||||
});
|
||||
});
|
||||
|
||||
describe('C++ dense Unreal-Engine header regression (#1160/#1158)', () => {
|
||||
// Regression guard for the three UE blank passes together, on a HEAVILY
|
||||
// reflected class in the shape that broke real engine headers
|
||||
// (`CharacterMovementComponent.h` carries ~240 in-body reflection macros).
|
||||
// On the real headers the accumulated tree-sitter errors collapse the whole
|
||||
// `class_specifier` into an ERROR node and the class itself vanishes; that
|
||||
// full collapse is emergent from real-header content we can't ship here
|
||||
// (Unreal's source is EULA-licensed), so this reproduces the *recoverable*
|
||||
// signal it leaves: with the blank passes reverted, tree-sitter drops every
|
||||
// one of these decorated members and the `UMETA` enum into error recovery,
|
||||
// so the assertions below flip from pass to fail. Verified against the
|
||||
// pre-fix build: `Compute0`, the last member, and `EDenseMode` are all
|
||||
// absent before the fix and present after — reverting any of
|
||||
// blankCppAnnotationMacroCalls / blankCppApiPrefixMacros /
|
||||
// blankCppInlineAnnotationMacros regresses at least one of them.
|
||||
const N = 120; // 120 UPROPERTY + 120 UFUNCTION = ~240 in-body macros
|
||||
function denseReflectedHeader(): string {
|
||||
let members = '';
|
||||
for (let i = 0; i < N; i++) {
|
||||
// line-leading UPROPERTY with nested meta=(...) (blankCppAnnotationMacroCalls)
|
||||
members += `\tUPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Move", meta=(ClampMin="0.0", EditCondition="bOn${i}", AllowedClasses="/Script/Engine.Texture"))\n\tTSubclassOf<AActor> Prop${i};\n`;
|
||||
// line-leading UFUNCTION + member-level ENGINE_API + UPARAM(ref) param
|
||||
// (all three passes) on an inline definition (has a body → is a node)
|
||||
members += `\tUFUNCTION(BlueprintCallable, Category="Move", meta=(DisplayName="Compute ${i}"))\n\tENGINE_API float Compute${i}(UPARAM(ref) float& In) const { return In * ${i}.0f; }\n`;
|
||||
}
|
||||
return `UCLASS(MinimalAPI, Blueprintable)
|
||||
class ENGINE_API UDenseMovement : public UPawnMovementComponent, public IRVOAvoidanceInterface, public INetworkPredictionInterface
|
||||
{
|
||||
\tGENERATED_BODY()
|
||||
public:
|
||||
\tDECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnMoved, float, Speed, FVector, Loc);
|
||||
\tusing FLegacyTick UE_DEPRECATED(5.5, "use TDelegate<void(float)>") = TMulticastDelegate<void(float)>;
|
||||
${members}};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EDenseMode : uint8
|
||||
{
|
||||
\tWalking UMETA(DisplayName="Walk (fast), safe"),
|
||||
\tFlying UMETA(DisplayName="Fly"),
|
||||
\tCustom UMETA(Hidden),
|
||||
};
|
||||
`;
|
||||
}
|
||||
|
||||
it('recovers a ~240-macro reflected class, its base clause, and every decorated member', () => {
|
||||
const result = extractFromSource('DenseMovement.h', denseReflectedHeader());
|
||||
// The class and its whole multiple-inheritance base clause survive.
|
||||
expect(result.nodes.some((n) => n.kind === 'class' && n.name === 'UDenseMovement')).toBe(true);
|
||||
for (const base of ['UPawnMovementComponent', 'IRVOAvoidanceInterface', 'INetworkPredictionInterface']) {
|
||||
expect(
|
||||
result.unresolvedReferences.find((r) => r.referenceKind === 'extends' && r.referenceName === base)
|
||||
).toBeTruthy();
|
||||
}
|
||||
// The real guard: the decorated inline members parse instead of being lost
|
||||
// to error recovery — the first, a middle, and the LAST (proof the whole
|
||||
// dense body closed, not just the head).
|
||||
expect(result.nodes.some((n) => n.name === 'Compute0')).toBe(true);
|
||||
expect(result.nodes.some((n) => n.name === 'Compute60')).toBe(true);
|
||||
expect(result.nodes.some((n) => n.name === `Compute${N - 1}`)).toBe(true);
|
||||
});
|
||||
|
||||
it('recovers a UENUM whose values carry mid-line UMETA', () => {
|
||||
const result = extractFromSource('DenseMovement.h', denseReflectedHeader());
|
||||
// A mid-line UMETA drops the enum into error recovery pre-fix;
|
||||
// blankCppInlineAnnotationMacros restores it.
|
||||
expect(result.nodes.some((n) => n.kind === 'enum' && n.name === 'EDenseMode')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CUDA extraction (#387)', () => {
|
||||
// CUDA parses with the C++ grammar. Three CUDA-only shapes misparse:
|
||||
// execution-space specifiers (`__global__ void f(…)`) shunt the real return
|
||||
|
||||
Reference in New Issue
Block a user