1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TemporaryLifetimeBoundChecker.h"
#include "CustomMatchers.h"
#include "clang/Lex/Lexer.h"
void TemporaryLifetimeBoundChecker::registerMatchers(MatchFinder *AstMatcher) {
// Look for a call to a MOZ_LIFETIME_BOUND member function
auto isTemporaryLifetimeBoundCall =
cxxMemberCallExpr(
onImplicitObjectArgument(anyOf(has(cxxTemporaryObjectExpr()),
has(materializeTemporaryExpr()))),
callee(functionDecl(isMozTemporaryLifetimeBound())))
.bind("call");
// XXX This definitely does not catch everything relevant. In particular, the
// matching on conditionalOperator would need to be recursive. But it's a
// start.
auto hasTemporaryLifetimeBoundCall =
anyOf(isTemporaryLifetimeBoundCall,
conditionalOperator(
anyOf(hasFalseExpression(isTemporaryLifetimeBoundCall),
hasTrueExpression(isTemporaryLifetimeBoundCall))));
AstMatcher->addMatcher(
returnStmt(hasReturnValue(
allOf(exprWithCleanups().bind("expr-with-cleanups"),
ignoringParenCasts(hasTemporaryLifetimeBoundCall))))
.bind("return-stmt"),
this);
AstMatcher->addMatcher(
varDecl(hasType(references(cxxRecordDecl())),
hasInitializer(
allOf(exprWithCleanups(),
ignoringParenCasts(hasTemporaryLifetimeBoundCall))))
.bind("var-decl"),
this);
}
void TemporaryLifetimeBoundChecker::check(
const MatchFinder::MatchResult &Result) {
const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
const auto *ReturnStatement =
Result.Nodes.getNodeAs<ReturnStmt>("return-stmt");
const auto *ReferenceVarDecl = Result.Nodes.getNodeAs<VarDecl>("var-decl");
const char ErrorReturn[] =
"cannot return result of lifetime-bound function %0 on "
"temporary of type %1";
const char ErrorBindToReference[] =
"cannot bind result of lifetime-bound function %0 on "
"temporary of type %1 to reference, does not extend lifetime";
const char NoteCalledFunction[] = "member function declared here";
// We are either a return statement...
if (ReturnStatement) {
const auto *ExprWithCleanups =
Result.Nodes.getNodeAs<Expr>("expr-with-cleanups");
if (!ExprWithCleanups->isLValue()) {
return;
}
const auto Range = ReturnStatement->getSourceRange();
diag(Range.getBegin(), ErrorReturn, DiagnosticIDs::Error)
<< Range << Call->getMethodDecl()
<< Call->getImplicitObjectArgument()
->getType()
.withoutLocalFastQualifiers();
}
// ... or a variable declaration that declare a reference
if (ReferenceVarDecl) {
const auto Range = ReferenceVarDecl->getSourceRange();
diag(Range.getBegin(), ErrorBindToReference, DiagnosticIDs::Error)
<< Range << Call->getMethodDecl()
<< Call->getImplicitObjectArgument()
->getType()
.withoutLocalFastQualifiers();
}
const auto *MethodDecl = Call->getMethodDecl();
diag(MethodDecl->getCanonicalDecl()->getLocation(), NoteCalledFunction,
DiagnosticIDs::Note);
}
|