1 #include "TestingSupport.h"
2 #include "clang/AST/ASTContext.h"
3 #include "clang/AST/Decl.h"
4 #include "clang/AST/Stmt.h"
5 #include "clang/ASTMatchers/ASTMatchFinder.h"
6 #include "clang/ASTMatchers/ASTMatchers.h"
7 #include "clang/Basic/LLVM.h"
8 #include "clang/Basic/LangOptions.h"
9 #include "clang/Basic/SourceLocation.h"
10 #include "clang/Basic/SourceManager.h"
11 #include "clang/Basic/TokenKinds.h"
12 #include "clang/Lex/Lexer.h"
13 #include "llvm/ADT/DenseMap.h"
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/ADT/StringSet.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Testing/Annotations/Annotations.h"
18 #include "gtest/gtest.h"
23 #include <system_error>
27 using namespace clang
;
28 using namespace dataflow
;
29 using namespace ast_matchers
;
32 isAnnotationDirectlyAfterStatement(const Stmt
*Stmt
, unsigned AnnotationBegin
,
33 const SourceManager
&SourceManager
,
34 const LangOptions
&LangOptions
) {
36 Lexer::findNextToken(Stmt
->getEndLoc(), SourceManager
, LangOptions
);
38 while (NextToken
&& SourceManager
.getFileOffset(NextToken
->getLocation()) <
40 if (NextToken
->isNot(tok::semi
))
43 NextToken
= Lexer::findNextToken(NextToken
->getEndLoc(), SourceManager
,
50 llvm::DenseMap
<unsigned, std::string
> test::buildLineToAnnotationMapping(
51 const SourceManager
&SM
, const LangOptions
&LangOpts
,
52 SourceRange BoundingRange
, llvm::Annotations AnnotatedCode
) {
53 CharSourceRange CharBoundingRange
=
54 Lexer::getAsCharRange(BoundingRange
, SM
, LangOpts
);
56 llvm::DenseMap
<unsigned, std::string
> LineNumberToContent
;
57 auto Code
= AnnotatedCode
.code();
58 auto Annotations
= AnnotatedCode
.ranges();
59 for (auto &AnnotationRange
: Annotations
) {
60 SourceLocation Loc
= SM
.getLocForStartOfFile(SM
.getMainFileID())
61 .getLocWithOffset(AnnotationRange
.Begin
);
62 if (SM
.isPointWithin(Loc
, CharBoundingRange
.getBegin(),
63 CharBoundingRange
.getEnd())) {
64 LineNumberToContent
[SM
.getPresumedLineNumber(Loc
)] =
65 Code
.slice(AnnotationRange
.Begin
, AnnotationRange
.End
).str();
68 return LineNumberToContent
;
71 llvm::Expected
<llvm::DenseMap
<const Stmt
*, std::string
>>
72 test::buildStatementToAnnotationMapping(const FunctionDecl
*Func
,
73 llvm::Annotations AnnotatedCode
) {
74 llvm::DenseMap
<const Stmt
*, std::string
> Result
;
75 llvm::StringSet
<> ExistingAnnotations
;
78 findAll(stmt(unless(anyOf(hasParent(expr()), hasParent(returnStmt()))))
81 // This map should stay sorted because the binding algorithm relies on the
82 // ordering of statement offsets
83 std::map
<unsigned, const Stmt
*> Stmts
;
84 auto &Context
= Func
->getASTContext();
85 auto &SourceManager
= Context
.getSourceManager();
87 for (auto &Match
: match(StmtMatcher
, *Func
->getBody(), Context
)) {
88 const auto *S
= Match
.getNodeAs
<Stmt
>("stmt");
89 unsigned Offset
= SourceManager
.getFileOffset(S
->getEndLoc());
93 unsigned FunctionBeginOffset
=
94 SourceManager
.getFileOffset(Func
->getBeginLoc());
95 unsigned FunctionEndOffset
= SourceManager
.getFileOffset(Func
->getEndLoc());
97 std::vector
<llvm::Annotations::Range
> Annotations
= AnnotatedCode
.ranges();
98 llvm::erase_if(Annotations
, [=](llvm::Annotations::Range R
) {
99 return R
.Begin
< FunctionBeginOffset
|| R
.End
>= FunctionEndOffset
;
101 std::reverse(Annotations
.begin(), Annotations
.end());
102 auto Code
= AnnotatedCode
.code();
105 for (auto OffsetAndStmt
= Stmts
.rbegin(); OffsetAndStmt
!= Stmts
.rend();
107 unsigned Offset
= OffsetAndStmt
->first
;
108 const Stmt
*Stmt
= OffsetAndStmt
->second
;
110 if (I
< Annotations
.size() && Annotations
[I
].Begin
>= Offset
) {
111 auto Range
= Annotations
[I
];
113 if (!isAnnotationDirectlyAfterStatement(Stmt
, Range
.Begin
, SourceManager
,
114 Context
.getLangOpts())) {
115 return llvm::createStringError(
116 std::make_error_code(std::errc::invalid_argument
),
117 "Annotation is not placed after a statement: %s",
118 SourceManager
.getLocForStartOfFile(SourceManager
.getMainFileID())
119 .getLocWithOffset(Offset
)
120 .printToString(SourceManager
)
124 auto Annotation
= Code
.slice(Range
.Begin
, Range
.End
).str();
125 if (!ExistingAnnotations
.insert(Annotation
).second
) {
126 return llvm::createStringError(
127 std::make_error_code(std::errc::invalid_argument
),
128 "Repeated use of annotation: %s", Annotation
.data());
130 Result
[Stmt
] = std::move(Annotation
);
134 if (I
< Annotations
.size() && Annotations
[I
].Begin
>= Offset
) {
135 return llvm::createStringError(
136 std::make_error_code(std::errc::invalid_argument
),
137 "Multiple annotations bound to the statement at the location: %s",
138 Stmt
->getBeginLoc().printToString(SourceManager
).data());
143 if (I
< Annotations
.size()) {
144 return llvm::createStringError(
145 std::make_error_code(std::errc::invalid_argument
),
146 "Not all annotations were bound to statements. Unbound annotation at: "
148 SourceManager
.getLocForStartOfFile(SourceManager
.getMainFileID())
149 .getLocWithOffset(Annotations
[I
].Begin
)
150 .printToString(SourceManager
)
157 llvm::Error
test::checkDataflowWithNoopAnalysis(
158 llvm::StringRef Code
,
160 void(const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &,
163 DataflowAnalysisOptions Options
, LangStandard::Kind Std
,
164 llvm::StringRef TargetFun
) {
165 return checkDataflowWithNoopAnalysis(Code
, ast_matchers::hasName(TargetFun
),
166 VerifyResults
, Options
, Std
);
169 llvm::Error
test::checkDataflowWithNoopAnalysis(
170 llvm::StringRef Code
,
171 ast_matchers::internal::Matcher
<FunctionDecl
> TargetFuncMatcher
,
173 void(const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &,
176 DataflowAnalysisOptions Options
, LangStandard::Kind Std
) {
177 llvm::SmallVector
<std::string
, 3> ASTBuildArgs
= {
178 // -fnodelayed-template-parsing is the default everywhere but on Windows.
179 // Set it explicitly so that tests behave the same on Windows as on other
181 "-fsyntax-only", "-fno-delayed-template-parsing",
183 std::string(LangStandard::getLangStandardForKind(Std
).getName())};
184 AnalysisInputs
<NoopAnalysis
> AI(
185 Code
, TargetFuncMatcher
,
186 [UseBuiltinModel
= Options
.BuiltinOpts
.has_value()](ASTContext
&C
,
190 DataflowAnalysisOptions
{
191 UseBuiltinModel
? Env
.getDataflowAnalysisContext().getOptions()
192 : std::optional
<BuiltinOptions
>()});
194 AI
.ASTBuildArgs
= ASTBuildArgs
;
195 if (Options
.BuiltinOpts
)
196 AI
.BuiltinOptions
= *Options
.BuiltinOpts
;
197 return checkDataflow
<NoopAnalysis
>(
201 const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
202 const AnalysisOutputs
&AO
) { VerifyResults(Results
, AO
.ASTCtx
); });
205 const ValueDecl
*test::findValueDecl(ASTContext
&ASTCtx
, llvm::StringRef Name
) {
206 auto TargetNodes
= match(
207 valueDecl(unless(indirectFieldDecl()), hasName(Name
)).bind("v"), ASTCtx
);
208 assert(TargetNodes
.size() == 1 && "Name must be unique");
209 auto *const Result
= selectFirst
<ValueDecl
>("v", TargetNodes
);
210 assert(Result
!= nullptr);
214 const IndirectFieldDecl
*test::findIndirectFieldDecl(ASTContext
&ASTCtx
,
215 llvm::StringRef Name
) {
216 auto TargetNodes
= match(indirectFieldDecl(hasName(Name
)).bind("i"), ASTCtx
);
217 assert(TargetNodes
.size() == 1 && "Name must be unique");
218 const auto *Result
= selectFirst
<IndirectFieldDecl
>("i", TargetNodes
);
219 assert(Result
!= nullptr);
223 std::vector
<const Formula
*> test::parseFormulas(Arena
&A
, StringRef Lines
) {
224 std::vector
<const Formula
*> Result
;
225 while (!Lines
.empty()) {
226 auto [First
, Rest
] = Lines
.split('\n');
228 if (First
.trim().empty())
230 if (auto F
= A
.parseFormula(First
))
231 Result
.push_back(&*F
);
233 ADD_FAILURE() << llvm::toString(F
.takeError());