diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index ccbf348b00a686..0ad16d12accf74 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -264,7 +264,7 @@ class NamedDecl : public Decl { NamedDecl(Kind DK, DeclContext *DC, SourceLocation L, DeclarationName N) : Decl(DK, DC, L), Name(N) // @unreal: BEGIN - , UnrealType(UnrealType::UT_None), UnrealSpecifiers(), UnrealMetadata() + , UnrealType(UnrealType::UT_None), UnrealExported(false), UnrealSpecifiers(), UnrealMetadata() // @unreal: END {} @@ -273,6 +273,12 @@ class NamedDecl : public Decl { /// The Unreal type flag for this decl. unsigned UnrealType; + /// If true, this type is exported from modules as far as Unreal + /// is concerned. This is set when _API is present on the type, + /// and can be used to track that _API is present regardless of + /// what the macro expands to. + bool UnrealExported; + /// The Unreal specifiers that are associated with this decl. /// todo: Make this a map for faster lookups. std::vector UnrealSpecifiers; diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.Unreal.h b/clang/include/clang/ASTMatchers/ASTMatchers.Unreal.h index be353d0cea3151..f200c4e95702bd 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.Unreal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.Unreal.h @@ -501,4 +501,16 @@ AST_MATCHER(QualType, isExpensiveToCopy) { return IsExpensive && *IsExpensive; } +/// Matches if a named decl has an ..._API macro applied to it. +/// +/// Given +/// \code +/// class MYMODULE_API FSomeDecl {}; +/// \endcode +/// \c namedDecl(isUnrealExported()) +/// matches the class. +AST_MATCHER(NamedDecl, isUnrealExported) { + return Node.UnrealExported; +} + // @unreal: END \ No newline at end of file diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 522c8f1a9d2003..d859a4e5a729d5 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -995,6 +995,7 @@ PRAGMA_ANNOTATION(unreal_uinterface) PRAGMA_ANNOTATION(unreal_ustruct) PRAGMA_ANNOTATION(unreal_specifier) PRAGMA_ANNOTATION(unreal_metadata_specifier) +PRAGMA_ANNOTATION(unreal_exported) // @unreal: END #undef PRAGMA_ANNOTATION diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.Unreal.h b/clang/lib/ASTMatchers/Dynamic/Registry.Unreal.h index a1b7a77a85adf1..c2c7a27de03107 100644 --- a/clang/lib/ASTMatchers/Dynamic/Registry.Unreal.h +++ b/clang/lib/ASTMatchers/Dynamic/Registry.Unreal.h @@ -18,4 +18,5 @@ REGISTER_MATCHER(isMissingDllImportOrExport); REGISTER_MATCHER(isPODType); REGISTER_MATCHER(hasRedundantNamespacing); REGISTER_MATCHER(isExpensiveToCopy); +REGISTER_MATCHER(isUnrealExported); // @unreal: END \ No newline at end of file diff --git a/clang/lib/Lex/UnrealEnginePPTagger.cpp b/clang/lib/Lex/UnrealEnginePPTagger.cpp index 67ac72d9debcaa..ecabbd7b48476d 100644 --- a/clang/lib/Lex/UnrealEnginePPTagger.cpp +++ b/clang/lib/Lex/UnrealEnginePPTagger.cpp @@ -54,30 +54,33 @@ void UnrealEnginePPTagger::MacroExpands(const Token &MacroNameTok, const MacroArgs *Args) { if (MacroNameTok.isAnyIdentifier()) { llvm::StringRef MacroName = MacroNameTok.getIdentifierInfo()->getName(); - bool IsRecognised = false; + bool RequiresParameterHandling = false; std::vector TokensToPush; if (MacroName == "UCLASS") { TokensToPush.push_back( TokenInfo(tok::TokenKind::annot_unreal_uclass, nullptr)); - IsRecognised = true; + RequiresParameterHandling = true; } else if (MacroName == "USTRUCT") { TokensToPush.push_back( TokenInfo(tok::TokenKind::annot_unreal_ustruct, nullptr)); - IsRecognised = true; + RequiresParameterHandling = true; } else if (MacroName == "UINTERFACE") { TokensToPush.push_back( TokenInfo(tok::TokenKind::annot_unreal_uinterface, nullptr)); - IsRecognised = true; + RequiresParameterHandling = true; } else if (MacroName == "UPROPERTY") { TokensToPush.push_back( TokenInfo(tok::TokenKind::annot_unreal_uproperty, nullptr)); - IsRecognised = true; + RequiresParameterHandling = true; } else if (MacroName == "UFUNCTION") { TokensToPush.push_back( TokenInfo(tok::TokenKind::annot_unreal_ufunction, nullptr)); - IsRecognised = true; + RequiresParameterHandling = true; + } else if (MacroName.ends_with("_API")) { + TokensToPush.push_back( + TokenInfo(tok::TokenKind::annot_unreal_exported, nullptr)); } - if (IsRecognised && Args != nullptr) { + if (RequiresParameterHandling && Args != nullptr) { assert(Args->getNumMacroArguments() == 1 && "Expected U* specifier to only have one (varargs) argument"); const Token *ArgTokens = Args->getUnexpArgument(0); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index f6c172fbfa1dd8..e38ed8a2b4ecd5 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4720,7 +4720,7 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, // @unreal: BEGIN if (Tok.isOneOf(tok::annot_unreal_ufunction, tok::annot_unreal_uproperty, tok::annot_unreal_specifier, - tok::annot_unreal_metadata_specifier)) { + tok::annot_unreal_metadata_specifier, tok::annot_unreal_exported)) { if (Tok.getAnnotationValue() == nullptr) { HandlePragmaUnreal(Tok.getKind(), UnrealSpecifier()); } else { diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 789244682c4cd8..d933d4b50c737f 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -3411,6 +3411,7 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclarationWithPragmas( case tok::annot_unreal_uproperty: case tok::annot_unreal_specifier: case tok::annot_unreal_metadata_specifier: + case tok::annot_unreal_exported: if (Tok.getAnnotationValue() == nullptr) { HandlePragmaUnreal(Tok.getKind(), UnrealSpecifier()); } else { diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index 167471f04b3add..4b4b369ee0b2b4 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -727,7 +727,8 @@ void Parser::ConsumePragmaUnreal() { while (Tok.isOneOf(tok::annot_unreal_uclass, tok::annot_unreal_uinterface, tok::annot_unreal_ustruct, tok::annot_unreal_ufunction, tok::annot_unreal_uproperty, tok::annot_unreal_specifier, - tok::annot_unreal_metadata_specifier)) { + tok::annot_unreal_metadata_specifier, + tok::annot_unreal_exported)) { if (Tok.getAnnotationValue() == nullptr) { HandlePragmaUnreal(Tok.getKind(), UnrealSpecifier()); } else { @@ -741,14 +742,16 @@ void Parser::CheckNoPragmaUnreal() { if (Tok.isOneOf(tok::annot_unreal_uclass, tok::annot_unreal_uinterface, tok::annot_unreal_ustruct, tok::annot_unreal_ufunction, tok::annot_unreal_uproperty, tok::annot_unreal_specifier, - tok::annot_unreal_metadata_specifier)) { + tok::annot_unreal_metadata_specifier, + tok::annot_unreal_exported)) { Diag(Tok.getLocation(), diag::err_expected_member_name_or_semi) << SourceRange(); assert(!Tok.isOneOf(tok::annot_unreal_uclass, tok::annot_unreal_uinterface, tok::annot_unreal_ustruct, tok::annot_unreal_ufunction, tok::annot_unreal_uproperty, tok::annot_unreal_specifier, - tok::annot_unreal_metadata_specifier)); + tok::annot_unreal_metadata_specifier, + tok::annot_unreal_exported)); } } diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index f7cac07be12275..f5f55b8f7dfc27 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -51,42 +51,74 @@ Sema::PragmaStackSentinelRAII::~PragmaStackSentinelRAII() { } // @unreal: BEGIN +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wswitch-enum" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4062) +#endif void Sema::AddUnrealSpecifiersForDecl(Decl *D) { - if (this->UnrealStack.size() > 0) { - if (NamedDecl *ND = dyn_cast(D)) { - if (FieldDecl *FD = dyn_cast(ND)) { - if (this->UnrealStack[0].Kind == tok::annot_unreal_uproperty) { + if (NamedDecl *ND = dyn_cast(D)) { + while (this->UnrealStack.size() > 0) { + const auto &Current = this->UnrealStack[0]; + switch (Current.Kind) { + case tok::annot_unreal_exported: { + ND->UnrealExported = true; + break; + } + case tok::annot_unreal_uproperty: { + if (FieldDecl *FD = dyn_cast(ND)) { ND->UnrealType = UnrealType::UT_UProperty; } + break; } - if (CXXMethodDecl *MD = dyn_cast(ND)) { - if (this->UnrealStack[0].Kind == tok::annot_unreal_ufunction) { + case tok::annot_unreal_ufunction: { + if (CXXMethodDecl *MD = dyn_cast(ND)) { ND->UnrealType = UnrealType::UT_UFunction; } + break; } - if (RecordDecl *RD = dyn_cast(ND)) { - if (this->UnrealStack[0].Kind == tok::annot_unreal_uclass) { + case tok::annot_unreal_uclass: { + if (RecordDecl *RD = dyn_cast(ND)) { ND->UnrealType = UnrealType::UT_UClass; - } else if (this->UnrealStack[0].Kind == tok::annot_unreal_uinterface) { + } + break; + } + case tok::annot_unreal_uinterface: { + if (RecordDecl *RD = dyn_cast(ND)) { ND->UnrealType = UnrealType::UT_UInterface; - } else if (this->UnrealStack[0].Kind == tok::annot_unreal_ustruct) { + } + break; + } + case tok::annot_unreal_ustruct: { + if (RecordDecl *RD = dyn_cast(ND)) { ND->UnrealType = UnrealType::UT_UStruct; } + break; + } + case tok::annot_unreal_specifier: { + if (ND->UnrealType != UnrealType::UT_None) { + ND->UnrealSpecifiers.push_back(Current.SpecData); + } + break; } - if (ND->UnrealType != UnrealType::UT_None) { - for (int i = 1; i < this->UnrealStack.size(); i++) { - const auto &Val = this->UnrealStack[i]; - if (Val.Kind == tok::annot_unreal_specifier) { - ND->UnrealSpecifiers.push_back(Val.SpecData); - } else if (Val.Kind == tok::annot_unreal_metadata_specifier) { - ND->UnrealMetadata.push_back(Val.SpecData); - } + case tok::annot_unreal_metadata_specifier: { + if (ND->UnrealType != UnrealType::UT_None) { + ND->UnrealMetadata.push_back(Current.SpecData); } + break; + } } - this->UnrealStack.clear(); + this->UnrealStack.erase(this->UnrealStack.begin()); } } } +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif // @unreal: END void Sema::AddAlignmentAttributesForRecord(RecordDecl *RD) {