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