LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / compilerplugins / clang / unsignedcompare.cxx
blobf231db0858f47e86ee3a78e06215056f9061d391
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 #ifndef LO_CLANG_SHARED_PLUGINS
12 // Find explicit casts from signed to unsigned integer in comparison against unsigned integer, where
13 // the cast is presumably used to avoid warnings about signed vs. unsigned comparisons, and could
14 // thus be replaced with o3tl::make_unsigned for clarity.
16 #include <cassert>
18 #include "config_clang.h"
20 #include "compat.hxx"
21 #include "plugin.hxx"
23 namespace
25 // clang::Type::isSignedIntegerType returns true for more types than what C++ defines as signed
26 // integer types:
27 bool isSignedIntegerType(QualType type)
29 if (auto const t = type->getAs<BuiltinType>())
31 // Assumes that the only extended signed integer type supported by Clang is Int128:
32 switch (t->getKind())
34 case BuiltinType::SChar:
35 case BuiltinType::Short:
36 case BuiltinType::Int:
37 case BuiltinType::Long:
38 case BuiltinType::LongLong:
39 case BuiltinType::Int128:
40 return true;
41 default:
42 break;
45 return false;
48 // clang::Type::isUnsignedIntegerType returns true for more types than what C++ defines as signed
49 // integer types:
50 bool isUnsignedIntegerType(QualType type)
52 if (auto const t = type->getAs<BuiltinType>())
54 // Assumes that the only extended unsigned integer type supported by Clang is UInt128:
55 switch (t->getKind())
57 case BuiltinType::UChar:
58 case BuiltinType::UShort:
59 case BuiltinType::UInt:
60 case BuiltinType::ULong:
61 case BuiltinType::ULongLong:
62 case BuiltinType::UInt128:
63 return true;
64 default:
65 break;
68 return false;
71 int getRank(QualType type)
73 auto const t = type->getAs<BuiltinType>();
74 assert(t != nullptr);
75 // Assumes that the only extended signed/unsigned integer types supported by Clang are Int128
76 // and UInt128:
77 switch (t->getKind())
79 case BuiltinType::SChar:
80 case BuiltinType::UChar:
81 return 0;
82 case BuiltinType::Short:
83 case BuiltinType::UShort:
84 return 1;
85 case BuiltinType::Int:
86 case BuiltinType::UInt:
87 return 2;
88 case BuiltinType::Long:
89 case BuiltinType::ULong:
90 return 3;
91 case BuiltinType::LongLong:
92 case BuiltinType::ULongLong:
93 return 4;
94 case BuiltinType::Int128:
95 case BuiltinType::UInt128:
96 return 5;
97 default:
98 llvm_unreachable("bad integer type");
102 int orderTypes(QualType type1, QualType type2)
104 auto const r1 = getRank(type1);
105 auto const r2 = getRank(type2);
106 return r1 < r2 ? -1 : r1 == r2 ? 0 : 1;
109 class UnsignedCompare : public loplugin::FilteringPlugin<UnsignedCompare>
111 public:
112 explicit UnsignedCompare(loplugin::InstantiationData const& data)
113 : FilteringPlugin(data)
117 bool VisitBinaryOperator(BinaryOperator const* expr)
119 if (ignoreLocation(expr))
121 return true;
123 // o3tl::make_unsigned requires its argument to be non-negative, but this plugin doesn't
124 // check that when it reports its finding, so will produce false positives when the cast is
125 // actually meant to e.g. clamp from a large signed type to a small unsigned type. The
126 // assumption is that this will only be likely the case for BO_EQ (==) and BO_NE (!=)
127 // comparisons, so filter these out here (not sure what case BO_Cmp (<=>) will turn out to
128 // be, so lets keep it here at least for now):
129 switch (expr->getOpcode())
131 #if CLANG_VERSION >= 60000
132 case BO_Cmp:
133 #endif
134 case BO_LT:
135 case BO_GT:
136 case BO_LE:
137 case BO_GE:
138 break;
139 default:
140 return true;
142 auto const castL = isCastToUnsigned(expr->getLHS());
143 auto const castR = isCastToUnsigned(expr->getRHS());
144 //TODO(?): Also report somewhat suspicious cases where both sides are cast to unsigned:
145 if ((castL == nullptr) == (castR == nullptr))
147 return true;
149 auto const cast = castL != nullptr ? castL : castR;
150 auto const other = castL != nullptr ? expr->getRHS() : expr->getLHS();
151 auto const otherT = other->IgnoreImpCasts()->getType();
152 if (!isUnsignedIntegerType(otherT))
154 return true;
156 auto const castFromT = cast->getSubExprAsWritten()->getType();
157 auto const castToT = cast->getTypeAsWritten();
158 report(DiagnosticsEngine::Warning,
159 "explicit cast from %0 to %1 (of %select{smaller|equal|larger}2 rank) in comparison "
160 "against %3: if the cast value is known to be non-negative, use o3tl::make_unsigned "
161 "instead of the cast",
162 cast->getExprLoc())
163 << castFromT << castToT << (orderTypes(castToT, castFromT) + 1) << otherT
164 << expr->getSourceRange();
165 return true;
168 bool preRun() override
170 return compiler.getLangOpts().CPlusPlus
171 && compiler.getPreprocessor()
172 .getIdentifierInfo("LIBO_INTERNAL_ONLY")
173 ->hasMacroDefinition();
176 void run() override
178 if (preRun())
180 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
184 private:
185 ExplicitCastExpr const* isCastToUnsigned(Expr const* expr)
187 auto const e = dyn_cast<ExplicitCastExpr>(expr->IgnoreParenImpCasts());
188 if (e == nullptr)
190 return nullptr;
192 auto const t1 = e->getTypeAsWritten();
193 if (!isUnsignedIntegerType(t1))
195 return nullptr;
197 auto const e2 = e->getSubExprAsWritten();
198 auto const t2 = e2->getType();
199 if (!isSignedIntegerType(t2))
201 return nullptr;
203 // Filter out e.g. `size_t(-1)`:
204 if (!e2->isValueDependent())
206 if (auto const val = compat::getIntegerConstantExpr(e2, compiler.getASTContext()))
208 if (val->isNegative())
210 return nullptr;
214 auto loc = compat::getBeginLoc(e);
215 while (compiler.getSourceManager().isMacroArgExpansion(loc))
217 loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
219 // This covers both "plain" code in such include files, as well as expansion of (object-like) macros like
221 // #define SAL_MAX_INT8 ((sal_Int8) 0x7F)
223 // defined in such include files:
224 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(loc)))
225 { //TODO: '#ifdef LIBO_INTERNAL_ONLY' within UNO include files
226 return nullptr;
228 return e;
232 loplugin::Plugin::Registration<UnsignedCompare> unsignedcompare("unsignedcompare");
235 #endif // LO_CLANG_SHARED_PLUGINS
237 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */