| //===-- 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 |