blob: 91b2d3ef870647bc03b2583f2ebe1472ca65f0b5 [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 "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