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.
22 // clang::Type::isSignedIntegerType returns true for more types than what C++ defines as signed
24 bool isSignedIntegerType(QualType type
)
26 if (auto const t
= type
->getAs
<BuiltinType
>())
28 // Assumes that the only extended signed integer type supported by Clang is Int128:
31 case BuiltinType::SChar
:
32 case BuiltinType::Short
:
33 case BuiltinType::Int
:
34 case BuiltinType::Long
:
35 case BuiltinType::LongLong
:
36 case BuiltinType::Int128
:
45 // clang::Type::isUnsignedIntegerType returns true for more types than what C++ defines as signed
47 bool isUnsignedIntegerType(QualType type
)
49 if (auto const t
= type
->getAs
<BuiltinType
>())
51 // Assumes that the only extended unsigned integer type supported by Clang is UInt128:
54 case BuiltinType::UChar
:
55 case BuiltinType::UShort
:
56 case BuiltinType::UInt
:
57 case BuiltinType::ULong
:
58 case BuiltinType::ULongLong
:
59 case BuiltinType::UInt128
:
68 int getRank(QualType type
)
70 auto const t
= type
->getAs
<BuiltinType
>();
72 // Assumes that the only extended signed/unsigned integer types supported by Clang are Int128
76 case BuiltinType::SChar
:
77 case BuiltinType::UChar
:
79 case BuiltinType::Short
:
80 case BuiltinType::UShort
:
82 case BuiltinType::Int
:
83 case BuiltinType::UInt
:
85 case BuiltinType::Long
:
86 case BuiltinType::ULong
:
88 case BuiltinType::LongLong
:
89 case BuiltinType::ULongLong
:
91 case BuiltinType::Int128
:
92 case BuiltinType::UInt128
:
95 llvm_unreachable("bad integer type");
99 int orderTypes(QualType type1
, QualType type2
)
101 auto const r1
= getRank(type1
);
102 auto const r2
= getRank(type2
);
103 return r1
< r2
? -1 : r1
== r2
? 0 : 1;
106 class UnsignedCompare
: public loplugin::FilteringPlugin
<UnsignedCompare
>
109 explicit UnsignedCompare(loplugin::InstantiationData
const& data
)
110 : FilteringPlugin(data
)
114 bool VisitBinaryOperator(BinaryOperator
const* expr
)
116 if (ignoreLocation(expr
))
120 // o3tl::make_unsigned requires its argument to be non-negative, but this plugin doesn't
121 // check that when it reports its finding, so will produce false positives when the cast is
122 // actually meant to e.g. clamp from a large signed type to a small unsigned type. The
123 // assumption is that this will only be likely the case for BO_EQ (==) and BO_NE (!=)
124 // comparisons, so filter these out here (not sure what case BO_Cmp (<=>) will turn out to
125 // be, so lets keep it here at least for now):
126 switch (expr
->getOpcode())
137 auto const castL
= isCastToUnsigned(expr
->getLHS());
138 auto const castR
= isCastToUnsigned(expr
->getRHS());
139 //TODO(?): Also report somewhat suspicious cases where both sides are cast to unsigned:
140 if ((castL
== nullptr) == (castR
== nullptr))
144 auto const cast
= castL
!= nullptr ? castL
: castR
;
145 auto const other
= castL
!= nullptr ? expr
->getRHS() : expr
->getLHS();
146 auto const otherT
= other
->IgnoreImpCasts()->getType();
147 if (!isUnsignedIntegerType(otherT
))
151 auto const castFromT
= cast
->getSubExprAsWritten()->getType();
152 auto const castToT
= cast
->getTypeAsWritten();
153 report(DiagnosticsEngine::Warning
,
154 "explicit cast from %0 to %1 (of %select{smaller|equal|larger}2 rank) in comparison "
155 "against %3: if the cast value is known to be non-negative, use o3tl::make_unsigned "
156 "instead of the cast",
158 << castFromT
<< castToT
<< (orderTypes(castToT
, castFromT
) + 1) << otherT
159 << expr
->getSourceRange();
163 bool preRun() override
165 return compiler
.getLangOpts().CPlusPlus
166 && compiler
.getPreprocessor()
167 .getIdentifierInfo("LIBO_INTERNAL_ONLY")
168 ->hasMacroDefinition();
175 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
180 ExplicitCastExpr
const* isCastToUnsigned(Expr
const* expr
)
182 auto const e
= dyn_cast
<ExplicitCastExpr
>(expr
->IgnoreParenImpCasts());
187 auto const t1
= e
->getTypeAsWritten();
188 if (!isUnsignedIntegerType(t1
))
192 auto const e2
= e
->getSubExprAsWritten();
193 auto const t2
= e2
->getType();
194 if (!isSignedIntegerType(t2
))
198 // Filter out e.g. `size_t(-1)`:
199 if (!e2
->isValueDependent())
201 if (auto const val
= e2
->getIntegerConstantExpr(compiler
.getASTContext()))
203 if (val
->isNegative())
209 auto loc
= e
->getBeginLoc();
210 while (compiler
.getSourceManager().isMacroArgExpansion(loc
))
212 loc
= compiler
.getSourceManager().getImmediateMacroCallerLoc(loc
);
214 // This covers both "plain" code in such include files, as well as expansion of (object-like) macros like
216 // #define SAL_MAX_INT8 ((sal_Int8) 0x7F)
218 // defined in such include files:
219 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(loc
)))
220 { //TODO: '#ifdef LIBO_INTERNAL_ONLY' within UNO include files
227 loplugin::Plugin::Registration
<UnsignedCompare
> unsignedcompare("unsignedcompare");
230 #endif // LO_CLANG_SHARED_PLUGINS
232 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */