Skip to content

Commit

Permalink
Merge pull request #6 from RedpointGames/iwyu-detection
Browse files Browse the repository at this point in the history
Add include-what-you-use detection to find unneeded headers
  • Loading branch information
hach-que authored Aug 1, 2024
2 parents 1012fab + 749dd18 commit 87ab46b
Show file tree
Hide file tree
Showing 29 changed files with 1,287 additions and 405 deletions.
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/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -1494,4 +1494,5 @@ def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInCon

// @unreal: BEGIN
def UnrealEngine : DiagGroup<"unreal-engine">;
def IncludeWhatYouUse : DiagGroup<"include-what-you-use">;
// @unreal: END
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -3467,6 +3467,15 @@ def warn_unreal_data_discarded_on_new_specifier : Warning<
InGroup<UnrealEngine>;
def note_unreal_data_previous_location : Note<
"previously discarded metadata was located here">;
def err_unreal_annotation_found_in_unexpected_place : Error<
"Unreal Engine macro used in unexpected place">;
def warn_iwyu_remove_unused_header : Warning<
"#include header is completely unused and can be removed">,
InGroup<IncludeWhatYouUse>;
def warn_iwyu_replace_unused_header : Warning<
"#include header does not contain any immediate dependency "
"and can be replaced with an #include to '%0' instead">,
InGroup<IncludeWhatYouUse>;
// @unreal: END

def err_anonymous_property: Error<
Expand Down
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
20 changes: 20 additions & 0 deletions clang/include/clang/Lex/PPCallbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class IdentifierInfo;
class MacroDefinition;
class MacroDirective;
class MacroArgs;
class LookupResult;
class Scope;
class DeclContext;

/// This interface provides a way to observe the actions of the
/// preprocessor as it does its thing.
Expand Down Expand Up @@ -428,6 +431,12 @@ class PPCallbacks {
/// \param IfLoc the source location of the \#if/\#ifdef/\#ifndef directive.
virtual void Endif(SourceLocation Loc, SourceLocation IfLoc) {
}

// @unreal: BEGIN
/// Hook when the semantic system performs a successful lookup.
virtual void SemaSuccessfulLookup(LookupResult &R, Scope *S) {}
virtual void SemaSuccessfulLookup(LookupResult &R, DeclContext *DC) {}
// @unreal: END
};

/// Simple wrapper class for chaining callbacks.
Expand Down Expand Up @@ -702,6 +711,17 @@ class PPChainedCallbacks : public PPCallbacks {
First->Endif(Loc, IfLoc);
Second->Endif(Loc, IfLoc);
}

// @unreal: BEGIN
void SemaSuccessfulLookup(LookupResult &R, Scope *S) override {
First->SemaSuccessfulLookup(R, S);
Second->SemaSuccessfulLookup(R, S);
}
void SemaSuccessfulLookup(LookupResult &R, DeclContext *DC) override {
First->SemaSuccessfulLookup(R, DC);
Second->SemaSuccessfulLookup(R, DC);
}
// @unreal: END
};

} // end namespace clang
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Parse/Parser.Unreal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
void PPLexWithUnrealAnnotationTokens();
52 changes: 31 additions & 21 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,10 @@ class Parser : public CodeCompletionHandler {
return ParseTopLevelDecl(Result, IS);
}

// @unreal: BEGIN
#include "Parser.Unreal.h"
// @unreal: END

/// ConsumeToken - Consume the current 'peek token' and lex the next one.
/// This does not work with special tokens: string literals, code completion,
/// annotation tokens and balanced tokens must be handled using the specific
Expand All @@ -518,7 +522,9 @@ class Parser : public CodeCompletionHandler {
assert(!isTokenSpecial() &&
"Should consume special tokens with Consume*Token");
PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
// @unreal: BEGIN
PPLexWithUnrealAnnotationTokens();
// @unreal: END
return PrevTokLocation;
}

Expand All @@ -528,7 +534,9 @@ class Parser : public CodeCompletionHandler {
assert(!isTokenSpecial() &&
"Should consume special tokens with Consume*Token");
PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
// @unreal: BEGIN
PPLexWithUnrealAnnotationTokens();
// @unreal: END
return true;
}

Expand Down Expand Up @@ -606,15 +614,19 @@ class Parser : public CodeCompletionHandler {
void UnconsumeToken(Token &Consumed) {
Token Next = Tok;
PP.EnterToken(Consumed, /*IsReinject*/true);
PP.Lex(Tok);
// @unreal: BEGIN
PPLexWithUnrealAnnotationTokens();
// @unreal: END
PP.EnterToken(Next, /*IsReinject*/true);
}

SourceLocation ConsumeAnnotationToken() {
assert(Tok.isAnnotation() && "wrong consume method");
SourceLocation Loc = Tok.getLocation();
PrevTokLocation = Tok.getAnnotationEndLoc();
PP.Lex(Tok);
// @unreal: BEGIN
PPLexWithUnrealAnnotationTokens();
// @unreal: END
return Loc;
}

Expand All @@ -629,7 +641,9 @@ class Parser : public CodeCompletionHandler {
--ParenCount; // Don't let unbalanced )'s drive the count negative.
}
PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
// @unreal: BEGIN
PPLexWithUnrealAnnotationTokens();
// @unreal: END
return PrevTokLocation;
}

Expand All @@ -645,7 +659,9 @@ class Parser : public CodeCompletionHandler {
}

PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
// @unreal: BEGIN
PPLexWithUnrealAnnotationTokens();
// @unreal: END
return PrevTokLocation;
}

Expand All @@ -661,7 +677,9 @@ class Parser : public CodeCompletionHandler {
}

PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
// @unreal: BEGIN
PPLexWithUnrealAnnotationTokens();
// @unreal: END
return PrevTokLocation;
}

Expand All @@ -673,7 +691,9 @@ class Parser : public CodeCompletionHandler {
assert(isTokenStringLiteral() &&
"Should only consume string literals with this method");
PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
// @unreal: BEGIN
PPLexWithUnrealAnnotationTokens();
// @unreal: END
return PrevTokLocation;
}

Expand All @@ -685,7 +705,9 @@ class Parser : public CodeCompletionHandler {
SourceLocation ConsumeCodeCompletionToken() {
assert(Tok.is(tok::code_completion));
PrevTokLocation = Tok.getLocation();
PP.Lex(Tok);
// @unreal: BEGIN
PPLexWithUnrealAnnotationTokens();
// @unreal: END
return PrevTokLocation;
}

Expand Down Expand Up @@ -733,18 +755,6 @@ class Parser : public CodeCompletionHandler {
/// #pragma GCC visibility...
void HandlePragmaVisibility();

// @unreal: BEGIN
/// Consume any and all Unreal Engine tokens into the stack.
void ConsumePragmaUnreal();

/// Checks that there aren't any Unreal tokens on the stack.
void CheckNoPragmaUnreal();

/// Handle Unreal Engine semantics.
void HandlePragmaUnreal(tok::TokenKind Kind,
const UnrealSpecifier &UnrealData);
// @unreal: END

/// Handle the annotation token produced for
/// #pragma pack...
void HandlePragmaPack();
Expand Down
21 changes: 21 additions & 0 deletions clang/include/clang/Sema/Sema.Unreal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Unreal stacks.
struct UnrealSpecifierSema {
tok::TokenKind Kind;
clang::UnrealSpecifier SpecData;
clang::SourceLocation Loc;
UnrealSpecifierSema(tok::TokenKind InKind,
const clang::UnrealSpecifier &InSpecData,
const clang::SourceLocation &InLoc)
: Kind(InKind), SpecData(InSpecData), Loc(InLoc){};
};
std::vector<UnrealSpecifierSema> UnrealStack;
std::map<std::string, CXXRecordDecl *>
ExpectedIInterfaceToUInterfaceAttachments;

void ActOnUnrealData(SourceLocation TokenLoc, tok::TokenKind Kind,
const UnrealSpecifier &UnrealData);

/// Called to add specifiers from the Unreal stack.
void AddUnrealSpecifiersForDecl(Decl *RD);

void ProcessUnrealInterfaceMappings(TagDecl* New);
24 changes: 1 addition & 23 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -704,19 +704,7 @@ class Sema final {
PragmaStack<bool> StrictGuardStackCheckStack;

// @unreal: BEGIN
// Unreal stacks.
struct UnrealSpecifierSema {
tok::TokenKind Kind;
clang::UnrealSpecifier SpecData;
clang::SourceLocation Loc;
UnrealSpecifierSema(tok::TokenKind InKind,
const clang::UnrealSpecifier &InSpecData,
const clang::SourceLocation &InLoc)
: Kind(InKind), SpecData(InSpecData), Loc(InLoc){};
};
std::vector<UnrealSpecifierSema> UnrealStack;
std::map<std::string, CXXRecordDecl *>
ExpectedIInterfaceToUInterfaceAttachments;
#include "Sema.Unreal.h"
// @unreal: END

// This stack tracks the current state of Sema.CurFPFeatures.
Expand Down Expand Up @@ -10923,11 +10911,6 @@ class Sema final {
void ActOnPragmaOptionsAlign(PragmaOptionsAlignKind Kind,
SourceLocation PragmaLoc);

// @unreal: BEGIN
void ActOnUnrealData(SourceLocation TokenLoc, tok::TokenKind Kind,
const UnrealSpecifier &UnrealData);
// @unreal: END

/// ActOnPragmaPack - Called on well formed \#pragma pack(...).
void ActOnPragmaPack(SourceLocation PragmaLoc, PragmaMsStackAction Action,
StringRef SlotLabel, Expr *Alignment);
Expand Down Expand Up @@ -11090,11 +11073,6 @@ class Sema final {
/// Called to set exception behavior for floating point operations.
void setExceptionMode(SourceLocation Loc, LangOptions::FPExceptionModeKind);

// @unreal: BEGIN
/// Called to add specifiers from the Unreal stack.
void AddUnrealSpecifiersForDecl(Decl *RD);
// @unreal: END

/// AddAlignmentAttributesForRecord - Adds any needed alignment attributes to
/// a the record decl, to handle '\#pragma pack' and '\#pragma options align'.
void AddAlignmentAttributesForRecord(RecordDecl *RD);
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
Loading

0 comments on commit 87ab46b

Please sign in to comment.