162a090f9SKadir Cetinkaya //===-- ReplayPreambleTests.cpp -------------------------------------------===//
262a090f9SKadir Cetinkaya //
362a090f9SKadir Cetinkaya // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
462a090f9SKadir Cetinkaya // See https://llvm.org/LICENSE.txt for license information.
562a090f9SKadir Cetinkaya // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
662a090f9SKadir Cetinkaya //
762a090f9SKadir Cetinkaya //===----------------------------------------------------------------------===//
862a090f9SKadir Cetinkaya //
962a090f9SKadir Cetinkaya // These tests cover clangd's logic to replay PP events from preamble to
1062a090f9SKadir Cetinkaya // clang-tidy checks.
1162a090f9SKadir Cetinkaya //
1262a090f9SKadir Cetinkaya //===----------------------------------------------------------------------===//
1362a090f9SKadir Cetinkaya
1462a090f9SKadir Cetinkaya #include "../../clang-tidy/ClangTidyCheck.h"
1562a090f9SKadir Cetinkaya #include "../../clang-tidy/ClangTidyModule.h"
1662a090f9SKadir Cetinkaya #include "../../clang-tidy/ClangTidyModuleRegistry.h"
1762a090f9SKadir Cetinkaya #include "AST.h"
18b99f7e69SSam McCall #include "Config.h"
1962a090f9SKadir Cetinkaya #include "Diagnostics.h"
2062a090f9SKadir Cetinkaya #include "ParsedAST.h"
2162a090f9SKadir Cetinkaya #include "SourceCode.h"
2262a090f9SKadir Cetinkaya #include "TestTU.h"
2362a090f9SKadir Cetinkaya #include "TidyProvider.h"
24b99f7e69SSam McCall #include "support/Context.h"
2562a090f9SKadir Cetinkaya #include "clang/AST/DeclTemplate.h"
2662a090f9SKadir Cetinkaya #include "clang/Basic/FileEntry.h"
2762a090f9SKadir Cetinkaya #include "clang/Basic/LLVM.h"
2862a090f9SKadir Cetinkaya #include "clang/Basic/SourceLocation.h"
2962a090f9SKadir Cetinkaya #include "clang/Basic/SourceManager.h"
3062a090f9SKadir Cetinkaya #include "clang/Basic/TokenKinds.h"
3162a090f9SKadir Cetinkaya #include "clang/Lex/PPCallbacks.h"
3262a090f9SKadir Cetinkaya #include "clang/Lex/Token.h"
3362a090f9SKadir Cetinkaya #include "clang/Tooling/Syntax/Tokens.h"
3462a090f9SKadir Cetinkaya #include "llvm/ADT/StringRef.h"
3562a090f9SKadir Cetinkaya #include "llvm/Support/Registry.h"
3662a090f9SKadir Cetinkaya #include "llvm/Testing/Annotations/Annotations.h"
3762a090f9SKadir Cetinkaya #include "gmock/gmock-matchers.h"
3862a090f9SKadir Cetinkaya #include "gmock/gmock.h"
3962a090f9SKadir Cetinkaya #include "gtest/gtest.h"
4062a090f9SKadir Cetinkaya #include <cstddef>
4162a090f9SKadir Cetinkaya #include <memory>
4262a090f9SKadir Cetinkaya #include <vector>
4362a090f9SKadir Cetinkaya
44*aaa4ff88SDavid Stone namespace clang {
45*aaa4ff88SDavid Stone
46*aaa4ff88SDavid Stone class Module;
47*aaa4ff88SDavid Stone
48*aaa4ff88SDavid Stone namespace clangd {
4962a090f9SKadir Cetinkaya namespace {
5062a090f9SKadir Cetinkaya struct Inclusion {
Inclusionclang::clangd::__anonc0cd58060111::Inclusion5162a090f9SKadir Cetinkaya Inclusion(const SourceManager &SM, SourceLocation HashLoc,
5262a090f9SKadir Cetinkaya const Token &IncludeTok, llvm::StringRef FileName, bool IsAngled,
5362a090f9SKadir Cetinkaya CharSourceRange FilenameRange)
5462a090f9SKadir Cetinkaya : HashOffset(SM.getDecomposedLoc(HashLoc).second), IncTok(IncludeTok),
5562a090f9SKadir Cetinkaya IncDirective(IncludeTok.getIdentifierInfo()->getName()),
5662a090f9SKadir Cetinkaya FileNameOffset(SM.getDecomposedLoc(FilenameRange.getBegin()).second),
5762a090f9SKadir Cetinkaya FileName(FileName), IsAngled(IsAngled) {
5862a090f9SKadir Cetinkaya EXPECT_EQ(
5962a090f9SKadir Cetinkaya toSourceCode(SM, FilenameRange.getAsRange()).drop_back().drop_front(),
6062a090f9SKadir Cetinkaya FileName);
6162a090f9SKadir Cetinkaya }
6262a090f9SKadir Cetinkaya size_t HashOffset;
6362a090f9SKadir Cetinkaya syntax::Token IncTok;
6462a090f9SKadir Cetinkaya llvm::StringRef IncDirective;
6562a090f9SKadir Cetinkaya size_t FileNameOffset;
6662a090f9SKadir Cetinkaya llvm::StringRef FileName;
6762a090f9SKadir Cetinkaya bool IsAngled;
6862a090f9SKadir Cetinkaya };
6962a090f9SKadir Cetinkaya static std::vector<Inclusion> Includes;
7062a090f9SKadir Cetinkaya static std::vector<syntax::Token> SkippedFiles;
7162a090f9SKadir Cetinkaya struct ReplayPreamblePPCallback : public PPCallbacks {
7262a090f9SKadir Cetinkaya const SourceManager &SM;
ReplayPreamblePPCallbackclang::clangd::__anonc0cd58060111::ReplayPreamblePPCallback7362a090f9SKadir Cetinkaya explicit ReplayPreamblePPCallback(const SourceManager &SM) : SM(SM) {}
7462a090f9SKadir Cetinkaya
InclusionDirectiveclang::clangd::__anonc0cd58060111::ReplayPreamblePPCallback7562a090f9SKadir Cetinkaya void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
7662a090f9SKadir Cetinkaya StringRef FileName, bool IsAngled,
7762a090f9SKadir Cetinkaya CharSourceRange FilenameRange, OptionalFileEntryRef,
78da95d926SJan Svoboda StringRef, StringRef, const clang::Module *, bool,
7962a090f9SKadir Cetinkaya SrcMgr::CharacteristicKind) override {
8062a090f9SKadir Cetinkaya Includes.emplace_back(SM, HashLoc, IncludeTok, FileName, IsAngled,
8162a090f9SKadir Cetinkaya FilenameRange);
8262a090f9SKadir Cetinkaya }
8362a090f9SKadir Cetinkaya
FileSkippedclang::clangd::__anonc0cd58060111::ReplayPreamblePPCallback8462a090f9SKadir Cetinkaya void FileSkipped(const FileEntryRef &, const Token &FilenameTok,
8562a090f9SKadir Cetinkaya SrcMgr::CharacteristicKind) override {
8662a090f9SKadir Cetinkaya SkippedFiles.emplace_back(FilenameTok);
8762a090f9SKadir Cetinkaya }
8862a090f9SKadir Cetinkaya };
8962a090f9SKadir Cetinkaya struct ReplayPreambleCheck : public tidy::ClangTidyCheck {
ReplayPreambleCheckclang::clangd::__anonc0cd58060111::ReplayPreambleCheck9062a090f9SKadir Cetinkaya ReplayPreambleCheck(StringRef Name, tidy::ClangTidyContext *Context)
9162a090f9SKadir Cetinkaya : ClangTidyCheck(Name, Context) {}
registerPPCallbacksclang::clangd::__anonc0cd58060111::ReplayPreambleCheck9262a090f9SKadir Cetinkaya void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
9362a090f9SKadir Cetinkaya Preprocessor *ModuleExpanderPP) override {
9462a090f9SKadir Cetinkaya PP->addPPCallbacks(::std::make_unique<ReplayPreamblePPCallback>(SM));
9562a090f9SKadir Cetinkaya }
9662a090f9SKadir Cetinkaya };
9762a090f9SKadir Cetinkaya llvm::StringLiteral CheckName = "replay-preamble-check";
9862a090f9SKadir Cetinkaya struct ReplayPreambleModule : public tidy::ClangTidyModule {
9962a090f9SKadir Cetinkaya void
addCheckFactoriesclang::clangd::__anonc0cd58060111::ReplayPreambleModule10062a090f9SKadir Cetinkaya addCheckFactories(tidy::ClangTidyCheckFactories &CheckFactories) override {
10162a090f9SKadir Cetinkaya CheckFactories.registerCheck<ReplayPreambleCheck>(CheckName);
10262a090f9SKadir Cetinkaya }
10362a090f9SKadir Cetinkaya };
10462a090f9SKadir Cetinkaya static tidy::ClangTidyModuleRegistry::Add<ReplayPreambleModule>
10562a090f9SKadir Cetinkaya X("replay-preamble-module", "");
10662a090f9SKadir Cetinkaya
10762a090f9SKadir Cetinkaya MATCHER_P(rangeIs, R, "") {
10862a090f9SKadir Cetinkaya return arg.beginOffset() == R.Begin && arg.endOffset() == R.End;
10962a090f9SKadir Cetinkaya }
11062a090f9SKadir Cetinkaya
TEST(ReplayPreambleTest,IncludesAndSkippedFiles)11162a090f9SKadir Cetinkaya TEST(ReplayPreambleTest, IncludesAndSkippedFiles) {
11262a090f9SKadir Cetinkaya TestTU TU;
11362a090f9SKadir Cetinkaya // This check records inclusion directives replayed by clangd.
11462a090f9SKadir Cetinkaya TU.ClangTidyProvider = addTidyChecks(CheckName);
11562a090f9SKadir Cetinkaya llvm::Annotations Test(R"cpp(
11662a090f9SKadir Cetinkaya $hash^#$include[[import]] $filebegin^"$filerange[[bar.h]]"
11762a090f9SKadir Cetinkaya $hash^#$include[[include_next]] $filebegin^"$filerange[[baz.h]]"
11862a090f9SKadir Cetinkaya $hash^#$include[[include]] $filebegin^<$filerange[[a.h]]>)cpp");
11962a090f9SKadir Cetinkaya llvm::StringRef Code = Test.code();
12062a090f9SKadir Cetinkaya TU.Code = Code.str();
12162a090f9SKadir Cetinkaya TU.AdditionalFiles["bar.h"] = "";
12262a090f9SKadir Cetinkaya TU.AdditionalFiles["baz.h"] = "";
12362a090f9SKadir Cetinkaya TU.AdditionalFiles["a.h"] = "";
12462a090f9SKadir Cetinkaya // Since we are also testing #import directives, and they don't make much
12562a090f9SKadir Cetinkaya // sense in c++ (also they actually break on windows), just set language to
12662a090f9SKadir Cetinkaya // obj-c.
12762a090f9SKadir Cetinkaya TU.ExtraArgs = {"-isystem.", "-xobjective-c"};
12862a090f9SKadir Cetinkaya
129b99f7e69SSam McCall // Allow the check to run even though not marked as fast.
130b99f7e69SSam McCall Config Cfg;
131b99f7e69SSam McCall Cfg.Diagnostics.ClangTidy.FastCheckFilter = Config::FastCheckPolicy::Loose;
132b99f7e69SSam McCall WithContextValue WithCfg(Config::Key, std::move(Cfg));
133b99f7e69SSam McCall
13462a090f9SKadir Cetinkaya const auto &AST = TU.build();
13562a090f9SKadir Cetinkaya const auto &SM = AST.getSourceManager();
13662a090f9SKadir Cetinkaya
13762a090f9SKadir Cetinkaya auto HashLocs = Test.points("hash");
13862a090f9SKadir Cetinkaya ASSERT_EQ(HashLocs.size(), Includes.size());
13962a090f9SKadir Cetinkaya auto IncludeRanges = Test.ranges("include");
14062a090f9SKadir Cetinkaya ASSERT_EQ(IncludeRanges.size(), Includes.size());
14162a090f9SKadir Cetinkaya auto FileBeginLocs = Test.points("filebegin");
14262a090f9SKadir Cetinkaya ASSERT_EQ(FileBeginLocs.size(), Includes.size());
14362a090f9SKadir Cetinkaya auto FileRanges = Test.ranges("filerange");
14462a090f9SKadir Cetinkaya ASSERT_EQ(FileRanges.size(), Includes.size());
14562a090f9SKadir Cetinkaya
14662a090f9SKadir Cetinkaya ASSERT_EQ(SkippedFiles.size(), Includes.size());
14762a090f9SKadir Cetinkaya for (size_t I = 0; I < Includes.size(); ++I) {
14862a090f9SKadir Cetinkaya const auto &Inc = Includes[I];
14962a090f9SKadir Cetinkaya
15062a090f9SKadir Cetinkaya EXPECT_EQ(Inc.HashOffset, HashLocs[I]);
15162a090f9SKadir Cetinkaya
15262a090f9SKadir Cetinkaya auto IncRange = IncludeRanges[I];
15362a090f9SKadir Cetinkaya EXPECT_THAT(Inc.IncTok.range(SM), rangeIs(IncRange));
15462a090f9SKadir Cetinkaya EXPECT_EQ(Inc.IncTok.kind(), tok::identifier);
15562a090f9SKadir Cetinkaya EXPECT_EQ(Inc.IncDirective,
15662a090f9SKadir Cetinkaya Code.substr(IncRange.Begin, IncRange.End - IncRange.Begin));
15762a090f9SKadir Cetinkaya
15862a090f9SKadir Cetinkaya EXPECT_EQ(Inc.FileNameOffset, FileBeginLocs[I]);
15962a090f9SKadir Cetinkaya EXPECT_EQ(Inc.IsAngled, Code[FileBeginLocs[I]] == '<');
16062a090f9SKadir Cetinkaya
16162a090f9SKadir Cetinkaya auto FileRange = FileRanges[I];
16262a090f9SKadir Cetinkaya EXPECT_EQ(Inc.FileName,
16362a090f9SKadir Cetinkaya Code.substr(FileRange.Begin, FileRange.End - FileRange.Begin));
16462a090f9SKadir Cetinkaya
16562a090f9SKadir Cetinkaya EXPECT_EQ(SM.getDecomposedLoc(SkippedFiles[I].location()).second,
16662a090f9SKadir Cetinkaya Inc.FileNameOffset);
16762a090f9SKadir Cetinkaya // This also contains quotes/angles so increment the range by one from both
16862a090f9SKadir Cetinkaya // sides.
16962a090f9SKadir Cetinkaya EXPECT_EQ(
17062a090f9SKadir Cetinkaya SkippedFiles[I].text(SM),
17162a090f9SKadir Cetinkaya Code.substr(FileRange.Begin - 1, FileRange.End - FileRange.Begin + 2));
17262a090f9SKadir Cetinkaya EXPECT_EQ(SkippedFiles[I].kind(), tok::header_name);
17362a090f9SKadir Cetinkaya }
17462a090f9SKadir Cetinkaya }
17562a090f9SKadir Cetinkaya } // namespace
176*aaa4ff88SDavid Stone } // namespace clangd
177*aaa4ff88SDavid Stone } // namespace clang
178