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