1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
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.
18 #include "config_clang.h"
25 // clang::Type::isSignedIntegerType returns true for more types than what C++ defines as signed
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:
34 case BuiltinType::SChar
:
35 case BuiltinType::Short
:
36 case BuiltinType::Int
:
37 case BuiltinType::Long
:
38 case BuiltinType::LongLong
:
39 case BuiltinType::Int128
:
48 // clang::Type::isUnsignedIntegerType returns true for more types than what C++ defines as signed
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:
57 case BuiltinType::UChar
:
58 case BuiltinType::UShort
:
59 case BuiltinType::UInt
:
60 case BuiltinType::ULong
:
61 case BuiltinType::ULongLong
:
62 case BuiltinType::UInt128
:
71 int getRank(QualType type
)
73 auto const t
= type
->getAs
<BuiltinType
>();
75 // Assumes that the only extended signed/unsigned integer types supported by Clang are Int128
79 case BuiltinType::SChar
:
80 case BuiltinType::UChar
:
82 case BuiltinType::Short
:
83 case BuiltinType::UShort
:
85 case BuiltinType::Int
:
86 case BuiltinType::UInt
:
88 case BuiltinType::Long
:
89 case BuiltinType::ULong
:
91 case BuiltinType::LongLong
:
92 case BuiltinType::ULongLong
:
94 case BuiltinType::Int128
:
95 case BuiltinType::UInt128
:
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
>
112 explicit UnsignedCompare(loplugin::InstantiationData
const& data
)
113 : FilteringPlugin(data
)
117 bool VisitBinaryOperator(BinaryOperator
const* expr
)
119 if (ignoreLocation(expr
))
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
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))
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
))
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",
163 << castFromT
<< castToT
<< (orderTypes(castToT
, castFromT
) + 1) << otherT
164 << expr
->getSourceRange();
168 bool preRun() override
170 return compiler
.getLangOpts().CPlusPlus
171 && compiler
.getPreprocessor()
172 .getIdentifierInfo("LIBO_INTERNAL_ONLY")
173 ->hasMacroDefinition();
180 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
185 ExplicitCastExpr
const* isCastToUnsigned(Expr
const* expr
)
187 auto const e
= dyn_cast
<ExplicitCastExpr
>(expr
->IgnoreParenImpCasts());
192 auto const t1
= e
->getTypeAsWritten();
193 if (!isUnsignedIntegerType(t1
))
197 auto const e2
= e
->getSubExprAsWritten();
198 auto const t2
= e2
->getType();
199 if (!isSignedIntegerType(t2
))
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())
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
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: */