More work on name validation

This commit is contained in:
2026-04-09 22:41:38 -04:00
parent d98c0081e6
commit ad6a33582b
8 changed files with 27 additions and 31 deletions

View File

@@ -48,11 +48,9 @@ public:
if (!BP) return;
// Check that the proposed name is valid
FName InternalID = WingUtils::CheckProposedName(Component, WingOut::Stdout);
TSet<FName> InUse = WingUtils::GetAllInUseNames(BP);
FName InternalID = WingUtils::CheckProposedName(Component, InUse, WingOut::Stdout);
if (InternalID.IsNone()) return;
TSet<FName> Names;
WingUtils::GetAllInUseNames(BP, Names);
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("variable or component"), WingOut::Stdout)) return;
// Resolve the component class by name
UWingTypes::Requirements Req;

View File

@@ -73,11 +73,9 @@ public:
}
// Check graph name uniqueness and legality
FName InternalID = WingUtils::CheckProposedName(Graph, WingOut::Stdout);
TSet<FName> InUse = WingUtils::GetAllInUseNames(BP);
FName InternalID = WingUtils::CheckProposedName(Graph, InUse, WingOut::Stdout);
if (InternalID.IsNone()) return;
TSet<FName> Names;
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("Graph"), WingOut::Stdout)) return;
if (!WingUtils::FindNoDuplicateNames(Names, WingUtils::AllGraphs(BP), TEXT("Graph"), WingOut::Stdout)) return;
// Parse and validate variables before making changes
WingVariables Vars;

View File

@@ -43,11 +43,9 @@ public:
if (!BP) return;
// Check for valid proposed name
FName InternalID = WingUtils::CheckProposedName(Dispatcher, WingOut::Stdout);
TSet<FName> InUse = WingUtils::GetAllInUseNames(BP);
FName InternalID = WingUtils::CheckProposedName(Dispatcher, InUse, WingOut::Stdout);
if (InternalID.IsNone()) return;
TSet<FName> Names;
WingUtils::GetAllInUseNames(BP, Names);
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("variable or component"), WingOut::Stdout)) return;
// Parse the arguments.
WingVariables Vars;

View File

@@ -66,14 +66,10 @@ public:
}
// Validate the proposed name.
FName InternalID = WingUtils::CheckProposedName(Name, WingOut::Stdout);
TSet<FName> InUse = WingUtils::GetAllInUseNames(BP);
FName InternalID = WingUtils::CheckProposedName(Name, InUse, WingOut::Stdout);
if (InternalID.IsNone()) return;
// Check that the name is unique among existing widgets.
TSet<FName> Names;
WingUtils::GetAllInUseNames(BP, Names);
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("widget or variable"), WingOut::Stdout)) return;
// If a parent is specified, find it and verify it's a panel.
UPanelWidget* ParentPanel = nullptr;
TArray<UWidget*> AllWidgets;

View File

@@ -149,8 +149,7 @@ bool UWingComponent::AddComponent(UBlueprint *BP, UClass *Class, UWingComponentR
Errors.Printf(TEXT("This blueprint cannot contain components.\n"));
return false;
}
TSet<FName> Names;
WingUtils::GetAllInUseNames(BP, Names);
TSet<FName> Names = WingUtils::GetAllInUseNames(BP);
if (Names.Contains(Name))
{
Errors.Printf(TEXT("There is already a variable or component named %s in %s.\n"),

View File

@@ -61,7 +61,7 @@ FName WingUtils::CheckInternalizeID(const FString &ExternalID, WingOut Errors)
return InternalID;
}
FName WingUtils::CheckProposedName(const FString &ExternalID, WingOut Errors)
FName WingUtils::CheckProposedName(const FString &ExternalID, const TSet<FName> &InUse, WingOut Errors)
{
FName InternalID = CheckInternalizeID(ExternalID, Errors);
if (InternalID.ToString().Len() > 100)
@@ -76,6 +76,11 @@ FName WingUtils::CheckProposedName(const FString &ExternalID, WingOut Errors)
UWingServer::SuggestManual(WingManual::Section::EscapeSequences);
return FName();
}
if (InUse.Contains(InternalID))
{
Errors.Printf(TEXT("ERROR: id %s is already in use"), *ExternalID);
return FName();
}
return InternalID;
}
@@ -483,8 +488,9 @@ UObject *WingUtils::GetGeneratedCDO(UBlueprint *BP)
return BP->GeneratedClass->GetDefaultObject();
}
void WingUtils::GetAllInUseNames(UBlueprint *BP, TSet<FName> &Names)
TSet<FName> WingUtils::GetAllInUseNames(UBlueprint *BP)
{
TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names, true);
FBlueprintEditorUtils::GetFunctionNameList(BP, Names);
FBlueprintEditorUtils::GetAllGraphNames(BP, Names);
@@ -496,6 +502,7 @@ void WingUtils::GetAllInUseNames(UBlueprint *BP, TSet<FName> &Names)
WBP->WidgetTree->GetAllWidgets(AllWidgets);
WingUtils::AddNamesToSet(Names, AllWidgets);
}
return Names;
}
// ============================================================

View File

@@ -611,9 +611,8 @@ bool WingVariables::CreateBlueprint(WingOut Errors)
ClearLinks();
// Check for name collisions against existing variables, components, and the like.
TSet<FName> Names;
WingUtils::GetAllInUseNames(Blueprint.Get(), Names);
if (!WingUtils::FindNoDuplicateNames(Names, BlueprintVariables.Variables, TEXT("variable or component"), Errors)) return false;
TSet<FName> InUse = WingUtils::GetAllInUseNames(Blueprint.Get());
if (!WingUtils::FindNoDuplicateNames(InUse, BlueprintVariables.Variables, TEXT("variable or component"), Errors)) return false;
// Create the variables.
for (const WingVariables::Var& V : BlueprintVariables.Variables)

View File

@@ -218,13 +218,14 @@ public:
// This routine is used when the LLM is proposing a new
// name in order to create a new graph, new node, or
// something like that. This verifies that the name is
// a valid external ID, it converts it to an internal ID,
// and it also verifies that it's a readable internal ID.
// If anything goes wrong, it prints an error and returns
// empty string.
// a valid external ID, it converts it to an internal
// ID, and it also verifies that it's a readable
// internal ID, and that it is not already in use.
// If anything goes wrong, it prints an error and
// returns empty string.
////////////////////////////////////////////////////////
static FName CheckProposedName(const FString &Name, WingOut Errors);
static FName CheckProposedName(const FString &Name, const TSet<FName> &InUse, WingOut Errors);
static FString FormatNodeTitle(const UEdGraphNode *Node);
@@ -250,7 +251,7 @@ public:
static TArray<UEdGraphNode*> AllNodes(UEdGraph *Graph);
static TArray<UBlueprint*> GetAncestorBlueprints(UBlueprint *BP, bool OldestFirst = false);
static UObject *GetGeneratedCDO(UBlueprint *BP);
static void GetAllInUseNames(UBlueprint *BP, TSet<FName> &Names);
static TSet<FName> GetAllInUseNames(UBlueprint *BP);
// ----- Graph pin helpers -----
static UEdGraphPin* CheckGetPin(UEdGraphNode* Node, FName PinName, WingOut Errors);