diff options
Diffstat (limited to '')
-rw-r--r-- | build/clang-plugin/TemporaryLifetimeBoundChecker.cpp | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/build/clang-plugin/TemporaryLifetimeBoundChecker.cpp b/build/clang-plugin/TemporaryLifetimeBoundChecker.cpp new file mode 100644 index 0000000000..dc66f62b0d --- /dev/null +++ b/build/clang-plugin/TemporaryLifetimeBoundChecker.cpp @@ -0,0 +1,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); +} |