https://github.com/11happy created https://github.com/llvm/llvm-project/pull/84481
**Overview:** This pull request fixes #80850 where author suggests adding a readability check to detect missing parentheses around mathematical expressions when operators of different priorities are used. **Testing:** - Tested the updated code. - Verified that other functionalities remain unaffected.  **Dependencies:** - No dependencies on other pull requests. **CC:** - @PiotrZSL >From 8fdf6306085ed4cf0f77b7e718e374e9f65fedf9 Mon Sep 17 00:00:00 2001 From: 11happy <soni5ha...@gmail.com> Date: Fri, 8 Mar 2024 19:02:47 +0530 Subject: [PATCH] add clang-tidy check readability-math-missing-parentheses Signed-off-by: 11happy <soni5ha...@gmail.com> --- .../clang-tidy/readability/CMakeLists.txt | 1 + .../MathMissingParenthesesCheck.cpp | 167 ++++++++++++++++++ .../readability/MathMissingParenthesesCheck.h | 31 ++++ .../readability/ReadabilityTidyModule.cpp | 3 + clang-tools-extra/docs/ReleaseNotes.rst | 6 + .../docs/clang-tidy/checks/list.rst | 1 + .../readability/math-missing-parentheses.rst | 19 ++ .../readability/math-missing-parentheses.cpp | 42 +++++ 8 files changed, 270 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/readability/math-missing-parentheses.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt index a6c8cbd8eb448a..0d4fa095501dfb 100644 --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -27,6 +27,7 @@ add_clang_library(clangTidyReadabilityModule IsolateDeclarationCheck.cpp MagicNumbersCheck.cpp MakeMemberFunctionConstCheck.cpp + MathMissingParenthesesCheck.cpp MisleadingIndentationCheck.cpp MisplacedArrayIndexCheck.cpp NamedParameterCheck.cpp diff --git a/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp b/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp new file mode 100644 index 00000000000000..d9574a9fb7a476 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp @@ -0,0 +1,167 @@ +//===--- MathMissingParenthesesCheck.cpp - clang-tidy ---------------------===// +// +// 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 "MathMissingParenthesesCheck.h" +#include "../utils/ASTUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Preprocessor.h" +#include <set> +#include <stack> + +using namespace clang::ast_matchers; + +namespace clang::tidy::readability { + +void MathMissingParenthesesCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(binaryOperator(unless(hasParent(binaryOperator())), + hasDescendant(binaryOperator())) + .bind("binOp"), + this); +} +static int precedenceCheck(const char op) { + if (op == '/' || op == '*' || op == '%') + return 5; + + else if (op == '+' || op == '-') + return 4; + + else if (op == '&') + return 3; + else if (op == '^') + return 2; + + else if (op == '|') + return 1; + + else + return 0; +} +static bool isOperand(const char c) { + if (c >= 'a' && c <= 'z') + return true; + else if (c >= 'A' && c <= 'Z') + return true; + else if (c >= '0' && c <= '9') + return true; + else if (c == '$') + return true; + else + return false; +} +static bool conditionForNegative(const std::string s, int i, + const std::string CurStr) { + if (CurStr[0] == '-') { + if (i == 0) { + return true; + } else { + while (s[i - 1] == ' ') { + i--; + } + if (!isOperand(s[i - 1])) { + return true; + } else { + return false; + } + } + } else { + return false; + } +} +static std::string getOperationOrder(std::string s, std::set<char> &Operators) { + std::stack<std::string> StackOne; + std::string TempStr = ""; + for (int i = 0; i < s.length(); i++) { + std::string CurStr = ""; + CurStr += s[i]; + if (CurStr == " ") + continue; + else { + if (isOperand(CurStr[0]) || conditionForNegative(s, i, CurStr)) { + while (i < s.length() && (isOperand(s[i]) || s[i] == '-')) { + if (s[i] == '-') { + TempStr += "$"; + } else { + TempStr += CurStr; + } + i++; + CurStr = s[i]; + } + TempStr += " "; + } else if (CurStr == "(") { + StackOne.push("("); + } else if (CurStr == ")") { + while (StackOne.top() != "(") { + TempStr += StackOne.top(); + StackOne.pop(); + } + StackOne.pop(); + } else { + while (!StackOne.empty() && precedenceCheck(CurStr[0]) <= + precedenceCheck((StackOne.top())[0])) { + TempStr += StackOne.top(); + StackOne.pop(); + } + StackOne.push(CurStr); + } + } + } + while (!StackOne.empty()) { + TempStr += StackOne.top(); + StackOne.pop(); + } + std::stack<std::string> StackTwo; + for (int i = 0; i < TempStr.length(); i++) { + if (TempStr[i] == ' ') + continue; + else if (isOperand(TempStr[i])) { + std::string CurStr = ""; + while (i < TempStr.length() && isOperand(TempStr[i])) { + if (TempStr[i] == '$') { + CurStr += "-"; + } else { + CurStr += TempStr[i]; + } + i++; + } + StackTwo.push(CurStr); + } else { + std::string OperandOne = StackTwo.top(); + StackTwo.pop(); + std::string OperandTwo = StackTwo.top(); + StackTwo.pop(); + Operators.insert(TempStr[i]); + StackTwo.push("(" + OperandTwo + " " + TempStr[i] + " " + OperandOne + + ")"); + } + } + return StackTwo.top(); +} +void MathMissingParenthesesCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binOp"); + if (!BinOp) + return; + clang::SourceManager &SM = *Result.SourceManager; + clang::LangOptions LO = Result.Context->getLangOpts(); + clang::CharSourceRange Range = + clang::CharSourceRange::getTokenRange(BinOp->getSourceRange()); + std::string Expression = clang::Lexer::getSourceText(Range, SM, LO).str(); + std::set<char> Operators; + std::string FinalExpression = getOperationOrder(Expression, Operators); + if (Operators.size() > 1) { + if (FinalExpression.length() > 2) { + FinalExpression = FinalExpression.substr(1, FinalExpression.length() - 2); + } + diag(BinOp->getBeginLoc(), + "add parantheses to clarify the precedence of operations") + << FixItHint::CreateReplacement(Range, FinalExpression); + } +} + +} // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.h b/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.h new file mode 100644 index 00000000000000..60b402831b45e0 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.h @@ -0,0 +1,31 @@ +//===--- MathMissingParenthesesCheck.h - clang-tidy -------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MATHMISSINGPARENTHESESCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MATHMISSINGPARENTHESESCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::readability { + +/// Checks for mathematical expressions that involve operators of different +/// priorities. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability/math-missing-parentheses.html +class MathMissingParenthesesCheck : public ClangTidyCheck { +public: + MathMissingParenthesesCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace clang::tidy::readability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MATHMISSINGPARENTHESESCHECK_H diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp index 87b299bf1ef1c5..b198153d27ee49 100644 --- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -31,6 +31,7 @@ #include "IsolateDeclarationCheck.h" #include "MagicNumbersCheck.h" #include "MakeMemberFunctionConstCheck.h" +#include "MathMissingParenthesesCheck.h" #include "MisleadingIndentationCheck.h" #include "MisplacedArrayIndexCheck.h" #include "NamedParameterCheck.h" @@ -101,6 +102,8 @@ class ReadabilityModule : public ClangTidyModule { "readability-identifier-naming"); CheckFactories.registerCheck<ImplicitBoolConversionCheck>( "readability-implicit-bool-conversion"); + CheckFactories.registerCheck<MathMissingParenthesesCheck>( + "readability-math-missing-parentheses"); CheckFactories.registerCheck<RedundantInlineSpecifierCheck>( "readability-redundant-inline-specifier"); CheckFactories.registerCheck<InconsistentDeclarationParameterNameCheck>( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index fd2cba4e4f463b..61b9665ffe73ed 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -100,6 +100,12 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`readability-math-missing-parentheses + <clang-tidy/checks/readability/math-missing-parentheses>` check. + + Checks for mathematical expressions that involve operators + of different priorities. + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index f773e80b562e4f..0cc5c217899d4d 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -359,6 +359,7 @@ Clang-Tidy Checks :doc:`readability-isolate-declaration <readability/isolate-declaration>`, "Yes" :doc:`readability-magic-numbers <readability/magic-numbers>`, :doc:`readability-make-member-function-const <readability/make-member-function-const>`, "Yes" + :doc:`readability-math-missing-parentheses <readability/math-missing-parentheses>`, "Yes" :doc:`readability-misleading-indentation <readability/misleading-indentation>`, :doc:`readability-misplaced-array-index <readability/misplaced-array-index>`, "Yes" :doc:`readability-named-parameter <readability/named-parameter>`, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/math-missing-parentheses.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/math-missing-parentheses.rst new file mode 100644 index 00000000000000..fb3167e8e0f8f1 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/math-missing-parentheses.rst @@ -0,0 +1,19 @@ +.. title:: clang-tidy - readability-math-missing-parentheses + +readability-math-missing-parentheses +==================================== + +Checks for mathematical expressions that involve operators of different priorities. + +Before: + +.. code-block:: c++ + + int x = 1 + 2 * 3 - 4 / 5; + + +After: + +.. code-block:: c++ + + int x = (1 + (2 * 3)) - (4 / 5); \ No newline at end of file diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp new file mode 100644 index 00000000000000..54cc0d4dabbdee --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp @@ -0,0 +1,42 @@ +// RUN: %check_clang_tidy %s readability-math-missing-parentheses %t + +// FIXME: Add something that triggers the check here. +void f(){ + //CHECK-MESSAGES: :[[@LINE+2]]:13: warning: add parantheses to clarify the precedence of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int a = 1 + (2 * 3); + int a = 1 + 2 * 3; + + int b = 1 + 2 + 3; // No warning + + int c = 1 * 2 * 3; // No warning + + //CHECK-MESSAGES: :[[@LINE+2]]:13: warning: add parantheses to clarify the precedence of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int d = (1 + (2 * 3)) - (4 / 5); + int d = 1 + 2 * 3 - 4 / 5; + + //CHECK-MESSAGES: :[[@LINE+2]]:13: warning: add parantheses to clarify the precedence of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int e = (1 & (2 + 3)) | (4 * 5); + int e = 1 & 2 + 3 | 4 * 5; + + //CHECK-MESSAGES: :[[@LINE+2]]:13: warning: add parantheses to clarify the precedence of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int f = (1 * -2) + 4; + int f = 1 * -2 + 4; + + //CHECK-MESSAGES: :[[@LINE+2]]:13: warning: add parantheses to clarify the precedence of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int g = (((1 * 2) * 3) + 4) + 5; + int g = 1 * 2 * 3 + 4 + 5; + + // CHECK-MESSAGES: :[[@LINE+2]]:13: warning: add parantheses to clarify the precedence of operations [readability-math-missing-parentheses] + // CHECK-FIXES: int h = (120 & (2 + 3)) | (22 * 5); + int h = 120 & 2 + 3 | 22 * 5; + + int i = 1 & 2 & 3; // No warning + + int j = 1 | 2 | 3; // No warning + + int k = 1 ^ 2 ^ 3; // No warning + + // CHECK-MESSAGES: :[[@LINE+2]]:13: warning: add parantheses to clarify the precedence of operations [readability-math-missing-parentheses] + // CHECK-FIXES: int l = (1 + 2) ^ 3; + int l = 1 + 2 ^ 3; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits