[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / clang-tools-extra / include-cleaner / unittests / WalkASTTest.cpp
blob3e4fd42bf9cc5bb5e40ec6ac5da626658856afa2
1 #include "AnalysisInternal.h"
2 #include "clang/AST/ASTContext.h"
3 #include "clang/Basic/FileManager.h"
4 #include "clang/Frontend/TextDiagnostic.h"
5 #include "clang/Testing/TestAST.h"
6 #include "llvm/Support/Error.h"
7 #include "llvm/Testing/Support/Annotations.h"
8 #include "gtest/gtest.h"
10 namespace clang {
11 namespace include_cleaner {
12 namespace {
14 // Specifies a test of which symbols are referenced by a piece of code.
16 // Example:
17 // Target: int ^foo();
18 // Referencing: int x = ^foo();
19 // There must be exactly one referencing location marked.
20 void testWalk(llvm::StringRef TargetCode, llvm::StringRef ReferencingCode) {
21 llvm::Annotations Target(TargetCode);
22 llvm::Annotations Referencing(ReferencingCode);
24 TestInputs Inputs(Referencing.code());
25 Inputs.ExtraFiles["target.h"] = Target.code().str();
26 Inputs.ExtraArgs.push_back("-include");
27 Inputs.ExtraArgs.push_back("target.h");
28 TestAST AST(Inputs);
29 const auto &SM = AST.sourceManager();
31 // We're only going to record references from the nominated point,
32 // to the target file.
33 FileID ReferencingFile = SM.getMainFileID();
34 SourceLocation ReferencingLoc =
35 SM.getComposedLoc(ReferencingFile, Referencing.point());
36 FileID TargetFile = SM.translateFile(
37 llvm::cantFail(AST.fileManager().getFileRef("target.h")));
39 // Perform the walk, and capture the offsets of the referenced targets.
40 std::vector<size_t> ReferencedOffsets;
41 for (Decl *D : AST.context().getTranslationUnitDecl()->decls()) {
42 if (ReferencingFile != SM.getDecomposedExpansionLoc(D->getLocation()).first)
43 continue;
44 walkAST(*D, [&](SourceLocation Loc, NamedDecl &ND) {
45 if (SM.getFileLoc(Loc) != ReferencingLoc)
46 return;
47 auto NDLoc = SM.getDecomposedLoc(SM.getFileLoc(ND.getLocation()));
48 if (NDLoc.first != TargetFile)
49 return;
50 ReferencedOffsets.push_back(NDLoc.second);
51 });
53 llvm::sort(ReferencedOffsets);
55 // Compare results to the expected points.
56 // For each difference, show the target point in context, like a diagnostic.
57 std::string DiagBuf;
58 llvm::raw_string_ostream DiagOS(DiagBuf);
59 auto *DiagOpts = new DiagnosticOptions();
60 DiagOpts->ShowLevel = 0;
61 DiagOpts->ShowNoteIncludeStack = 0;
62 TextDiagnostic Diag(DiagOS, AST.context().getLangOpts(), DiagOpts);
63 auto DiagnosePoint = [&](const char *Message, unsigned Offset) {
64 Diag.emitDiagnostic(
65 FullSourceLoc(SM.getComposedLoc(TargetFile, Offset), SM),
66 DiagnosticsEngine::Note, Message, {}, {});
68 for (auto Expected : Target.points())
69 if (!llvm::is_contained(ReferencedOffsets, Expected))
70 DiagnosePoint("location not marked used", Expected);
71 for (auto Actual : ReferencedOffsets)
72 if (!llvm::is_contained(Target.points(), Actual))
73 DiagnosePoint("location unexpectedly used", Actual);
75 // If there were any differences, we print the entire referencing code once.
76 if (!DiagBuf.empty())
77 ADD_FAILURE() << DiagBuf << "\nfrom code:\n" << ReferencingCode;
80 TEST(WalkAST, DeclRef) {
81 testWalk("int ^x;", "int y = ^x;");
82 testWalk("int ^foo();", "int y = ^foo();");
83 testWalk("namespace ns { int ^x; }", "int y = ns::^x;");
84 testWalk("struct S { static int ^x; };", "int y = S::^x;");
85 // Canonical declaration only.
86 testWalk("extern int ^x; int x;", "int y = ^x;");
89 TEST(WalkAST, TagType) {
90 testWalk("struct ^S {};", "^S *y;");
91 testWalk("enum ^E {};", "^E *y;");
92 testWalk("struct ^S { static int x; };", "int y = ^S::x;");
95 TEST(WalkAST, Alias) {
96 testWalk(R"cpp(
97 namespace ns { int x; }
98 using ns::^x;
99 )cpp",
100 "int y = ^x;");
101 testWalk(R"cpp(
102 namespace ns { struct S; } // Not used
103 using ns::S; // FIXME: S should be used
104 )cpp",
105 "^S *x;");
108 } // namespace
109 } // namespace include_cleaner
110 } // namespace clang