Files
TheRock/compiler/amd-llvm/clang-tools-extra/unittests/clang-tidy/LexerUtilsTest.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

468 lines
17 KiB
C++

//===--- LexerUtilsTest.cpp - clang-tidy ---------------------------------===//
//
// 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 "../clang-tidy/utils/LexerUtils.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Serialization/PCHContainerOperations.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/Error.h"
#include "llvm/Testing/Annotations/Annotations.h"
#include "gtest/gtest.h"
namespace clang::tidy::test {
using clang::tooling::FileContentMappings;
static std::unique_ptr<ASTUnit>
buildAST(StringRef Code, const FileContentMappings &Mappings = {}) {
std::vector<std::string> Args = {"-std=c++20"};
return clang::tooling::buildASTFromCodeWithArgs(
Code, Args, "input.cc", "clang-tool",
std::make_shared<PCHContainerOperations>(),
clang::tooling::getClangStripDependencyFileAdjuster(), Mappings);
}
static CharSourceRange rangeFromAnnotations(const llvm::Annotations &A,
const SourceManager &SM, FileID FID,
llvm::StringRef Name = "") {
const auto R = A.range(Name);
const SourceLocation Begin =
SM.getLocForStartOfFile(FID).getLocWithOffset(R.Begin);
const SourceLocation End =
SM.getLocForStartOfFile(FID).getLocWithOffset(R.End);
return CharSourceRange::getCharRange(Begin, End);
}
static bool isRawIdentifierNamed(const Token &Tok, StringRef Name) {
return Tok.is(tok::raw_identifier) && Tok.getRawIdentifier() == Name;
}
namespace {
TEST(LexerUtilsTest, GetCommentsInRangeAdjacentComments) {
llvm::Annotations Code(R"cpp(
void f() {
$range[[/*first*/ /*second*/]]
int x = 0;
}
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange Range =
rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range");
const std::vector<utils::lexer::CommentToken> Comments =
utils::lexer::getCommentsInRange(Range, SM, LangOpts);
ASSERT_EQ(2u, Comments.size());
EXPECT_EQ("/*first*/", Comments[0].Text);
EXPECT_EQ("/*second*/", Comments[1].Text);
const StringRef CodeText = Code.code();
const size_t FirstOffset = CodeText.find("/*first*/");
ASSERT_NE(StringRef::npos, FirstOffset);
const size_t SecondOffset = CodeText.find("/*second*/");
ASSERT_NE(StringRef::npos, SecondOffset);
EXPECT_EQ(FirstOffset, SM.getFileOffset(Comments[0].Loc));
EXPECT_EQ(SecondOffset, SM.getFileOffset(Comments[1].Loc));
}
TEST(LexerUtilsTest, GetCommentsInRangeKeepsCommentsAcrossTokens) {
llvm::Annotations Code(R"cpp(
void f() {
int x = ($range[[/*first*/ 0, /*second*/]] 1);
}
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange Range =
rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range");
const std::vector<utils::lexer::CommentToken> Comments =
utils::lexer::getCommentsInRange(Range, SM, LangOpts);
ASSERT_EQ(2u, Comments.size());
EXPECT_EQ("/*first*/", Comments[0].Text);
EXPECT_EQ("/*second*/", Comments[1].Text);
const StringRef CodeText = Code.code();
const size_t FirstOffset = CodeText.find("/*first*/");
ASSERT_NE(StringRef::npos, FirstOffset);
const size_t SecondOffset = CodeText.find("/*second*/");
ASSERT_NE(StringRef::npos, SecondOffset);
EXPECT_EQ(FirstOffset, SM.getFileOffset(Comments[0].Loc));
EXPECT_EQ(SecondOffset, SM.getFileOffset(Comments[1].Loc));
}
TEST(LexerUtilsTest, GetCommentsInRangeLineComments) {
llvm::Annotations Code(R"cpp(
void f() {
$range[[// first
// second
]]
int x = 0;
}
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange Range =
rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range");
const std::vector<utils::lexer::CommentToken> Comments =
utils::lexer::getCommentsInRange(Range, SM, LangOpts);
ASSERT_EQ(2u, Comments.size());
EXPECT_EQ("// first", Comments[0].Text);
EXPECT_EQ("// second", Comments[1].Text);
const StringRef CodeText = Code.code();
const size_t FirstOffset = CodeText.find("// first");
ASSERT_NE(StringRef::npos, FirstOffset);
const size_t SecondOffset = CodeText.find("// second");
ASSERT_NE(StringRef::npos, SecondOffset);
EXPECT_EQ(FirstOffset, SM.getFileOffset(Comments[0].Loc));
EXPECT_EQ(SecondOffset, SM.getFileOffset(Comments[1].Loc));
}
TEST(LexerUtilsTest, GetCommentsInRangeNoComments) {
llvm::Annotations Code(R"cpp(
void f() {
int x = $range[[0 + 1]];
}
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange Range =
rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range");
const std::vector<utils::lexer::CommentToken> Comments =
utils::lexer::getCommentsInRange(Range, SM, LangOpts);
EXPECT_TRUE(Comments.empty());
}
TEST(LexerUtilsTest, GetCommentsInRangeInvalidRange) {
std::unique_ptr<ASTUnit> AST = buildAST("int value = 0;");
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const std::vector<utils::lexer::CommentToken> Comments =
utils::lexer::getCommentsInRange(CharSourceRange(), SM, LangOpts);
EXPECT_TRUE(Comments.empty());
}
TEST(LexerUtilsTest, FindTokenTextInRangeFindsMatch) {
llvm::Annotations Code(R"cpp(
struct S {
$range[[explicit ]] S(int);
};
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange SearchRange =
rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range");
const CharSourceRange MatchedRange = utils::lexer::findTokenTextInRange(
SearchRange, SM, LangOpts,
[](const Token &Tok) { return isRawIdentifierNamed(Tok, "explicit"); });
ASSERT_TRUE(MatchedRange.isValid());
const StringRef CodeText = Code.code();
const size_t ExplicitOffset = CodeText.find("explicit");
ASSERT_NE(StringRef::npos, ExplicitOffset);
const size_t ConstructorOffset = CodeText.find("S(int)");
ASSERT_NE(StringRef::npos, ConstructorOffset);
EXPECT_EQ(ExplicitOffset, SM.getFileOffset(MatchedRange.getBegin()));
EXPECT_EQ(ConstructorOffset, SM.getFileOffset(MatchedRange.getEnd()));
}
TEST(LexerUtilsTest, FindTokenTextInRangeReturnsInvalidWhenNotFound) {
llvm::Annotations Code(R"cpp(
struct S {
$range[[int x = 0;]]
S(int);
};
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange SearchRange =
rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range");
const CharSourceRange MatchedRange = utils::lexer::findTokenTextInRange(
SearchRange, SM, LangOpts,
[](const Token &Tok) { return isRawIdentifierNamed(Tok, "explicit"); });
EXPECT_TRUE(MatchedRange.isInvalid());
}
TEST(LexerUtilsTest, FindTokenTextInRangeDoesNotMatchTokenAtEndBoundary) {
llvm::Annotations Code(R"cpp(
struct S {
$range[[int x = 0; ]]explicit S(int);
};
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange SearchRange =
rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range");
const CharSourceRange MatchedRange = utils::lexer::findTokenTextInRange(
SearchRange, SM, LangOpts,
[](const Token &Tok) { return isRawIdentifierNamed(Tok, "explicit"); });
EXPECT_TRUE(MatchedRange.isInvalid());
}
TEST(LexerUtilsTest,
FindTokenTextInRangeReturnsInvalidWhenPredicateNeverMatches) {
llvm::Annotations Code(R"cpp(
struct S {
$range[[explicit ]] S(int);
};
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange SearchRange =
rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range");
const CharSourceRange MatchedRange = utils::lexer::findTokenTextInRange(
SearchRange, SM, LangOpts, [](const Token &) { return false; });
EXPECT_TRUE(MatchedRange.isInvalid());
}
TEST(LexerUtilsTest, FindTokenTextInRangeReturnsInvalidForInvalidRange) {
std::unique_ptr<ASTUnit> AST = buildAST("struct S { explicit S(int); };");
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange MatchedRange = utils::lexer::findTokenTextInRange(
CharSourceRange(), SM, LangOpts, [](const Token &) { return true; });
EXPECT_TRUE(MatchedRange.isInvalid());
}
TEST(LexerUtilsTest, FindTokenTextInRangeReturnsInvalidForReversedOffsets) {
llvm::Annotations Code(R"cpp(
struct S {
$a^explicit S(int);$b^
};
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const SourceLocation MainFileStart =
SM.getLocForStartOfFile(SM.getMainFileID());
const SourceLocation Begin = MainFileStart.getLocWithOffset(Code.point("b"));
const SourceLocation End = MainFileStart.getLocWithOffset(Code.point("a"));
ASSERT_TRUE(SM.isBeforeInTranslationUnit(End, Begin));
const CharSourceRange ReversedRange =
CharSourceRange::getCharRange(Begin, End);
const CharSourceRange MatchedRange = utils::lexer::findTokenTextInRange(
ReversedRange, SM, LangOpts,
[](const Token &Tok) { return isRawIdentifierNamed(Tok, "explicit"); });
EXPECT_TRUE(MatchedRange.isInvalid());
}
TEST(LexerUtilsTest, FindTokenTextInRangeReturnsInvalidWhenFileRangeIsInvalid) {
llvm::Annotations Code(R"cpp(
#include "header.h"
int $begin^main_var = 0;
)cpp");
const FileContentMappings Mappings = {
{"header.h", "int header_var = 0;\n"},
};
std::unique_ptr<ASTUnit> AST = buildAST(Code.code(), Mappings);
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const SourceLocation MainFileStart =
SM.getLocForStartOfFile(SM.getMainFileID());
const SourceLocation Begin =
MainFileStart.getLocWithOffset(Code.point("begin"));
ASSERT_TRUE(Begin.isFileID());
auto HeaderFile = AST->getFileManager().getOptionalFileRef("header.h");
ASSERT_TRUE(HeaderFile.has_value());
const FileID HeaderFID = SM.translateFile(*HeaderFile);
ASSERT_TRUE(HeaderFID.isValid());
const SourceLocation HeaderBegin = SM.getLocForStartOfFile(HeaderFID);
ASSERT_TRUE(HeaderBegin.isFileID());
const CharSourceRange SearchRange =
CharSourceRange::getCharRange(Begin, HeaderBegin);
const CharSourceRange FileRange =
Lexer::makeFileCharRange(SearchRange, SM, LangOpts);
EXPECT_TRUE(FileRange.isInvalid());
const CharSourceRange MatchedRange = utils::lexer::findTokenTextInRange(
SearchRange, SM, LangOpts, [](const Token &) { return true; });
EXPECT_TRUE(MatchedRange.isInvalid());
}
TEST(LexerUtilsTest, FindTokenTextInRangeReturnsInvalidForMacroRange) {
std::unique_ptr<ASTUnit> AST = buildAST(R"cpp(
#define EXPLICIT explicit
struct S {
EXPLICIT S(int);
};
)cpp");
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CXXConstructorDecl *Ctor = [&Context] {
for (const Decl *D : Context.getTranslationUnitDecl()->decls()) {
const auto *RD = dyn_cast<CXXRecordDecl>(D);
if (!RD)
continue;
for (const CXXConstructorDecl *Ctor : RD->ctors())
if (!Ctor->isImplicit())
return Ctor;
}
return static_cast<const CXXConstructorDecl *>(nullptr);
}();
ASSERT_NE(nullptr, Ctor);
ASSERT_TRUE(Ctor->getOuterLocStart().isMacroID());
const CharSourceRange SearchRange = CharSourceRange::getTokenRange(
Ctor->getOuterLocStart(), Ctor->getEndLoc());
const CharSourceRange MatchedRange = utils::lexer::findTokenTextInRange(
SearchRange, SM, LangOpts,
[](const Token &Tok) { return isRawIdentifierNamed(Tok, "explicit"); });
EXPECT_TRUE(MatchedRange.isInvalid());
}
TEST(LexerUtilsTest, GetTrailingCommentsInRangeAdjacentComments) {
llvm::Annotations Code(R"cpp(
void f() {
$range[[/*first*/ /*second*/]]
int x = 0;
}
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange Range =
rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range");
const std::vector<utils::lexer::CommentToken> Comments =
utils::lexer::getTrailingCommentsInRange(Range, SM, LangOpts);
ASSERT_EQ(2u, Comments.size());
EXPECT_EQ("/*first*/", Comments[0].Text);
EXPECT_EQ("/*second*/", Comments[1].Text);
const StringRef CodeText = Code.code();
const size_t FirstOffset = CodeText.find("/*first*/");
ASSERT_NE(StringRef::npos, FirstOffset);
const size_t SecondOffset = CodeText.find("/*second*/");
ASSERT_NE(StringRef::npos, SecondOffset);
EXPECT_EQ(FirstOffset, SM.getFileOffset(Comments[0].Loc));
EXPECT_EQ(SecondOffset, SM.getFileOffset(Comments[1].Loc));
}
TEST(LexerUtilsTest, GetTrailingCommentsInRangeClearsOnToken) {
llvm::Annotations Code(R"cpp(
void f() {
int x = ($range[[/*first*/ 0, /*second*/]] 1);
}
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange Range =
rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range");
const std::vector<utils::lexer::CommentToken> Comments =
utils::lexer::getTrailingCommentsInRange(Range, SM, LangOpts);
ASSERT_EQ(1u, Comments.size());
EXPECT_EQ("/*second*/", Comments.front().Text);
const StringRef CodeText = Code.code();
const size_t SecondOffset = CodeText.find("/*second*/");
ASSERT_NE(StringRef::npos, SecondOffset);
EXPECT_EQ(SecondOffset, SM.getFileOffset(Comments.front().Loc));
}
TEST(LexerUtilsTest, GetTrailingCommentsInRangeLineComments) {
llvm::Annotations Code(R"cpp(
void f() {
$range[[// first
// second
]]
int x = 0;
}
)cpp");
std::unique_ptr<ASTUnit> AST = buildAST(Code.code());
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const CharSourceRange Range =
rangeFromAnnotations(Code, SM, SM.getMainFileID(), "range");
const std::vector<utils::lexer::CommentToken> Comments =
utils::lexer::getTrailingCommentsInRange(Range, SM, LangOpts);
ASSERT_EQ(2u, Comments.size());
EXPECT_EQ("// first", Comments[0].Text);
EXPECT_EQ("// second", Comments[1].Text);
const StringRef CodeText = Code.code();
const size_t FirstOffset = CodeText.find("// first");
ASSERT_NE(StringRef::npos, FirstOffset);
const size_t SecondOffset = CodeText.find("// second");
ASSERT_NE(StringRef::npos, SecondOffset);
EXPECT_EQ(FirstOffset, SM.getFileOffset(Comments[0].Loc));
EXPECT_EQ(SecondOffset, SM.getFileOffset(Comments[1].Loc));
}
TEST(LexerUtilsTest, GetTrailingCommentsInRangeInvalidRange) {
std::unique_ptr<ASTUnit> AST = buildAST("int value = 0;");
ASSERT_TRUE(AST);
const ASTContext &Context = AST->getASTContext();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
const std::vector<utils::lexer::CommentToken> Comments =
utils::lexer::getTrailingCommentsInRange(CharSourceRange(), SM, LangOpts);
EXPECT_TRUE(Comments.empty());
}
} // namespace
} // namespace clang::tidy::test