| //===-- CollectMacrosTests.cpp ----------------------------------*- C++ -*-===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | #include "AST.h" | 
 | #include "Annotations.h" | 
 | #include "CollectMacros.h" | 
 | #include "Matchers.h" | 
 | #include "SourceCode.h" | 
 | #include "TestTU.h" | 
 | #include "clang/Basic/SourceLocation.h" | 
 | #include "llvm/Support/ScopedPrinter.h" | 
 | #include "gmock/gmock.h" | 
 | #include "gtest/gtest.h" | 
 | #include <vector> | 
 |  | 
 | namespace clang { | 
 | namespace clangd { | 
 | namespace { | 
 |  | 
 | using testing::UnorderedElementsAreArray; | 
 |  | 
 | MATCHER_P(rangeIs, R, "") { | 
 |   return arg.StartOffset == R.Begin && arg.EndOffset == R.End; | 
 | } | 
 | MATCHER(isDef, "") { return arg.IsDefinition; } | 
 | MATCHER(inConditionalDirective, "") { return arg.InConditionalDirective; } | 
 |  | 
 | TEST(CollectMainFileMacros, SelectedMacros) { | 
 |   // References of the same symbol must have the ranges with the same | 
 |   // name(integer). If there are N different symbols then they must be named | 
 |   // from 1 to N. Macros for which SymbolID cannot be computed must be named | 
 |   // "Unknown". The payload of the annotation describes the extra bit | 
 |   // information of the MacroOccurrence (e.g. $1(def) => IsDefinition). | 
 |   const char *Tests[] = { | 
 |       R"cpp(// Macros: Cursor on definition. | 
 |         #define $1(def)[[FOO]](x,y) (x + y) | 
 |         int main() { int x = $1[[FOO]]($1[[FOO]](3, 4), $1[[FOO]](5, 6)); } | 
 |       )cpp", | 
 |       R"cpp( | 
 |         #define $1(def)[[M]](X) X; | 
 |         #define $2(def)[[abc]] 123 | 
 |         int s = $1[[M]]($2[[abc]]); | 
 |       )cpp", | 
 |       // FIXME: Locating macro in duplicate definitions doesn't work. Enable | 
 |       // this once LocateMacro is fixed. | 
 |       // R"cpp(// Multiple definitions. | 
 |       //   #define $1[[abc]] 1 | 
 |       //   int func1() { int a = $1[[abc]];} | 
 |       //   #undef $1[[abc]] | 
 |  | 
 |       //   #define $2[[abc]] 2 | 
 |       //   int func2() { int a = $2[[abc]];} | 
 |       //   #undef $2[[abc]] | 
 |       // )cpp", | 
 |       R"cpp( | 
 |         #ifdef $Unknown(condit)[[UNDEFINED]] | 
 |         #elifdef $Unknown(condit)[[UNDEFINED]] | 
 |         #endif | 
 |  | 
 |         #ifdef $Unknown(condit)[[UNDEFINED]] | 
 |         #elifndef $Unknown(condit)[[UNDEFINED]] | 
 |         #endif | 
 |  | 
 |         #ifndef $Unknown(condit)[[UNDEFINED]] | 
 |         #endif | 
 |  | 
 |         #if defined($Unknown(condit)[[UNDEFINED]]) | 
 |         #endif | 
 |       )cpp", | 
 |       R"cpp( | 
 |         #ifndef $Unknown(condit)[[abc]] | 
 |         #define $1(def)[[abc]] | 
 |         #ifdef $1(condit)[[abc]] | 
 |         #endif | 
 |         #endif | 
 |       )cpp", | 
 |       R"cpp( | 
 |         // Macros from token concatenations not included. | 
 |         #define $1(def)[[CONCAT]](X) X##A() | 
 |         #define $2(def)[[PREPEND]](X) MACRO##X() | 
 |         #define $3(def)[[MACROA]]() 123 | 
 |         int B = $1[[CONCAT]](MACRO); | 
 |         int D = $2[[PREPEND]](A); | 
 |       )cpp", | 
 |       R"cpp( | 
 |         #define $1(def)[[MACRO_ARGS2]](X, Y) X Y | 
 |         #define $3(def)[[BAR]] 1 | 
 |         #define $2(def)[[FOO]] $3[[BAR]] | 
 |         int A = $2[[FOO]]; | 
 |       )cpp"}; | 
 |   auto ExpectedResults = [](const llvm::Annotations &T, StringRef Name) { | 
 |     std::vector<Matcher<MacroOccurrence>> ExpectedLocations; | 
 |     for (const auto &[R, Bits] : T.rangesWithPayload(Name)) { | 
 |       if (Bits == "def") | 
 |         ExpectedLocations.push_back(testing::AllOf(rangeIs(R), isDef())); | 
 |       else if (Bits == "condit") | 
 |         ExpectedLocations.push_back( | 
 |             testing::AllOf(rangeIs(R), inConditionalDirective())); | 
 |       else | 
 |         ExpectedLocations.push_back(testing::AllOf(rangeIs(R))); | 
 |     } | 
 |     return ExpectedLocations; | 
 |   }; | 
 |  | 
 |   for (const char *Test : Tests) { | 
 |     llvm::Annotations T(Test); | 
 |     auto Inputs = TestTU::withCode(T.code()); | 
 |     Inputs.ExtraArgs.push_back("-std=c++2b"); | 
 |     auto AST = Inputs.build(); | 
 |     auto ActualMacroRefs = AST.getMacros(); | 
 |     auto &SM = AST.getSourceManager(); | 
 |     auto &PP = AST.getPreprocessor(); | 
 |     for (const auto &[Name, Ranges] : T.all_ranges()) { | 
 |       if (Name == "Unknown") { | 
 |         EXPECT_THAT(ActualMacroRefs.UnknownMacros, | 
 |                     UnorderedElementsAreArray(ExpectedResults(T, "Unknown"))) | 
 |             << "Unknown macros doesn't match in " << Test; | 
 |         continue; | 
 |       } | 
 |  | 
 |       auto Loc = sourceLocationInMainFile( | 
 |           SM, offsetToPosition(T.code(), Ranges.front().Begin)); | 
 |       ASSERT_TRUE(bool(Loc)); | 
 |       const auto *Id = syntax::spelledIdentifierTouching(*Loc, AST.getTokens()); | 
 |       ASSERT_TRUE(Id); | 
 |       auto Macro = locateMacroAt(*Id, PP); | 
 |       assert(Macro); | 
 |       auto SID = getSymbolID(Macro->Name, Macro->Info, SM); | 
 |  | 
 |       EXPECT_THAT(ActualMacroRefs.MacroRefs[SID], | 
 |                   UnorderedElementsAreArray(ExpectedResults(T, Name))) | 
 |           << "Annotation=" << Name << ", MacroName=" << Macro->Name | 
 |           << ", Test = " << Test; | 
 |     } | 
 |   } | 
 | } | 
 | } // namespace | 
 | } // namespace clangd | 
 | } // namespace clang |