blob: 991278647cb32d05e9497bfdac21ffbb3a31779c [file] [log] [blame]
//===-- 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 "Annotations.h"
#include "CollectMacros.h"
#include "Matchers.h"
#include "SourceCode.h"
#include "TestTU.h"
#include "index/SymbolID.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/Support/ScopedPrinter.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace clang {
namespace clangd {
namespace {
using testing::UnorderedElementsAreArray;
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".
const char *Tests[] = {
R"cpp(// Macros: Cursor on definition.
#define $1[[FOO]](x,y) (x + y)
int main() { int x = $1[[FOO]]($1[[FOO]](3, 4), $1[[FOO]](5, 6)); }
)cpp",
R"cpp(
#define $1[[M]](X) X;
#define $2[[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[[UNDEFINED]]
#endif
)cpp",
R"cpp(
#ifndef $Unknown[[abc]]
#define $1[[abc]]
#ifdef $1[[abc]]
#endif
#endif
)cpp",
R"cpp(
// Macros from token concatenations not included.
#define $1[[CONCAT]](X) X##A()
#define $2[[PREPEND]](X) MACRO##X()
#define $3[[MACROA]]() 123
int B = $1[[CONCAT]](MACRO);
int D = $2[[PREPEND]](A);
)cpp",
R"cpp(
// FIXME: Macro names in a definition are not detected.
#define $1[[MACRO_ARGS2]](X, Y) X Y
#define $2[[FOO]] BAR
#define $3[[BAR]] 1
int A = $2[[FOO]];
)cpp"};
for (const char *Test : Tests) {
Annotations T(Test);
auto AST = TestTU::withCode(T.code()).build();
auto ActualMacroRefs = AST.getMacros();
auto &SM = AST.getSourceManager();
auto &PP = AST.getPreprocessor();
// Known macros.
for (int I = 1;; I++) {
const auto ExpectedRefs = T.ranges(llvm::to_string(I));
if (ExpectedRefs.empty())
break;
auto Loc = sourceLocationInMainFile(SM, ExpectedRefs.begin()->start);
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);
std::vector<Range> Ranges;
for (const auto &Ref : ActualMacroRefs.MacroRefs[SID])
Ranges.push_back(Ref.Rng);
EXPECT_THAT(ExpectedRefs, UnorderedElementsAreArray(Ranges))
<< "Annotation=" << I << ", MacroName=" << Macro->Name
<< ", Test = " << Test;
}
// Unknown macros.
std::vector<Range> Ranges;
for (const auto &Ref : AST.getMacros().UnknownMacros)
Ranges.push_back(Ref.Rng);
EXPECT_THAT(Ranges, UnorderedElementsAreArray(T.ranges("Unknown")))
<< "Unknown macros doesn't match in " << Test;
}
}
} // namespace
} // namespace clangd
} // namespace clang