================ @@ -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" +#include "llvm/ADT/Twine.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, ---------------- 5chmidti wrote:
If the check now removes the void cast, the traversal up to the void cast should become a matcher with hasAncestor. I would've suggested this here as well via `optional` and binding the comma or cast ops. https://github.com/llvm/llvm-project/pull/189962 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
