99 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "MathMissingParenthesesCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/Lex/Lexer.h"13 14using namespace clang::ast_matchers;15 16namespace clang::tidy::readability {17 18void MathMissingParenthesesCheck::registerMatchers(MatchFinder *Finder) {19 Finder->addMatcher(20 binaryOperator(21 unless(hasParent(binaryOperator(unless(isAssignmentOperator()),22 unless(isComparisonOperator())))),23 unless(isAssignmentOperator()), unless(isComparisonOperator()),24 unless(hasAnyOperatorName("&&", "||")),25 hasDescendant(binaryOperator()))26 .bind("binOp"),27 this);28}29 30static int getPrecedence(const BinaryOperator *BinOp) {31 if (!BinOp)32 return 0;33 switch (BinOp->getOpcode()) {34 case BO_Mul:35 case BO_Div:36 case BO_Rem:37 return 5;38 case BO_Add:39 case BO_Sub:40 return 4;41 case BO_And:42 return 3;43 case BO_Xor:44 return 2;45 case BO_Or:46 return 1;47 default:48 return 0;49 }50}51static void addParentheses(const BinaryOperator *BinOp,52 const BinaryOperator *ParentBinOp,53 ClangTidyCheck *Check,54 const clang::SourceManager &SM,55 const clang::LangOptions &LangOpts) {56 if (!BinOp)57 return;58 59 const int Precedence1 = getPrecedence(BinOp);60 const int Precedence2 = getPrecedence(ParentBinOp);61 62 if (ParentBinOp != nullptr && Precedence1 != Precedence2 && Precedence1 > 0 &&63 Precedence2 > 0) {64 const clang::SourceLocation StartLoc = BinOp->getBeginLoc();65 const clang::SourceLocation EndLoc =66 clang::Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0, SM, LangOpts);67 68 auto Diag =69 Check->diag(StartLoc,70 "'%0' has higher precedence than '%1'; add parentheses to "71 "explicitly specify the order of operations")72 << (Precedence1 > Precedence2 ? BinOp->getOpcodeStr()73 : ParentBinOp->getOpcodeStr())74 << (Precedence1 > Precedence2 ? ParentBinOp->getOpcodeStr()75 : BinOp->getOpcodeStr())76 << SourceRange(StartLoc, EndLoc);77 78 if (EndLoc.isValid()) {79 Diag << FixItHint::CreateInsertion(StartLoc, "(")80 << FixItHint::CreateInsertion(EndLoc, ")");81 }82 }83 84 addParentheses(dyn_cast<BinaryOperator>(BinOp->getLHS()->IgnoreImpCasts()),85 BinOp, Check, SM, LangOpts);86 addParentheses(dyn_cast<BinaryOperator>(BinOp->getRHS()->IgnoreImpCasts()),87 BinOp, Check, SM, LangOpts);88}89 90void MathMissingParenthesesCheck::check(91 const MatchFinder::MatchResult &Result) {92 const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binOp");93 const SourceManager &SM = *Result.SourceManager;94 const clang::LangOptions &LO = Result.Context->getLangOpts();95 addParentheses(BinOp, nullptr, this, SM, LO);96}97 98} // namespace clang::tidy::readability99