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"
11 namespace include_cleaner
{
14 // Specifies a test of which symbols are referenced by a piece of code.
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");
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
)
44 walkAST(*D
, [&](SourceLocation Loc
, NamedDecl
&ND
) {
45 if (SM
.getFileLoc(Loc
) != ReferencingLoc
)
47 auto NDLoc
= SM
.getDecomposedLoc(SM
.getFileLoc(ND
.getLocation()));
48 if (NDLoc
.first
!= TargetFile
)
50 ReferencedOffsets
.push_back(NDLoc
.second
);
53 llvm::sort(ReferencedOffsets
);
55 // Compare results to the expected points.
56 // For each difference, show the target point in context, like a diagnostic.
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
) {
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.
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
) {
97 namespace ns { int x; }
102 namespace ns { struct S; } // Not used
103 using ns::S; // FIXME: S should be used
109 } // namespace include_cleaner