Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track use of MODULE_API macros and add isUnrealExported() AST matcher #4

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{}

Expand All @@ -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<UnrealSpecifier> UnrealSpecifiers;
Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/ASTMatchers/ASTMatchers.Unreal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions clang/lib/ASTMatchers/Dynamic/Registry.Unreal.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ REGISTER_MATCHER(isMissingDllImportOrExport);
REGISTER_MATCHER(isPODType);
REGISTER_MATCHER(hasRedundantNamespacing);
REGISTER_MATCHER(isExpensiveToCopy);
REGISTER_MATCHER(isUnrealExported);
// @unreal: END
17 changes: 10 additions & 7 deletions clang/lib/Lex/UnrealEnginePPTagger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<TokenInfo> 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);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
9 changes: 6 additions & 3 deletions clang/lib/Parse/ParsePragma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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));
}
}

Expand Down
70 changes: 51 additions & 19 deletions clang/lib/Sema/SemaAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<NamedDecl>(D)) {
if (FieldDecl *FD = dyn_cast<FieldDecl>(ND)) {
if (this->UnrealStack[0].Kind == tok::annot_unreal_uproperty) {
if (NamedDecl *ND = dyn_cast<NamedDecl>(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<FieldDecl>(ND)) {
ND->UnrealType = UnrealType::UT_UProperty;
}
break;
}
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(ND)) {
if (this->UnrealStack[0].Kind == tok::annot_unreal_ufunction) {
case tok::annot_unreal_ufunction: {
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(ND)) {
ND->UnrealType = UnrealType::UT_UFunction;
}
break;
}
if (RecordDecl *RD = dyn_cast<RecordDecl>(ND)) {
if (this->UnrealStack[0].Kind == tok::annot_unreal_uclass) {
case tok::annot_unreal_uclass: {
if (RecordDecl *RD = dyn_cast<RecordDecl>(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<RecordDecl>(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<RecordDecl>(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) {
Expand Down