// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "ParserHelper.h" #include "BaseParser.h" #include "Scope.h" #include "UnrealTypeDefinitionInfo.h" #include "GeneratedCodeVersion.h" #include "ClassMaps.h" #include "RigVMDefines.h" class UClass; enum class EGeneratedCodeVersion : uint8; class FFeedbackContext; class UPackage; struct FManifestModule; class IScriptGeneratorPluginInterface; class FStringOutputDevice; class FUnrealSourceFile; class FScope; class FHeaderProvider; struct FDeclaration; /*----------------------------------------------------------------------------- Constants & types. -----------------------------------------------------------------------------*/ enum {MAX_NEST_LEVELS = 16}; /* Code nesting types. */ enum class ENestType { GlobalScope, Class, FunctionDeclaration, Interface, NativeInterface }; /** Types of statements to allow within a particular nesting block. */ enum class ENestAllowFlags { None = 0, Function = 1, // Allow Event declarations at this level. VarDecl = 2, // Allow variable declarations at this level. Class = 4, // Allow class definition heading. Return = 8, // Allow 'return' within a function. TypeDecl = 16, // Allow declarations which do not affect memory layout, such as structs, enums, and consts, but not implicit delegates ImplicitDelegateDecl = 32, // Allow implicit delegates (i.e. those not decorated with UDELEGATE) to be declared }; ENUM_CLASS_FLAGS(ENestAllowFlags) /** Options for GetVarType method */ enum class EGetVarTypeOptions { None = 0, OuterTypeDeprecated = 1, // Containing type has been deprecated NoAutoConst = 2, // Don't automatically mark properties as CPF_Const }; ENUM_CLASS_FLAGS(EGetVarTypeOptions) namespace EDelegateSpecifierAction { enum Type { DontParse, Parse }; } enum class ELayoutMacroType { None = -1, Array, ArrayEditorOnly, Bitfield, BitfieldEditorOnly, Field, FieldEditorOnly, FieldInitialized, }; /** Information for a particular nesting level. */ class FNestInfo { /** Link to the stack node. */ FScope* Scope; public: /** * Gets nesting scope. */ FScope* GetScope() const { return Scope; } /** * Sets nesting scope. */ void SetScope(FScope* InScope) { this->Scope = InScope; } /** Statement that caused the nesting. */ ENestType NestType; /** Types of statements to allow at this nesting level. */ ENestAllowFlags Allow; }; struct FIndexRange { int32 StartIndex; int32 Count; }; #ifndef UHT_DOCUMENTATION_POLICY_DEFAULT #define UHT_DOCUMENTATION_POLICY_DEFAULT false #endif struct FDocumentationPolicy { bool bClassOrStructCommentRequired = UHT_DOCUMENTATION_POLICY_DEFAULT; bool bFunctionToolTipsRequired = UHT_DOCUMENTATION_POLICY_DEFAULT; bool bMemberToolTipsRequired = UHT_DOCUMENTATION_POLICY_DEFAULT; bool bParameterToolTipsRequired = UHT_DOCUMENTATION_POLICY_DEFAULT; bool bFloatRangesRequired = UHT_DOCUMENTATION_POLICY_DEFAULT; }; ///////////////////////////////////////////////////// // FHeaderParser // // Header parser class. Extracts metadata from annotated C++ headers and gathers enough // information to autogenerate additional headers and other boilerplate code. // class FHeaderParser : public FBaseParser { friend class FRecordTokens; public: // Performs a preliminary parse of the text in the specified buffer, pulling out: // Class name and parent class name // Is it an interface // The list of other classes/interfaces it is dependent on // // It also splits the buffer up into: // ScriptText (text outside of #if CPP and #if DEFAULTS blocks) static void SimplifiedClassParse(FUnrealSourceFile& SourceFile, const TCHAR* Buffer, FStringOutputDevice& ScriptText); /** * Returns True if the given class name includes a valid Unreal prefix and matches up with the given original class Name. * * @param InNameToCheck - Name w/ potential prefix to check * @param OriginalClassName - Name of class w/ no prefix to check against */ static bool ClassNameHasValidPrefix(const FString& InNameToCheck, const FString& OriginalClassName); /** * Parse the given header. * * @param PackDef The owning package of the source * @param SourceFile The source file being parsed */ static void Parse(FUnrealPackageDefinitionInfo& PackageDef, FUnrealSourceFile& SourceFile); protected: friend struct FScriptLocation; friend struct FNativeClassHeaderGenerator; // Filename currently being parsed const FString Filename; // Was the first include in the file a validly formed auto-generated header include? bool bSpottedAutogeneratedHeaderInclude = false; // Current nest level, starts at 0. int32 NestLevel = 0; // Top nesting level. FNestInfo* TopNest = nullptr; /** * Gets current nesting scope. */ FScope* GetCurrentScope() const { return TopNest->GetScope(); } /** * Tells if parser is currently in a class. */ bool IsInAClass() const { int32 Index = 0; while (TopNest[Index].NestType != ENestType::GlobalScope) { if (TopNest[Index].NestType == ENestType::Class || TopNest->NestType == ENestType::Interface || TopNest->NestType == ENestType::NativeInterface) { return true; } --Index; } return false; } /** * Gets current class definition. */ FUnrealClassDefinitionInfo& GetCurrentClassDef() const { check(TopNest->NestType == ENestType::Class || TopNest->NestType == ENestType::Interface || TopNest->NestType == ENestType::NativeInterface); return UHTCastChecked(static_cast(TopNest->GetScope())->GetStructDef()); } // Information about all nesting levels. FNestInfo Nest[MAX_NEST_LEVELS]; // enum for complier directives used to build up the directive stack struct ECompilerDirective { enum Type { // this directive is insignificant and does not change the code generation at all Insignificant = 0, // this indicates we are in a WITH_EDITOR #if-Block WithEditor = 1<<0, // this indicates we are in a WITH_EDITORONLY_DATA #if-Block WithEditorOnlyData = 1<<1, }; }; /** * Compiler directive nest in which the parser currently is * NOTE: compiler directives are combined when more are added onto the stack, so * checking the only the top of stack is enough to determine in which #if-Block(s) the current code * is. * * ex. Stack.Num() == 1 while entering #if WITH_EDITOR: * CompilerDirectiveStack[1] == CompilerDirectiveStack[0] | ECompilerDirective::WithEditor == * CompilerDirecitveStack[1] == CompilerDirectiveStack.Num()-1 | ECompilerDirective::WithEditor * * ex. Stack.Num() == 2 while entering #if WITH_EDITOR: * CompilerDirectiveStack[3] == CompilerDirectiveStack[0] | CompilerDirectiveStack[1] | CompilerDirectiveStack[2] | ECompilerDirective::WithEditor == * CompilerDirecitveStack[3] == CompilerDirectiveStack.Num()-1 | ECompilerDirective::WithEditor */ TArray CompilerDirectiveStack; // Return the top level compiler directive state uint32 GetCurrentCompilerDirective() const { return CompilerDirectiveStack.IsEmpty() ? 0 : CompilerDirectiveStack.Last(); } // Pushes the Directive specified to the CompilerDirectiveStack according to the rules described above void PushCompilerDirective(ECompilerDirective::Type Directive) { CompilerDirectiveStack.Push(Directive | GetCurrentCompilerDirective()); } // Removes the top most compiler directive stack entry void PopCompilerDirective() { if (CompilerDirectiveStack.Num() < 1) { Throwf(TEXT("Unmatched '#endif' in class or global scope")); } CompilerDirectiveStack.Pop(); } /** * The starting class flags (i.e. the class flags that were set before the * CLASS_RecompilerClear mask was applied) for the class currently being compiled */ uint32 PreviousClassFlags; // For new-style classes, used to keep track of an unmatched {} pair bool bEncounteredNewStyleClass_UnmatchedBrackets; // Indicates that UCLASS/USTRUCT/UINTERFACE has already been parsed in this .h file.. bool bHaveSeenUClass; // Indicates that a GENERATED_UCLASS_BODY or GENERATED_BODY has been found in the UClass. bool bClassHasGeneratedBody; // Indicates that a GENERATED_UINTERFACE_BODY has been found in the UClass. bool bClassHasGeneratedUInterfaceBody; // Indicates that a GENERATED_IINTERFACE_BODY has been found in the UClass. bool bClassHasGeneratedIInterfaceBody; // public, private, etc at the current parse spot EAccessSpecifier CurrentAccessSpecifier; //////////////////////////////////////////////////// // List of all used identifiers for net service function declarations (every function must be unique) TMap UsedRPCIds; // List of all net service functions with undeclared response functions TMap RPCsNeedingHookup; // Constructor. explicit FHeaderParser(FUnrealPackageDefinitionInfo& InPackageDef, FUnrealSourceFile& InSourceFile); // Returns true if the token is a dynamic delegate declaration bool IsValidDelegateDeclaration(const FToken& Token) const; // Returns true if the current token is a bitfield type bool IsBitfieldProperty(ELayoutMacroType LayoutMacroType); // Parse the parameter list of a function or delegate declaration void ParseParameterList(FUnrealFunctionDefinitionInfo& FunctionDef, bool bExpectCommaBeforeName = false, TMap* MetaData = NULL, EGetVarTypeOptions Options = EGetVarTypeOptions::None); public: // Throws if a specifier value wasn't provided static void RequireSpecifierValue(const FUHTMessageProvider& Context, const FPropertySpecifier& Specifier, bool bRequireExactlyOne = false); static FString RequireExactlyOneSpecifierValue(const FUHTMessageProvider& Context, const FPropertySpecifier& Specifier); /** * Find a field in the specified context. Starts with the specified scope, then iterates * through the Outer chain until the field is found. * * @param InScope scope to start searching for the field in * @param InIdentifier name of the field we're searching for * @param bIncludeParents whether to allow searching in the scope of a parent struct * @param FieldClass class of the field to search for. used to e.g. search for functions only * @param Thing hint text that will be used in the error message if an error is encountered * * @return a pointer to a UField with a name matching InIdentifier, or NULL if it wasn't found */ static FUnrealFunctionDefinitionInfo* FindFunction(const FUnrealStructDefinitionInfo& InScope, const TCHAR* InIdentifier, bool bIncludeParents = true, const TCHAR* Thing = nullptr); static FUnrealPropertyDefinitionInfo* FindProperty(const FUnrealStructDefinitionInfo& InScope, const TCHAR* InIdentifier, bool bIncludeParents = true, const TCHAR* Thing = nullptr); // Checks ToValidate to make sure that its associated sparse class data struct, if one exists, is a valid structure to use for storing sparse class data. static void CheckSparseClassData(const FUnrealStructDefinitionInfo& StructDef); // Validates that ClassFlags are set appropriately static void ValidateClassFlags(const FUnrealClassDefinitionInfo& ToValidate); protected: //@TODO: Remove this method static void ParseClassName(const TCHAR* Temp, FString& ClassName); /** * @param Input An input string, expected to be a script comment. * @return The input string, reformatted in such a way as to be appropriate for use as a tooltip. */ static FString FormatCommentForToolTip(const FString& Input); /** * Retrieves parameter comments / tooltips from the function comment * @param Input An input string, expected to be a script comment. * @return The map of parameter name to comment per parameter */ static TMap GetParameterToolTipsFromFunctionComment(const FString& Input); // High-level compiling functions. /** * Parses given source file. * * @returns Compilation result enum. */ void ParseHeader(); void CompileDirective(); FUnrealEnumDefinitionInfo& CompileEnum(); FUnrealScriptStructDefinitionInfo& CompileStructDeclaration(); bool CompileDeclaration(TArray& DelegatesToFixup, FToken& Token); /** Skip C++ (noexport) declaration. */ bool SkipDeclaration(FToken& Token); /** Similar to MatchSymbol() but will return to the exact location as on entry if the symbol was not found. */ bool SafeMatchSymbol(const TCHAR Match); FUnrealClassDefinitionInfo& ParseClassNameDeclaration(FString& DeclaredClassName, FString& RequiredAPIMacroIfPresent); /** The property style of a variable declaration being parsed */ struct EPropertyDeclarationStyle { enum Type { None, UPROPERTY }; }; /** * Resets current class data back to its defaults. */ void ResetClassData(); /** * Create new function object based on given info structure. */ FUnrealFunctionDefinitionInfo& CreateFunction(const TCHAR* FuncName, FFuncInfo&& FuncInfo, EFunctionType InFunctionType) const; FUnrealClassDefinitionInfo& CompileClassDeclaration(); FUnrealFunctionDefinitionInfo& CompileDelegateDeclaration(const FStringView& DelegateIdentifier, EDelegateSpecifierAction::Type SpecifierAction = EDelegateSpecifierAction::DontParse); FUnrealFunctionDefinitionInfo& CompileFunctionDeclaration(); void CompileVariableDeclaration (FUnrealStructDefinitionInfo& StructDef); void CompileInterfaceDeclaration(); void CompileRigVMMethodDeclaration(FUnrealStructDefinitionInfo& StructDef); void ParseRigVMMethodParameters(FUnrealStructDefinitionInfo& StructDef); FUnrealClassDefinitionInfo* ParseInterfaceNameDeclaration(FString& DeclaredInterfaceName, FString& RequiredAPIMacroIfPresent); bool TryParseIInterfaceClass(); bool CompileStatement(TArray& DelegatesToFixup); // Checks to see if a particular kind of command is allowed on this nesting level. bool IsAllowedInThisNesting(ENestAllowFlags AllowFlags); // Make sure that a particular kind of command is allowed on this nesting level. // If it's not, issues a compiler error referring to the token and the current // nesting level. void CheckAllow(const TCHAR* Thing, ENestAllowFlags AllowFlags); void SkipStatements( int32 SubCount, const TCHAR* ErrorTag ); /** * Parses a variable or return value declaration and determines the variable type and property flags. * * @param Scope struct to create the property in * @param Options options to control how the variable type is parsed * @param VarProperty will be filled in with type and property flag data for the property declaration that was parsed * @param Disallow contains a mask of variable modifiers that are disallowed in this context * @param OuterPropertyType only specified when compiling the inner properties for arrays or maps. * @param OuterPropertyFlags flags associated with the outer property being compiled. * @param PropertyDeclarationStyle if the variable is defined with a UPROPERTY * @param VariableCategory what kind of variable is being parsed * @param ParsedVarIndexRange The source text [Start, End) index range for the parsed type. */ void GetVarType( FScope* Scope, EGetVarTypeOptions Options, FPropertyBase& VarProperty, EPropertyFlags Disallow, EUHTPropertyType OuterPropertyType, EPropertyFlags OuterPropertyFlags, EPropertyDeclarationStyle::Type PropertyDeclarationStyle, EVariableCategory VariableCategory, FIndexRange* ParsedVarIndexRange = nullptr, ELayoutMacroType* OutLayoutMacroType = nullptr); /** * Parses a variable name declaration and creates a new FProperty object. * * @param ParentStruct struct to create the property in * @param VarProperty type and propertyflag info for the new property (inout) * @param VariableCategory what kind of variable is being created * * @return a reference to the new property if successful, or NULL if there was no property to parse */ FUnrealPropertyDefinitionInfo& GetVarNameAndDim( FUnrealStructDefinitionInfo& ParentStruct, FPropertyBase& VarProperty, EVariableCategory VariableCategory, ELayoutMacroType LayoutMacroType = ELayoutMacroType::None); /** * Parses optional metadata text. * * @param MetaData the metadata map to store parsed metadata in * @param FieldName the field being parsed (used for logging) * * @return true if metadata was specified */ void ParseFieldMetaData(TMap& MetaData, const TCHAR* FieldName); /** * Formats the current comment, if any, and adds it to the metadata as a tooltip. * * @param MetaData the metadata map to store the tooltip in */ void AddFormattedPrevCommentAsTooltipMetaData(TMap& MetaData); /** * Tries to parse the token as an access protection specifier (public:, protected:, or private:) * * @return EAccessSpecifier this is, or zero if it is none */ EAccessSpecifier ParseAccessProtectionSpecifier(const FToken& Token); const TCHAR* NestTypeName( ENestType NestType ); FUnrealClassDefinitionInfo* GetQualifiedClass(const TCHAR* Thing); /** * Increase the nesting level, setting the new top nesting level to * the one specified. If pushing a function or state and it overrides a similar * thing declared on a lower nesting level, verifies that the override is legal. * * @param NestType the new nesting type * @param InNode @todo */ void PushNest(ENestType NestType, FUnrealStructDefinitionInfo* InNodeDef, FUnrealSourceFile* SourceFile = nullptr); void PopNest(ENestType NestType, const TCHAR* Descr); /** * Tasks that need to be done after popping function declaration * from parsing stack. * * @param PoppedFunction Function that have just been popped. */ void PostPopFunctionDeclaration(FUnrealFunctionDefinitionInfo& PoppedFunctionDef); /** * Tasks that need to be done after popping interface definition * from parsing stack. * * @param CurrentInterfaceDef Interface that have just been popped. */ void PostPopNestInterface(FUnrealClassDefinitionInfo& CurrentInterfaceDef); /** * Tasks that need to be done after popping class definition * from parsing stack. * * @param CurrentClassDef Class that have just been popped. */ void PostPopNestClass(FUnrealClassDefinitionInfo& CurrentClassDef); /** * Binds all delegate properties declared in ValidationScope the delegate functions specified in the variable declaration, verifying that the function is a valid delegate * within the current scope. This must be done once the entire class has been parsed because instance delegate properties must be declared before the delegate declaration itself. * * @todo: this function will no longer be required once the post-parse fixup phase is added (TTPRO #13256) * * @param Struct the struct to validate delegate properties for * @param Scope the current scope * @param DelegateCache cached map of delegates that have already been found; used for faster lookup. */ void FixupDelegateProperties(FUnrealStructDefinitionInfo& StructDef, FScope& Scope, TMap& DelegateCache); // Retry functions. void InitScriptLocation( FScriptLocation& Retry ); void ReturnToLocation( const FScriptLocation& Retry, bool Binary=1, bool Text=1 ); void ValidateTypeIsDeprecated(EVariableCategory VariableCategory, FUnrealTypeDefinitionInfo* TypeDef); void ValidatePropertyIsDeprecatedIfNecessary(bool bOuterTypeDeprecated, EVariableCategory VariableCategory, const FPropertyBase& VarProperty, EUHTPropertyType OuterPropertyType, EPropertyFlags OuterPropertyFlags); // Cache of ScriptStructs that have been validated for Net Replication and RPC TSet ScriptStructsValidForNet; /** * Validate that a ScriptStruct is ok to be Replicated or Sent in an RPC. * * @param OriginStructName The Name of the ScriptStruct to check * @param InStruct The ScriptStruct to check */ bool ValidateScriptStructOkForNet(const FString& OriginStructName, FUnrealScriptStructDefinitionInfo& InStructDef); private: // Definition of package being parsed FUnrealPackageDefinitionInfo& PackageDef; // True if the module currently being parsed is part of the engine, as opposed to being part of a game bool bIsCurrentModulePartOfEngine; /** * Tries to match constructor parameter list. Assumes that constructor * name is already matched. * * If fails it reverts all parsing done. * * @param Token Token to start parsing from. * * @returns True if matched. False otherwise. */ bool TryToMatchConstructorParameterList(FToken Token); // Parses possible version declaration in generated code, e.g. GENERATED_BODY(). void CompileVersionDeclaration(FUnrealStructDefinitionInfo& StructDef); // Verifies that all specified class's UProperties with function associations have valid targets void VerifyPropertyMarkups(FUnrealClassDefinitionInfo& TargetClassDef); // Verifies the target function meets the criteria for a blueprint property getter void VerifyBlueprintPropertyGetter(FUnrealPropertyDefinitionInfo& PropertyDef, FUnrealFunctionDefinitionInfo* TargetFuncDef); // Verifies the target function meets the criteria for a blueprint property setter void VerifyBlueprintPropertySetter(FUnrealPropertyDefinitionInfo& PropertyDef, FUnrealFunctionDefinitionInfo* TargetFuncDef); // Verifies the target function meets the criteria for a replication notify callback void VerifyRepNotifyCallback(FUnrealPropertyDefinitionInfo& PropertyDef, FUnrealFunctionDefinitionInfo* TargetFuncDef); // Verifies the target property meets the criteria for Getter/Setter accessor void VerifyGetterSetterAccessorProperties(const FUnrealClassDefinitionInfo& TargetClassDef, FUnrealPropertyDefinitionInfo& PropertyDef); // Vefifies the target property meets the criteria for FieldNotify void VerifyNotifyValueChangedProperties(FUnrealPropertyDefinitionInfo& PropertyDef); // Verifies that all specified class's UFunction void VerifyFunctionsMarkups(FUnrealClassDefinitionInfo& TargetClassDef); // Verifies the target function meets the criteria for FieldNotify void VerifyNotifyValueChangedFunction(const FUnrealFunctionDefinitionInfo& TargetFuncDef); // Constructs the policy from a string static FDocumentationPolicy GetDocumentationPolicyFromName(const FUHTMessageProvider& Context, const FString& PolicyName); // Constructs the policy for documentation checks for a given struct static FDocumentationPolicy GetDocumentationPolicyForStruct(FUnrealStructDefinitionInfo& StructDef); // Property types to provide UI Min and Max ranges static TArray PropertyCPPTypesRequiringUIRanges; // Returns true if a given CPP types required ui checking static bool DoesCPPTypeRequireDocumentation(const FString& CPPType); // Validates the documentation for a given enum void CheckDocumentationPolicyForEnum(FUnrealEnumDefinitionInfo& EnumDef, const TMap& MetaData, const TArray>& Entries); // Validates the documentation for a given struct void CheckDocumentationPolicyForStruct(FUnrealStructDefinitionInfo& StructDef); // Validates the documentation for a given method void CheckDocumentationPolicyForFunc(FUnrealClassDefinitionInfo& ClassDef, FUnrealFunctionDefinitionInfo& FunctionDef); // Checks if a valid range has been found on the provided metadata bool CheckUIMinMaxRangeFromMetaData(const FString& UIMin, const FString& UIMax); // Emits either a log message, warning, or error about a member pointer according to the behavior specified by the PointerMemberBehavior argument void ConditionalLogPointerUsage(EPointerMemberBehavior PointerMemberBehavior, const TCHAR* PointerTypeDesc, FString&& PointerTypeDecl, const TCHAR* AlternativeTypeDesc); // Check to see if the declaration is a constructor static bool CheckForConstructor(FUnrealStructDefinitionInfo& StructDef, const FDeclaration& Declaration); // Check to see if the declaration is a serialize static bool CheckForSerialize(FUnrealStructDefinitionInfo& StructDef, const FDeclaration& Declaration); // Check to see if the declaration is a property getter function bool CheckForPropertyGetterFunction(FUnrealStructDefinitionInfo& StructDef, const FDeclaration& Declaration); // Check to see if the declaration is a property setter function bool CheckForPropertySetterFunction(FUnrealStructDefinitionInfo& StructDef, const FDeclaration& Declaration); // Names that cannot be used enums, UStructs, or UClasses static TArray ReservedTypeNames; public: /** * Checks if the given token uses one of the reserved type names. * * @param TypeName String of the type to check (For UObject/UClass, use the stripped name) * @return True if the TypeName is a reserved name */ static bool IsReservedTypeName(const FString& TypeName); static const FName NAME_InputText; static const FName NAME_OutputText; static const FName NAME_ConstantText; static const FName NAME_VisibleText; static const FName NAME_SingletonText; static const TCHAR* TArrayText; static const TCHAR* TEnumAsByteText; static const TCHAR* GetRefText; static const TCHAR* FTArrayText; static const TCHAR* FTArrayViewText; static const TCHAR* GetArrayText; static const TCHAR* GetArrayViewText; static const TCHAR* FTScriptInterfaceText; }; ///////////////////////////////////////////////////// // FHeaderPreParser class FHeaderPreParser : public FBaseParser { public: FHeaderPreParser(FUnrealSourceFile& InSourceFile) : FBaseParser(InSourceFile) { } TSharedRef ParseClassDeclaration( const TCHAR* InputText, int32 InLineNumber, bool bClassIsAnInterface, const TCHAR* StartingMatchID ); TSharedRef ParseEnumDeclaration( const TCHAR* InputText, int32 InLineNumber ); TSharedRef ParseStructDeclaration( const TCHAR* InputText, int32 InLineNumber ); }; class FRecordTokens { public: explicit FRecordTokens(FHeaderParser& InParser, FUnrealStructDefinitionInfo* InStructDef, FToken* InToken); ~FRecordTokens(); bool Stop(); private: FHeaderParser& Parser; FUnrealStructDefinitionInfo* StructDef; uint32 CurrentCompilerDirective = 0; };