bump product version to 7.2.5.1
[LibreOffice.git] / compilerplugins / clang / comparisonwithconstant.cxx
blobb5dfe8cc6e7ffa9e7a522e425b6d24a823ae0c44
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <cassert>
11 #include <string>
12 #include <iostream>
13 #include <fstream>
14 #include <set>
16 #include "config_clang.h"
18 #include "compat.hxx"
19 #include "plugin.hxx"
21 /**
22 Look for comparisons where the constant is on the left, it should be on the right.
25 namespace {
27 class ComparisonWithConstant :
28 public loplugin::FilteringRewritePlugin<ComparisonWithConstant>
30 public:
31 explicit ComparisonWithConstant(loplugin::InstantiationData const & data): FilteringRewritePlugin(data) {}
33 virtual void run() override
35 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
38 // Deliberately drop RecursiveASTVisitor::TraverseBinaryOperator's DataRecursionQueue
39 // parameter; TraverseBinaryOperator must use stack instead of data recursion for any
40 // children's VisitBinaryOperator to see changes to occurrence_ by a parent
41 // VisitBinaryOperator:
42 bool TraverseBinaryOperator(BinaryOperator * S)
44 auto const op = S->getOpcode();
45 if (op != BO_EQ && op != BO_NE) {
46 return RecursiveASTVisitor::TraverseBinaryOperator(S);
48 auto const saved = occurrence_;
49 auto const ret = RecursiveASTVisitor::TraverseBinaryOperator(S);
50 occurrence_ = saved;
51 return ret;
54 #if CLANG_VERSION < 110000
55 bool TraverseBinEQ(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
56 bool TraverseBinNE(BinaryOperator * expr) { return TraverseBinaryOperator(expr); }
57 #endif
59 bool VisitBinaryOperator(const BinaryOperator *);
60 private:
61 bool rewrite(const BinaryOperator *);
62 std::string getExprAsString(SourceRange range);
63 SourceRange ignoreMacroExpansions(SourceRange range);
65 bool occurrence_ = false;
68 bool ComparisonWithConstant::VisitBinaryOperator(const BinaryOperator* binaryOp)
70 if (ignoreLocation(binaryOp)) {
71 return true;
73 if (!(binaryOp->getOpcode() == BO_EQ || binaryOp->getOpcode() == BO_NE)) {
74 return true;
76 // protect against clang assert
77 if (binaryOp->getLHS()->isValueDependent() || binaryOp->getRHS()->isValueDependent()) {
78 return true;
80 if (!binaryOp->getLHS()->isEvaluatable(compiler.getASTContext())) {
81 return true;
83 if (binaryOp->getRHS()->isEvaluatable(compiler.getASTContext())) {
84 return true;
86 if (occurrence_ || !rewrite(binaryOp))
88 report(
89 DiagnosticsEngine::Warning, "Rather put constant on right when comparing",
90 binaryOp->getSourceRange().getBegin())
91 << binaryOp->getSourceRange();
93 occurrence_ = true;
94 return true;
98 bool ComparisonWithConstant::rewrite(const BinaryOperator * binaryOp) {
99 if (rewriter == nullptr) {
100 return false;
103 auto lhsRange = ignoreMacroExpansions(binaryOp->getLHS()->getSourceRange());
104 if (!lhsRange.isValid()) {
105 return false;
107 auto rhsRange = ignoreMacroExpansions(binaryOp->getRHS()->getSourceRange());
108 if (!rhsRange.isValid()) {
109 return false;
112 const std::string lhsString = getExprAsString(lhsRange);
113 const std::string rhsString = getExprAsString(rhsRange);
115 // switch LHS and RHS
116 if (!replaceText(lhsRange, rhsString)) {
117 return false;
119 if (!replaceText(rhsRange, lhsString)) {
120 return false;
123 return true;
126 // get the expression contents
127 std::string ComparisonWithConstant::getExprAsString(SourceRange range)
129 SourceManager& SM = compiler.getSourceManager();
130 SourceLocation startLoc = range.getBegin();
131 SourceLocation endLoc = range.getEnd();
132 const char *p1 = SM.getCharacterData( startLoc );
133 const char *p2 = SM.getCharacterData( endLoc );
134 unsigned n = Lexer::MeasureTokenLength( endLoc, SM, compiler.getLangOpts());
135 return std::string( p1, p2 - p1 + n);
138 SourceRange ComparisonWithConstant::ignoreMacroExpansions(SourceRange range) {
139 while (compiler.getSourceManager().isMacroArgExpansion(range.getBegin())) {
140 range.setBegin(
141 compiler.getSourceManager().getImmediateMacroCallerLoc(
142 range.getBegin()));
144 if (range.getBegin().isMacroID()) {
145 SourceLocation loc;
146 if (Lexer::isAtStartOfMacroExpansion(
147 range.getBegin(), compiler.getSourceManager(),
148 compiler.getLangOpts(), &loc))
150 range.setBegin(loc);
153 while (compiler.getSourceManager().isMacroArgExpansion(range.getEnd())) {
154 range.setEnd(
155 compiler.getSourceManager().getImmediateMacroCallerLoc(
156 range.getEnd()));
158 if (range.getEnd().isMacroID()) {
159 SourceLocation loc;
160 if (Lexer::isAtEndOfMacroExpansion(
161 range.getEnd(), compiler.getSourceManager(),
162 compiler.getLangOpts(), &loc))
164 range.setEnd(loc);
167 return range.getBegin().isMacroID() || range.getEnd().isMacroID()
168 ? SourceRange() : range;
171 loplugin::Plugin::Registration< ComparisonWithConstant > X("comparisonwithconstant", false);
175 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */