1818#include " clang/Tooling/Inclusions/StandardLibrary.h"
1919#include " llvm/ADT/ArrayRef.h"
2020#include " llvm/ADT/SmallVector.h"
21+ #include " llvm/Support/ScopedPrinter.h"
2122#include " llvm/Testing/Support/Annotations.h"
2223#include " gmock/gmock.h"
2324#include " gtest/gtest.h"
@@ -27,6 +28,7 @@ namespace clang::include_cleaner {
2728namespace {
2829using testing::Contains;
2930using testing::ElementsAre;
31+ using testing::AllOf;
3032using testing::Pair;
3133using testing::UnorderedElementsAre;
3234
@@ -252,5 +254,119 @@ TEST(FixIncludes, Basic) {
252254)cpp" );
253255}
254256
257+ MATCHER_P3 (expandedAt, FileID, Offset, SM, " " ) {
258+ auto [ExpanedFileID, ExpandedOffset] = SM->getDecomposedExpansionLoc (arg);
259+ return ExpanedFileID == FileID && ExpandedOffset == Offset;
260+ }
261+ MATCHER_P3 (spelledAt, FileID, Offset, SM, " " ) {
262+ auto [SpelledFileID, SpelledOffset] = SM->getDecomposedSpellingLoc (arg);
263+ return SpelledFileID == FileID && SpelledOffset == Offset;
264+ }
265+ TEST (WalkUsed, FilterRefsNotSpelledInMainFile) {
266+ // Each test is expected to have a single expected ref of `target` symbol
267+ // (or have none).
268+ // The location in the reported ref is a macro location. $expand points to
269+ // the macro location, and $spell points to the spelled location.
270+ struct {
271+ llvm::StringRef Header;
272+ llvm::StringRef Main;
273+ } TestCases[] = {
274+ // Tests for decl references.
275+ {
276+ /* Header=*/ " int target();" ,
277+ R"cpp(
278+ #define CALL_FUNC $spell^target()
279+
280+ int b = $expand^CALL_FUNC;
281+ )cpp" ,
282+ },
283+ {/* Header=*/ R"cpp(
284+ int target();
285+ #define CALL_FUNC target()
286+ )cpp" ,
287+ // No ref of `target` being reported, as it is not spelled in main file.
288+ " int a = CALL_FUNC;" },
289+ {
290+ /* Header=*/ R"cpp(
291+ int target();
292+ #define PLUS_ONE(X) X() + 1
293+ )cpp" ,
294+ R"cpp(
295+ int a = $expand^PLUS_ONE($spell^target);
296+ )cpp" ,
297+ },
298+ {
299+ /* Header=*/ R"cpp(
300+ int target();
301+ #define PLUS_ONE(X) X() + 1
302+ )cpp" ,
303+ R"cpp(
304+ int a = $expand^PLUS_ONE($spell^target);
305+ )cpp" ,
306+ },
307+ // Tests for macro references
308+ {/* Header=*/ " #define target 1" ,
309+ R"cpp(
310+ #define USE_target $spell^target
311+ int b = $expand^USE_target;
312+ )cpp" },
313+ {/* Header=*/ R"cpp(
314+ #define target 1
315+ #define USE_target target
316+ )cpp" ,
317+ // No ref of `target` being reported, it is not spelled in main file.
318+ R"cpp(
319+ int a = USE_target;
320+ )cpp" },
321+ };
322+
323+ for (const auto &T : TestCases) {
324+ llvm::Annotations Main (T.Main );
325+ TestInputs Inputs (Main.code ());
326+ Inputs.ExtraFiles [" header.h" ] = guard (T.Header );
327+ RecordedPP Recorded;
328+ Inputs.MakeAction = [&]() {
329+ struct RecordAction : public SyntaxOnlyAction {
330+ RecordedPP &Out;
331+ RecordAction (RecordedPP &Out) : Out(Out) {}
332+ bool BeginSourceFileAction (clang::CompilerInstance &CI) override {
333+ auto &PP = CI.getPreprocessor ();
334+ PP.addPPCallbacks (Out.record (PP));
335+ return true ;
336+ }
337+ };
338+ return std::make_unique<RecordAction>(Recorded);
339+ };
340+ Inputs.ExtraArgs .push_back (" -include" );
341+ Inputs.ExtraArgs .push_back (" header.h" );
342+ TestAST AST (Inputs);
343+ llvm::SmallVector<Decl *> TopLevelDecls;
344+ for (Decl *D : AST.context ().getTranslationUnitDecl ()->decls ())
345+ TopLevelDecls.emplace_back (D);
346+ auto &SM = AST.sourceManager ();
347+
348+ SourceLocation RefLoc;
349+ walkUsed (TopLevelDecls, Recorded.MacroReferences ,
350+ /* PragmaIncludes=*/ nullptr , SM,
351+ [&](const SymbolReference &Ref, llvm::ArrayRef<Header>) {
352+ if (!Ref.RefLocation .isMacroID ())
353+ return ;
354+ if (llvm::to_string (Ref.Target ) == " target" ) {
355+ ASSERT_TRUE (RefLoc.isInvalid ())
356+ << " Expected only one 'target' ref loc per testcase" ;
357+ RefLoc = Ref.RefLocation ;
358+ }
359+ });
360+ FileID MainFID = SM.getMainFileID ();
361+ if (RefLoc.isValid ()) {
362+ EXPECT_THAT (RefLoc, AllOf (expandedAt (MainFID, Main.point (" expand" ), &SM),
363+ spelledAt (MainFID, Main.point (" spell" ), &SM)))
364+ << T.Main ;
365+ } else {
366+ EXPECT_THAT (Main.points (), testing::IsEmpty ());
367+ }
368+ }
369+ }
370+
255371} // namespace
256372} // namespace clang::include_cleaner
0 commit comments