================ @@ -0,0 +1,291 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "UseBitCastCheck.h" +#include "../utils/Matchers.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/STLExtras.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +static bool isSupportedMemcpyObjectExpr(const Expr *ExprNode) { + ExprNode = ExprNode->IgnoreParenImpCasts(); + + if (isa<DeclRefExpr>(ExprNode)) + return true; + + const auto *Member = dyn_cast<MemberExpr>(ExprNode); + if (!Member || !isa<FieldDecl>(Member->getMemberDecl())) + if (const auto *MemberPointer = dyn_cast<BinaryOperator>(ExprNode)) + if (MemberPointer->getOpcode() == BO_PtrMemD || + MemberPointer->getOpcode() == BO_PtrMemI) + return isSupportedMemcpyObjectExpr(MemberPointer->getLHS()); + + return Member && isSupportedMemcpyObjectExpr(Member->getBase()); +} + +static const Expr *extractMemcpyObjectExpr(const Expr *ExprNode) { + ExprNode = ExprNode->IgnoreParenCasts(); + const auto *AddressOf = dyn_cast<UnaryOperator>(ExprNode); + if (!AddressOf || AddressOf->getOpcode() != UO_AddrOf) + return nullptr; + + const Expr *ObjectExpr = AddressOf->getSubExpr()->IgnoreParenImpCasts(); + return isSupportedMemcpyObjectExpr(ObjectExpr) ? ObjectExpr : nullptr; +} + +static bool isBitCastableMemcpyObjectType(QualType Type, + const ASTContext &Context) { + Type = Type.getCanonicalType().getNonReferenceType(); + return !Type.isNull() && !Type.isVolatileQualified() && + !Type->isAnyPointerType() && !Type->isFunctionType() && + Type.isTriviallyCopyableType(Context) && + Type.isBitwiseCloneableType(Context); +} + +static bool canAssignBitCastResult(QualType Type) { + Type = Type.getCanonicalType().getNonReferenceType(); + if (Type.isNull() || Type.isConstQualified() || Type->isArrayType()) + return false; + + const auto *Record = Type->getAsCXXRecordDecl(); + return !Record || Record->hasSimpleCopyAssignment() || + Record->hasSimpleMoveAssignment(); +} + +static bool isSameUnqualifiedCanonicalType(QualType LHS, QualType RHS) { + return LHS.getCanonicalType().getUnqualifiedType() == + RHS.getCanonicalType().getUnqualifiedType(); +} + +static bool isMatchingSizeOfExpression(const Expr *SizeExpr, QualType SrcType, + QualType DstType, + const ASTContext &Context) { + const auto *UnaryExpr = + dyn_cast<UnaryExprOrTypeTraitExpr>(SizeExpr->IgnoreParenImpCasts()); + if (!UnaryExpr || UnaryExpr->getKind() != UETT_SizeOf || + SizeExpr->getBeginLoc().isMacroID()) + return false; + + const QualType SizeType = UnaryExpr->getTypeOfArgument(); + if (SizeType.isNull()) + return false; + + const QualType SizeCanonical = + SizeType.getCanonicalType().getUnqualifiedType(); + const QualType SrcCanonical = SrcType.getCanonicalType().getUnqualifiedType(); + const QualType DstCanonical = DstType.getCanonicalType().getUnqualifiedType(); + if (SizeCanonical != SrcCanonical && SizeCanonical != DstCanonical) + return false; + + return Context.getTypeSizeInChars(SrcCanonical) == + Context.getTypeSizeInChars(DstCanonical); +} + +static bool isStatementBody(const Stmt *Current, const Stmt *Parent) { + if (const auto *Block = dyn_cast<CompoundStmt>(Parent)) + return llvm::is_contained(Block->body(), Current); + + if (const auto *If = dyn_cast<IfStmt>(Parent)) + return If->getThen() == Current || If->getElse() == Current; + if (const auto *While = dyn_cast<WhileStmt>(Parent)) + return While->getBody() == Current; + if (const auto *Do = dyn_cast<DoStmt>(Parent)) + return Do->getBody() == Current; + if (const auto *For = dyn_cast<ForStmt>(Parent)) + return For->getBody() == Current; + if (const auto *RangeFor = dyn_cast<CXXForRangeStmt>(Parent)) + return RangeFor->getBody() == Current; + if (const auto *Label = dyn_cast<LabelStmt>(Parent)) + return Label->getSubStmt() == Current; + if (const auto *Case = dyn_cast<SwitchCase>(Parent)) + return Case->getSubStmt() == Current; + if (const auto *Attributed = dyn_cast<AttributedStmt>(Parent)) + return Attributed->getSubStmt() == Current; + + return false; +} + +namespace { + +// These states describe how to spell the replacement when only the memcpy call +// is replaced. An existing `(void)` cast is preserved by parenthesizing the +// assignment, while comma/discarded subexpressions need an injected `(void)`. +enum class MemcpyReplacementForm { + None, + StatementBody, + PreserveOuterVoidCast, + InjectVoidCast, +}; + +} // namespace + +static MemcpyReplacementForm getMemcpyReplacementForm(const Expr *ExprNode, + ASTContext &Context) { + const Stmt *Current = ExprNode; + MemcpyReplacementForm Kind = MemcpyReplacementForm::StatementBody; + + while (true) { + auto Parents = Context.getParents(*Current); + if (Parents.size() != 1) + return MemcpyReplacementForm::None; + + if (const auto *ParentExpr = Parents[0].get<Expr>()) { + if (isa<ExprWithCleanups, ImplicitCastExpr, MaterializeTemporaryExpr, + CXXBindTemporaryExpr, ParenExpr>(ParentExpr)) { + Current = ParentExpr; + continue; + } + + if (const auto *Cast = dyn_cast<CastExpr>(ParentExpr)) + if (Cast->getCastKind() == CK_ToVoid) + return MemcpyReplacementForm::PreserveOuterVoidCast; + + if (const auto *Comma = dyn_cast<BinaryOperator>(ParentExpr)) { + if (Comma->getOpcode() != BO_Comma) + return MemcpyReplacementForm::None; + if (Comma->getLHS() == Current) + return MemcpyReplacementForm::InjectVoidCast; + if (Comma->getRHS() == Current) { + Current = Comma; + Kind = MemcpyReplacementForm::InjectVoidCast; + continue; + } + } + + return MemcpyReplacementForm::None; + } + + const auto *ParentStmt = Parents[0].get<Stmt>(); + if (!ParentStmt || !isStatementBody(Current, ParentStmt)) + return MemcpyReplacementForm::None; + return Kind; + } +} + +namespace { + +AST_MATCHER(CallExpr, isDiscardedValueContext) { + return getMemcpyReplacementForm(&Node, Finder->getASTContext()) != + MemcpyReplacementForm::None; +} + +AST_MATCHER(CallExpr, isBitCastMemcpyCandidate) { + if (Node.getNumArgs() != 3 || Node.getBeginLoc().isMacroID()) + return false; + + const auto *DstExpr = extractMemcpyObjectExpr(Node.getArg(0)); + const auto *SrcExpr = extractMemcpyObjectExpr(Node.getArg(1)); + if (!DstExpr || !SrcExpr || DstExpr->getBeginLoc().isMacroID() || + SrcExpr->getBeginLoc().isMacroID()) + return false; + + const auto &Context = Finder->getASTContext(); + const QualType DstType = DstExpr->getType().getNonReferenceType(); + const QualType SrcType = SrcExpr->getType().getNonReferenceType(); + + return isBitCastableMemcpyObjectType(DstType, Context) && + isBitCastableMemcpyObjectType(SrcType, Context) && + canAssignBitCastResult(DstType) && + !isSameUnqualifiedCanonicalType(SrcType, DstType) && + isMatchingSizeOfExpression(Node.getArg(2), SrcType, DstType, Context); +} + +} // namespace + +static StringRef getSourceText(const Expr *ExprNode, const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::getSourceText( + CharSourceRange::getTokenRange(ExprNode->getSourceRange()), SM, LangOpts); +} + +UseBitCastCheck::UseBitCastCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", + utils::IncludeSorter::IS_LLVM), + areDiagsSelfContained()) {} + +void UseBitCastCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); +} + +void UseBitCastCheck::registerPPCallbacks(const SourceManager &SM, + Preprocessor *PP, + Preprocessor *ModuleExpanderPP) { + IncludeInserter.registerPreprocessor(PP); +} + +void UseBitCastCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr(callee(functionDecl(hasName("::memcpy"))), + isDiscardedValueContext(), unless(isInTemplateInstantiation()), ---------------- unterumarmung wrote:
Done https://github.com/llvm/llvm-project/pull/189962 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
