/* 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("call"); const auto *ReturnStatement = Result.Nodes.getNodeAs("return-stmt"); const auto *ReferenceVarDecl = Result.Nodes.getNodeAs("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-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); }