clang-tools 22.0.0git
MathMissingParenthesesCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
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
17
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 Expr *E, const BinaryOperator *ParentBinOp,
52 ClangTidyCheck *Check,
53 const clang::SourceManager &SM,
54 const clang::LangOptions &LangOpts) {
55 if (const auto *Paren = dyn_cast<ParenExpr>(E)) {
56 addParentheses(Paren->getSubExpr()->IgnoreImpCasts(), nullptr, Check, SM,
57 LangOpts);
58 return;
59 }
60
61 const auto *BinOp = dyn_cast<BinaryOperator>(E);
62 if (!BinOp)
63 return;
64
65 const int Precedence1 = getPrecedence(BinOp);
66 const int Precedence2 = getPrecedence(ParentBinOp);
67
68 if (ParentBinOp != nullptr && Precedence1 != Precedence2 && Precedence1 > 0 &&
69 Precedence2 > 0) {
70 const clang::SourceLocation StartLoc = BinOp->getBeginLoc();
71 const clang::SourceLocation EndLoc =
72 clang::Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0, SM, LangOpts);
73
74 auto Diag =
75 Check->diag(StartLoc,
76 "'%0' has higher precedence than '%1'; add parentheses to "
77 "explicitly specify the order of operations")
78 << (Precedence1 > Precedence2 ? BinOp->getOpcodeStr()
79 : ParentBinOp->getOpcodeStr())
80 << (Precedence1 > Precedence2 ? ParentBinOp->getOpcodeStr()
81 : BinOp->getOpcodeStr())
82 << SourceRange(StartLoc, EndLoc);
83
84 if (EndLoc.isValid()) {
85 Diag << FixItHint::CreateInsertion(StartLoc, "(")
86 << FixItHint::CreateInsertion(EndLoc, ")");
87 }
88 }
89
90 addParentheses(BinOp->getLHS()->IgnoreImpCasts(), BinOp, Check, SM, LangOpts);
91 addParentheses(BinOp->getRHS()->IgnoreImpCasts(), BinOp, Check, SM, LangOpts);
92}
93
95 const MatchFinder::MatchResult &Result) {
96 const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binOp");
97 const SourceManager &SM = *Result.SourceManager;
98 const clang::LangOptions &LO = Result.Context->getLangOpts();
99 addParentheses(BinOp, nullptr, this, SM, LO);
100}
101
102} // namespace clang::tidy::readability
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
static int getPrecedence(const BinaryOperator *BinOp)
static void addParentheses(const Expr *E, const BinaryOperator *ParentBinOp, ClangTidyCheck *Check, const clang::SourceManager &SM, const clang::LangOptions &LangOpts)