//===--- RedundantExpressionCheck.cpp - clang-tidy-------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "RedundantExpressionCheck.h" #include "../utils/Matchers.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" using namespace clang::ast_matchers; namespace clang { namespace tidy { namespace misc { static bool AreIdenticalExpr(const Expr *Left, const Expr *Right) { if (!Left || !Right) return !Left && !Right; Left = Left->IgnoreParens(); Right = Right->IgnoreParens(); // Compare classes. if (Left->getStmtClass() != Right->getStmtClass()) return false; // Compare children. Expr::const_child_iterator LeftIter = Left->child_begin(); Expr::const_child_iterator RightIter = Right->child_begin(); while (LeftIter != Left->child_end() && RightIter != Right->child_end()) { if (!AreIdenticalExpr(dyn_cast(*LeftIter), dyn_cast(*RightIter))) return false; ++LeftIter; ++RightIter; } if (LeftIter != Left->child_end() || RightIter != Right->child_end()) return false; // Perform extra checks. switch (Left->getStmtClass()) { default: return false; case Stmt::CharacterLiteralClass: return cast(Left)->getValue() == cast(Right)->getValue(); case Stmt::IntegerLiteralClass: { llvm::APInt LeftLit = cast(Left)->getValue(); llvm::APInt RightLit = cast(Right)->getValue(); return LeftLit.getBitWidth() == RightLit.getBitWidth() && LeftLit == RightLit; } case Stmt::FloatingLiteralClass: return cast(Left)->getValue().bitwiseIsEqual( cast(Right)->getValue()); case Stmt::StringLiteralClass: return cast(Left)->getBytes() == cast(Right)->getBytes(); case Stmt::DeclRefExprClass: return cast(Left)->getDecl() == cast(Right)->getDecl(); case Stmt::MemberExprClass: return cast(Left)->getMemberDecl() == cast(Right)->getMemberDecl(); case Stmt::CStyleCastExprClass: return cast(Left)->getTypeAsWritten() == cast(Right)->getTypeAsWritten(); case Stmt::CallExprClass: case Stmt::ImplicitCastExprClass: case Stmt::ArraySubscriptExprClass: return true; case Stmt::UnaryOperatorClass: if (cast(Left)->isIncrementDecrementOp()) return false; return cast(Left)->getOpcode() == cast(Right)->getOpcode(); case Stmt::BinaryOperatorClass: return cast(Left)->getOpcode() == cast(Right)->getOpcode(); } } AST_MATCHER(BinaryOperator, OperandsAreEquivalent) { return AreIdenticalExpr(Node.getLHS(), Node.getRHS()); } AST_MATCHER(BinaryOperator, isInMacro) { return Node.getOperatorLoc().isMacroID(); } AST_MATCHER(Expr, isInstantiationDependent) { return Node.isInstantiationDependent(); } void RedundantExpressionCheck::registerMatchers(MatchFinder *Finder) { const auto AnyLiteralExpr = ignoringParenImpCasts( anyOf(cxxBoolLiteral(), characterLiteral(), integerLiteral())); Finder->addMatcher( binaryOperator(anyOf(hasOperatorName("-"), hasOperatorName("/"), hasOperatorName("%"), hasOperatorName("|"), hasOperatorName("&"), hasOperatorName("^"), matchers::isComparisonOperator(), hasOperatorName("&&"), hasOperatorName("||"), hasOperatorName("=")), OperandsAreEquivalent(), // Filter noisy false positives. unless(isInstantiationDependent()), unless(isInMacro()), unless(hasType(realFloatingPointType())), unless(hasEitherOperand(hasType(realFloatingPointType()))), unless(hasLHS(AnyLiteralExpr))) .bind("binary"), this); } void RedundantExpressionCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *BinOp = Result.Nodes.getNodeAs("binary")) diag(BinOp->getOperatorLoc(), "both side of operator are equivalent"); } } // namespace misc } // namespace tidy } // namespace clang