/* 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/. */
AstMatcher->addMatcher( // This is a matcher on a method declaration,
cxxMethodDecl( // which is marked as no dangling on temporaries,
noDanglingOnTemporaries(),
// and which is && ref-qualified.
isRValueRefQualified(),
decl().bind("invalidMethodRefQualified")), this);
AstMatcher->addMatcher( // This is a matcher on a method declaration,
cxxMethodDecl( // which is marked as no dangling on temporaries,
noDanglingOnTemporaries(),
// which returns a primitive type,
returns(builtinType()),
// and which doesn't return a pointer.
unless(returns(pointerType())),
decl().bind("invalidMethodPointer")), this);
////////////////// // Main checker // //////////////////
auto hasParentCall = hasParent(
expr(anyOf(cxxOperatorCallExpr( // If we're in a lamda, we may have an operator call // expression ancestor in the AST, but the temporary we're // matching against is not going to have the same lifetime // as the constructor call.
unless(has(expr(ignoreTrivials(lambdaExpr())))),
expr().bind("parentOperatorCallExpr")),
callExpr( // If we're in a lamda, we may have a call expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // function call.
unless(has(expr(ignoreTrivials(lambdaExpr())))),
expr().bind("parentCallExpr")),
objcMessageExpr( // If we're in a lamda, we may have an objc message // expression ancestor in the AST, but the temporary we're // matching against is not going to have the same lifetime // as the function call.
unless(has(expr(ignoreTrivials(lambdaExpr())))),
expr().bind("parentObjCMessageExpr")),
cxxConstructExpr( // If we're in a lamda, we may have a construct expression // ancestor in the AST, but the temporary we're matching // against is not going to have the same lifetime as the // constructor call.
unless(has(expr(ignoreTrivials(lambdaExpr())))),
expr().bind("parentConstructExpr")))));
AstMatcher->addMatcher( // This is a matcher on a method call,
cxxMemberCallExpr( // which is in first party code,
isFirstParty(),
// and which is performed on a temporary,
on(allOf(unless(hasType(pointerType())), isTemporary(), // but which is not `this`.
unless(cxxThisExpr()))),
// and which is marked as no dangling on temporaries.
callee(cxxMethodDecl(noDanglingOnTemporaries())),
expr().bind("memberCallExpr"),
// We optionally match a parent call expression or a parent construct // expression because using a temporary inside a call is fine as long // as the pointer doesn't escape the function call.
anyOf( // This is the case where the call is the direct parent, so we // know that the member call expression is the argument.
allOf(hasParentCall, expr().bind("parentCallArg")),
// This is the case where the call is not the direct parent, so we // get its child to know in which argument tree we are.
hasAncestor(expr(hasParentCall, expr().bind("parentCallArg"))), // To make it optional.
anything())), this);
}
// If we have a parent call, we check whether or not we escape the function // being called. if (ParentOperatorCallExpr || ParentCallExpr || ParentConstructExpr) { // Just in case. if (!ParentCallArg) { return;
}
// No default constructor so we can't construct it using if/else. auto FunctionEscapeData =
ParentOperatorCallExpr
? escapesFunction(ParentCallArg, ParentOperatorCallExpr)
: ParentCallExpr
? escapesFunction(ParentCallArg, ParentCallExpr)
: escapesFunction(ParentCallArg, ParentConstructExpr);
// If there was an error in the escapesFunction call. if (std::error_code ec = FunctionEscapeData.getError()) { // FIXME: For now we ignore the variadic case and just consider that the // argument doesn't escape the function. Same for the case where we can't // find the function declaration or if the function is builtin. if (static_cast<EscapesFunctionError>(ec.value()) ==
EscapesFunctionError::FunctionIsVariadic || static_cast<EscapesFunctionError>(ec.value()) ==
EscapesFunctionError::FunctionDeclNotFound || static_cast<EscapesFunctionError>(ec.value()) ==
EscapesFunctionError::FunctionIsBuiltin) { return;
}
// We emit the internal checker error and return.
diag(MemberCall->getExprLoc(),
std::string(ec.category().name()) + " error: " + ec.message(),
DiagnosticIDs::Error); return;
}
// We deconstruct the function escape data. const Stmt *EscapeStmt; const Decl *EscapeDecl;
std::tie(EscapeStmt, EscapeDecl) = *FunctionEscapeData;
// If we didn't escape a parent function, we're done: we don't emit any // diagnostic. if (!EscapeStmt || !EscapeDecl) { return;
}
// We emit the error diagnostic indicating that we are calling the method // temporary.
diag(MemberCall->getExprLoc(), Error, DiagnosticIDs::Error)
<< MemberCall->getMethodDecl()->getName()
<< MemberCall->getSourceRange();
// We indicate the escape statement.
diag(EscapeStmt->getBeginLoc(), EscapeStmtNote, DiagnosticIDs::Note)
<< EscapeStmt->getSourceRange();
// We build the escape note along with its source range.
StringRef EscapeDeclNote;
SourceRange EscapeDeclRange; if (isa<ParmVarDecl>(EscapeDecl)) {
EscapeDeclNote = "through the parameter declared here";
EscapeDeclRange = EscapeDecl->getSourceRange();
} elseif (isa<VarDecl>(EscapeDecl)) {
EscapeDeclNote = "through the variable declared here";
EscapeDeclRange = EscapeDecl->getSourceRange();
} elseif (isa<FieldDecl>(EscapeDecl)) {
EscapeDeclNote = "through the field declared here";
EscapeDeclRange = EscapeDecl->getSourceRange();
} elseif (auto FuncDecl = dyn_cast<FunctionDecl>(EscapeDecl)) {
EscapeDeclNote = "through the return value of the function declared here";
EscapeDeclRange = FuncDecl->getReturnTypeSourceRange();
} else { return;
}
// We emit the declaration note indicating through which decl the argument // escapes.
diag(EscapeDecl->getLocation(), EscapeDeclNote, DiagnosticIDs::Note)
<< EscapeDeclRange;
} else { // We emit the error diagnostic indicating that we are calling the method // temporary.
diag(MemberCall->getExprLoc(), Error, DiagnosticIDs::Error)
<< MemberCall->getMethodDecl()->getName()
<< MemberCall->getSourceRange();
}
}
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.