13 #include "clang/Basic/LangOptions.h" 14 #include "clang/Basic/SourceLocation.h" 15 #include "clang/Format/Format.h" 16 #include "llvm/Support/Error.h" 17 #include "llvm/Support/raw_os_ostream.h" 18 #include "llvm/Testing/Support/Annotations.h" 19 #include "llvm/Testing/Support/Error.h" 20 #include "gmock/gmock.h" 21 #include "gtest/gtest.h" 31 MATCHER_P2(
Pos,
Line, Col,
"") {
32 return arg.line == int(
Line) && arg.character == int(Col);
38 Position position(
int Line,
int Character) {
41 Pos.character = Character;
45 Range range(
const std::pair<int, int> &P1,
const std::pair<int, int> &P2) {
47 Range.start = position(P1.first, P1.second);
48 Range.end = position(P2.first, P2.second);
81 const char File[] = R
"(0:0 = 0 91 TEST(SourceCodeTests, PositionToOffset) {
93 EXPECT_THAT_EXPECTED(
positionToOffset(File, position(-1, 2)), llvm::Failed());
148 EXPECT_THAT_EXPECTED(
positionToOffset(File, position(3, 0)), llvm::Failed());
149 EXPECT_THAT_EXPECTED(
positionToOffset(File, position(3, 1)), llvm::Failed());
154 EXPECT_THAT_EXPECTED(
positionToOffset(File, position(-1, 2)), llvm::Failed());
205 EXPECT_THAT_EXPECTED(
positionToOffset(File, position(3, 0)), llvm::Failed());
206 EXPECT_THAT_EXPECTED(
positionToOffset(File, position(3, 1)), llvm::Failed());
210 EXPECT_THAT_EXPECTED(
positionToOffset(File, position(-1, 2)), llvm::Failed());
211 EXPECT_THAT_EXPECTED(
positionToOffset(File, position(3, 0)), llvm::Failed());
212 for (
Line L : FileLines) {
215 for (
unsigned I = 0; I <= L.Length; ++I)
218 EXPECT_THAT_EXPECTED(
positionToOffset(File, position(L.Number, L.Length+1)),
220 EXPECT_THAT_EXPECTED(
226 TEST(SourceCodeTests, OffsetToPosition) {
268 for (
Line L : FileLines) {
269 for (
unsigned I = 0; I <= L.Length; ++I)
275 TEST(SourceCodeTests, IsRangeConsecutive) {
283 TEST(SourceCodeTests, SourceLocationInMainFile) {
284 Annotations Source(R
"cpp( 287 ^baz ^() {} {} {} {} { }^ 290 SourceManagerForFile Owner("foo.cpp", Source.code());
291 SourceManager &SM = Owner.get();
293 SourceLocation StartOfFile = SM.getLocForStartOfFile(SM.getMainFileID());
297 EXPECT_THAT_EXPECTED(
299 HasValue(StartOfFile.getLocWithOffset(Source.code().size())));
308 for (
auto P : Source.points()) {
311 HasValue(StartOfFile.getLocWithOffset(Offset)));
315 TEST(SourceCodeTests, GetBeginningOfIdentifier) {
317 struct Bar { int func(); }; 318 #define MACRO(X) void f() { X; } 322 for (
const std::string &
Text : std::vector<std::string>{
330 "void f(int abc) { abc ^ ++; }",
331 "void f(int abc) { ^abc^++; }",
332 "void f(int abc) { ++^abc^; }",
333 "void f(int abc) { ++^abc; }",
334 "void f(int abc) { ^+^+abc; }",
335 "void f(int abc) { ^abc^ ++; }",
336 "void f(int abc) { abc ^++^; }",
337 "void f(int abc) { ^++^ abc; }",
338 "void f(int abc) { ++ ^abc^; }",
339 "void f(int abc) { ^++^/**/abc; }",
340 "void f(int abc) { ++/**/^abc; }",
341 "void f(int abc) { ^abc^/**/++; }",
342 "void f(int abc) { abc/**/^++; }",
347 "MACRO(bar->^func())",
348 "MACRO(bar->^fun^c())",
349 "MACRO(bar->^func^())",
350 "MACRO(^bar->func())",
351 "MACRO(^bar^->func())",
352 "^MACRO(bar->func())",
353 "^MAC^RO(bar->func())",
354 "^MACRO^(bar->func())",
356 std::string WithPreamble = Preamble +
Text;
357 Annotations TestCase(WithPreamble);
359 const auto &SourceMgr =
AST.getSourceManager();
361 TestCase.points().back(), SourceMgr,
AST.getLangOpts());
364 SourceMgr.getFileOffset(SourceMgr.getSpellingLoc(Actual)));
365 EXPECT_EQ(TestCase.points().front(), ActualPos) << Text;
369 TEST(SourceCodeTests, CollectIdentifiers) {
370 auto Style = format::getLLVMStyle();
373 void foo() { int xyz; int abc = xyz; return foo(); } 376 EXPECT_EQ(IDs.size(), 7u); 377 EXPECT_EQ(IDs["include"], 1u);
378 EXPECT_EQ(IDs[
"void"], 1u);
379 EXPECT_EQ(IDs[
"int"], 2u);
380 EXPECT_EQ(IDs[
"xyz"], 2u);
381 EXPECT_EQ(IDs[
"abc"], 1u);
382 EXPECT_EQ(IDs[
"return"], 1u);
383 EXPECT_EQ(IDs[
"foo"], 2u);
386 TEST(SourceCodeTests, CollectWords) {
390 std::string getSomeText() { return "magic word"; } 392 std::set<std::string> ActualWords(Words.keys().begin(), Words.keys().end()); 393 std::set<std::string> ExpectedWords = {"define",
"fizz",
"buzz",
"this",
394 "comment",
"string",
"some",
"text",
395 "return",
"magic",
"word"};
396 EXPECT_EQ(ActualWords, ExpectedWords);
399 TEST(SourceCodeTests, VisibleNamespaces) {
400 std::vector<std::pair<const char *, std::vector<std::string>>> Cases = {
403 // Using directive resolved against enclosing namespaces. 408 {"ns",
"",
"bar",
"foo",
"ns::bar"},
412 // Don't include namespaces we've closed, ignore namespace aliases. 413 using namespace clang; 417 namespace ll = ::llvm; 425 // Using directives visible even if a namespace is reopened. 426 // Ignore anonymous namespaces. 427 namespace foo{ using namespace bar; } 428 namespace foo{ namespace { 430 {"foo",
"",
"bar",
"foo::bar"},
443 // Namespaces with multiple chunks. 445 using namespace c::d; 466 namespace bar{})cpp", 470 for (
const auto& Case : Cases) {
471 EXPECT_EQ(Case.second,
477 TEST(SourceCodeTests, GetMacros) {
478 Annotations
Code(R
"cpp( 483 auto AST = TU.build();
488 EXPECT_THAT(*Result, MacroName(
"MACRO"));
491 TEST(SourceCodeTests, IsInsideMainFile){
493 TU.HeaderCode = R
"cpp( 494 #define DEFINE_CLASS(X) class X {}; 495 #define DEFINE_YY DEFINE_CLASS(YY) 498 DEFINE_CLASS(Header2) 507 TU.ExtraArgs.push_back("-DHeader=Header3");
508 TU.ExtraArgs.push_back(
"-DMain=Main3");
509 auto AST = TU.build();
510 const auto& SM =
AST.getSourceManager();
511 auto DeclLoc = [&
AST](llvm::StringRef
Name) {
514 for (
const auto *HeaderDecl : {
"Header1",
"Header2",
"Header3"})
517 for (
const auto *MainDecl : {
"Main1",
"Main2",
"Main3",
"YY"})
522 TEST(SourceCodeTests, HalfOpenFileRange) {
525 Annotations Test(R
"cpp( 526 #define FOO(X, Y) int Y = ++X 530 #define BUZZ BAZZ(ADD) 532 #define ADD(a) int f = a + 1; 537 $a[[P<P<P<P<P<int>>>>> a]]; 540 $d[[FOO(BAR(BAR(b)), d)]]; 541 // FIXME: We might want to select everything inside the outer ECHO. 542 ECHO(ECHO($e[[int) ECHO(e]])); 549 llvm::errs() << Test.code(); 550 const SourceManager &SM = AST.getSourceManager();
551 const LangOptions &LangOpts = AST.getLangOpts();
553 auto SourceRangeToRange = [&SM](SourceRange SrcRange) {
557 auto CheckRange = [&](llvm::StringRef
Name) {
560 SCOPED_TRACE(
"Checking range: " +
Name);
562 Range HalfOpenRange = SourceRangeToRange(*FileRange);
563 EXPECT_EQ(HalfOpenRange, Test.ranges(
Name)[0]);
574 TEST(SourceCodeTests, HalfOpenFileRangePathologicalPreprocessor) {
575 const char *Case = R
"cpp( 576 #define MACRO while(1) 578 [[#include "Expand.inc" 582 Annotations Test(Case); 585 auto AST = TU.build();
587 const auto &Func = cast<FunctionDecl>(
findDecl(
AST,
"test"));
588 const auto &Body = cast<CompoundStmt>(Func.getBody());
589 const auto &Loop = cast<WhileStmt>(*Body->child_begin());
591 AST.getSourceManager(),
AST.getLangOpts(), Loop->getSourceRange());
592 ASSERT_TRUE(Range) <<
"Failed to get file range";
593 EXPECT_EQ(
AST.getSourceManager().getFileOffset(Range->getBegin()),
594 Test.llvm::Annotations::range().Begin);
595 EXPECT_EQ(
AST.getSourceManager().getFileOffset(Range->getEnd()),
596 Test.llvm::Annotations::range().End);
599 TEST(SourceCodeTests, IncludeHashLoc) {
600 const char *Case = R
"cpp( 601 $foo^#include "foo.inc" 602 #define HEADER "bar.inc" 603 $bar^# include HEADER 605 Annotations Test(Case); 608 TU.AdditionalFiles[
"bar.inc"] =
"int bar;\n";
609 auto AST = TU.build();
610 const auto& SM =
AST.getSourceManager();
612 FileID Foo = SM.getFileID(
findDecl(
AST,
"foo").getLocation());
614 Test.llvm::Annotations::point(
"foo"));
615 FileID Bar = SM.getFileID(
findDecl(
AST,
"bar").getLocation());
617 Test.llvm::Annotations::point(
"bar"));
620 TEST(SourceCodeTests, GetEligiblePoints) {
623 const char *FullyQualifiedName;
626 {R
"cpp(// FIXME: We should also mark positions before and after 627 //declarations/definitions as eligible. 629 namespace a { namespace ns2 {} } 639 "ns1::ns2::symbol",
"ns1::ns2::"},
642 namespace a { namespace ns2 {} } 646 "ns1::ns2::symbol",
"ns1::"},
649 namespace a { namespace ns2 {} } 653 "ns1::ns2::symbol",
""},
660 namespace ns1 {namespace ns2 {^^}})cpp", 661 "ns1::ns2::symbol",
"ns1::ns2::"},
668 namespace ns1 {^namespace ns {}^})cpp", 669 "ns1::ns2::symbol",
"ns1::"},
671 for (
auto Case : Cases) {
672 Annotations Test(Case.Code);
675 format::getLLVMStyle());
676 EXPECT_THAT(Res.EligiblePoints, testing::ElementsAreArray(Test.points()))
678 EXPECT_EQ(Res.EnclosingNamespace, Case.EnclosingNamespace) << Test.code();
682 TEST(SourceCodeTests, IdentifierRanges) {
683 Annotations
Code(R
"cpp( 687 void f([[Foo]]* foo1) { 690 // cross-line identifier is not supported. 696 LangOptions LangOpts; 697 LangOpts.CPlusPlus = true;
698 EXPECT_EQ(Code.ranges(),
717 LangOptions LangOpts;
718 LangOpts.IsHeaderFile =
true;
722 LangOpts.IsHeaderFile =
false;
SourceLocation Loc
'#' location in the include directive
llvm::StringSet collectWords(llvm::StringRef Content)
Collects words from the source code.
OptionalMatcher< InnerMatcher > HasValue(const InnerMatcher &inner_matcher)
const FunctionDecl * Decl
size_t lspLength(llvm::StringRef Code)
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
SourceLocation getBeginningOfIdentifier(const Position &Pos, const SourceManager &SM, const LangOptions &LangOpts)
Get the beginning SourceLocation at a specified Pos in the main file.
Documents should not be synced at all.
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
llvm::Expected< SourceLocation > sourceLocationInMainFile(const SourceManager &SM, Position P)
Return the file location, corresponding to P.
std::vector< std::string > visibleNamespaces(llvm::StringRef Code, const format::FormatStyle &Style)
Heuristically determine namespaces visible at a point, without parsing Code.
bool isRangeConsecutive(const Range &Left, const Range &Right)
std::string EnclosingNamespace
Position offsetToPosition(llvm::StringRef Code, size_t Offset)
Turn an offset in Code into a [line, column] pair.
TEST(BackgroundQueueTest, Priority)
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
EligibleRegion getEligiblePoints(llvm::StringRef Code, llvm::StringRef FullyQualifiedName, const format::FormatStyle &Style)
Returns most eligible region to insert a definition for FullyQualifiedName in the Code...
static constexpr llvm::StringLiteral Name
Key< OffsetEncoding > kCurrentOffsetEncoding
Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc)
Turn a SourceLocation into a [line, column] pair.
static TestTU withCode(llvm::StringRef Code)
llvm::Optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
SourceLocation includeHashLoc(FileID IncludedFile, const SourceManager &SM)
Returns the #include location through which IncludedFIle was loaded.
int line
Line position in a document (zero-based).
const PreambleData * Preamble
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
bool isHeaderFile(llvm::StringRef FileName, llvm::Optional< LangOptions > LangOpts)
Infers whether this is a header from the FileName and LangOpts (if presents).
std::vector< Range > collectIdentifierRanges(llvm::StringRef Identifier, llvm::StringRef Content, const LangOptions &LangOpts)
Collects all ranges of the given identifier in the source code.
llvm::StringMap< unsigned > collectIdentifiers(llvm::StringRef Content, const format::FormatStyle &Style)
Collects identifiers with counts in the source code.
llvm::Optional< DefinedMacro > locateMacroAt(SourceLocation Loc, Preprocessor &PP)
Gets the macro at a specified Loc.
llvm::StringMap< std::string > AdditionalFiles
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)