1 //===--- NarrowingConversionsCheck.cpp - clang-tidy------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "NarrowingConversionsCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Type.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "llvm/ADT/APSInt.h"
14 #include "llvm/ADT/SmallString.h"
15 #include "llvm/ADT/SmallVector.h"
16 
17 #include <cstdint>
18 
19 using namespace clang::ast_matchers;
20 
21 namespace clang {
22 namespace tidy {
23 namespace cppcoreguidelines {
24 
25 NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef Name,
26                                                      ClangTidyContext *Context)
27     : ClangTidyCheck(Name, Context),
28       WarnOnFloatingPointNarrowingConversion(
29           Options.get("WarnOnFloatingPointNarrowingConversion", true)),
30       PedanticMode(Options.get("PedanticMode", false)) {}
31 
32 void NarrowingConversionsCheck::registerMatchers(MatchFinder *Finder) {
33   // ceil() and floor() are guaranteed to return integers, even though the type
34   // is not integral.
35   const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
36       hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor")))));
37 
38   // Casts:
39   //   i = 0.5;
40   //   void f(int); f(0.5);
41   Finder->addMatcher(
42       traverse(
43           ast_type_traits::TK_AsIs,
44           implicitCastExpr(hasImplicitDestinationType(builtinType()),
45                            hasSourceExpression(hasType(builtinType())),
46                            unless(hasSourceExpression(IsCeilFloorCallExpr)),
47                            unless(hasParent(castExpr())),
48                            unless(isInTemplateInstantiation()))
49               .bind("cast")),
50       this);
51 
52   // Binary operators:
53   //   i += 0.5;
54   Finder->addMatcher(binaryOperator(isAssignmentOperator(),
55                                     hasLHS(expr(hasType(builtinType()))),
56                                     hasRHS(expr(hasType(builtinType()))),
57                                     unless(hasRHS(IsCeilFloorCallExpr)),
58                                     unless(isInTemplateInstantiation()),
59                                     // The `=` case generates an implicit cast
60                                     // which is covered by the previous matcher.
61                                     unless(hasOperatorName("=")))
62                          .bind("binary_op"),
63                      this);
64 }
65 
66 static const BuiltinType *getBuiltinType(const Expr &E) {
67   return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
68 }
69 
70 static QualType getUnqualifiedType(const Expr &E) {
71   return E.getType().getUnqualifiedType();
72 }
73 
74 static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) {
75   llvm::APSInt IntegerConstant;
76   if (E.isIntegerConstantExpr(IntegerConstant, Ctx))
77     return APValue(IntegerConstant);
78   APValue Constant;
79   if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant))
80     return Constant;
81   return {};
82 }
83 
84 static bool getIntegerConstantExprValue(const ASTContext &Context,
85                                         const Expr &E, llvm::APSInt &Value) {
86   APValue Constant = getConstantExprValue(Context, E);
87   if (!Constant.isInt())
88     return false;
89   Value = Constant.getInt();
90   return true;
91 }
92 
93 static bool getFloatingConstantExprValue(const ASTContext &Context,
94                                          const Expr &E, llvm::APFloat &Value) {
95   APValue Constant = getConstantExprValue(Context, E);
96   if (!Constant.isFloat())
97     return false;
98   Value = Constant.getFloat();
99   return true;
100 }
101 
102 namespace {
103 
104 struct IntegerRange {
105   bool Contains(const IntegerRange &From) const {
106     return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 &&
107            llvm::APSInt::compareValues(Upper, From.Upper) >= 0;
108   }
109 
110   bool Contains(const llvm::APSInt &Value) const {
111     return llvm::APSInt::compareValues(Lower, Value) <= 0 &&
112            llvm::APSInt::compareValues(Upper, Value) >= 0;
113   }
114 
115   llvm::APSInt Lower;
116   llvm::APSInt Upper;
117 };
118 
119 } // namespace
120 
121 static IntegerRange createFromType(const ASTContext &Context,
122                                    const BuiltinType &T) {
123   if (T.isFloatingPoint()) {
124     unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
125         Context.getFloatTypeSemantics(T.desugar()));
126     // Contrary to two's complement integer, floating point values are
127     // symmetric and have the same number of positive and negative values.
128     // The range of valid integers for a floating point value is:
129     // [-2^PrecisionBits, 2^PrecisionBits]
130 
131     // Values are created with PrecisionBits plus two bits:
132     // - One to express the missing negative value of 2's complement
133     //   representation.
134     // - One for the sign.
135     llvm::APSInt UpperValue(PrecisionBits + 2, /*isUnsigned*/ false);
136     UpperValue.setBit(PrecisionBits);
137     llvm::APSInt LowerValue(PrecisionBits + 2, /*isUnsigned*/ false);
138     LowerValue.setBit(PrecisionBits);
139     LowerValue.setSignBit();
140     return {LowerValue, UpperValue};
141   }
142   assert(T.isInteger() && "Unexpected builtin type");
143   uint64_t TypeSize = Context.getTypeSize(&T);
144   bool IsUnsignedInteger = T.isUnsignedInteger();
145   return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
146           llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
147 }
148 
149 static bool isWideEnoughToHold(const ASTContext &Context,
150                                const BuiltinType &FromType,
151                                const BuiltinType &ToType) {
152   IntegerRange FromIntegerRange = createFromType(Context, FromType);
153   IntegerRange ToIntegerRange = createFromType(Context, ToType);
154   return ToIntegerRange.Contains(FromIntegerRange);
155 }
156 
157 static bool isWideEnoughToHold(const ASTContext &Context,
158                                const llvm::APSInt &IntegerConstant,
159                                const BuiltinType &ToType) {
160   IntegerRange ToIntegerRange = createFromType(Context, ToType);
161   return ToIntegerRange.Contains(IntegerConstant);
162 }
163 
164 static llvm::SmallString<64> getValueAsString(const llvm::APSInt &Value,
165                                               uint64_t HexBits) {
166   llvm::SmallString<64> Str;
167   Value.toString(Str, 10);
168   if (HexBits > 0) {
169     Str.append(" (0x");
170     llvm::SmallString<32> HexValue;
171     Value.toStringUnsigned(HexValue, 16);
172     for (size_t I = HexValue.size(); I < (HexBits / 4); ++I)
173       Str.append("0");
174     Str.append(HexValue);
175     Str.append(")");
176   }
177   return Str;
178 }
179 
180 void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
181                                                const Expr &Lhs,
182                                                const Expr &Rhs) {
183   diag(SourceLoc, "narrowing conversion from %0 to %1")
184       << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
185 }
186 
187 void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
188     SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) {
189   diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is "
190                   "implementation-defined")
191       << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
192 }
193 
194 void NarrowingConversionsCheck::diagNarrowIntegerConstant(
195     SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
196     const llvm::APSInt &Value) {
197   diag(SourceLoc,
198        "narrowing conversion from constant value %0 of type %1 to %2")
199       << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs)
200       << getUnqualifiedType(Lhs);
201 }
202 
203 void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
204     SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
205     const llvm::APSInt &Value, const uint64_t HexBits) {
206   diag(SourceLoc, "narrowing conversion from constant value %0 of type %1 "
207                   "to signed type %2 is implementation-defined")
208       << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs)
209       << getUnqualifiedType(Lhs);
210 }
211 
212 void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
213                                                    const Expr &Lhs,
214                                                    const Expr &Rhs) {
215   diag(SourceLoc, "narrowing conversion from constant %0 to %1")
216       << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
217 }
218 
219 void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
220                                                  const Expr &Lhs,
221                                                  const Expr &Rhs) {
222   diag(SourceLoc, "constant value should be of type of type %0 instead of %1")
223       << getUnqualifiedType(Lhs) << getUnqualifiedType(Rhs);
224 }
225 
226 void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
227     const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
228     const Expr &Rhs) {
229   APValue Constant = getConstantExprValue(Context, Rhs);
230   if (Constant.isInt())
231     return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt());
232   if (Constant.isFloat())
233     return diagNarrowConstant(SourceLoc, Lhs, Rhs);
234   return diagNarrowType(SourceLoc, Lhs, Rhs);
235 }
236 
237 void NarrowingConversionsCheck::handleIntegralCast(const ASTContext &Context,
238                                                    SourceLocation SourceLoc,
239                                                    const Expr &Lhs,
240                                                    const Expr &Rhs) {
241   const BuiltinType *ToType = getBuiltinType(Lhs);
242   // From [conv.integral]p7.3.8:
243   // Conversions to unsigned integer is well defined so no warning is issued.
244   // "The resulting value is the smallest unsigned value equal to the source
245   // value modulo 2^n where n is the number of bits used to represent the
246   // destination type."
247   if (ToType->isUnsignedInteger())
248     return;
249   const BuiltinType *FromType = getBuiltinType(Rhs);
250   llvm::APSInt IntegerConstant;
251   if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
252     if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
253       diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs, IntegerConstant,
254                                            Context.getTypeSize(FromType));
255     return;
256   }
257   if (!isWideEnoughToHold(Context, *FromType, *ToType))
258     diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
259 }
260 
261 void NarrowingConversionsCheck::handleIntegralToBoolean(
262     const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
263     const Expr &Rhs) {
264   // Conversion from Integral to Bool value is well defined.
265 
266   // We keep this function (even if it is empty) to make sure that
267   // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
268   // and handle the same cases.
269 }
270 
271 void NarrowingConversionsCheck::handleIntegralToFloating(
272     const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
273     const Expr &Rhs) {
274   const BuiltinType *ToType = getBuiltinType(Lhs);
275   llvm::APSInt IntegerConstant;
276   if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
277     if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
278       diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
279     return;
280   }
281   const BuiltinType *FromType = getBuiltinType(Rhs);
282   if (!isWideEnoughToHold(Context, *FromType, *ToType))
283     diagNarrowType(SourceLoc, Lhs, Rhs);
284 }
285 
286 void NarrowingConversionsCheck::handleFloatingToIntegral(
287     const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
288     const Expr &Rhs) {
289   llvm::APFloat FloatConstant(0.0);
290 
291   // We always warn when Rhs is non-constexpr.
292   if (!getFloatingConstantExprValue(Context, Rhs, FloatConstant))
293     return diagNarrowType(SourceLoc, Lhs, Rhs);
294 
295   QualType DestType = Lhs.getType();
296   unsigned DestWidth = Context.getIntWidth(DestType);
297   bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
298   llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
299   bool IsExact = false;
300   bool Overflows = FloatConstant.convertToInteger(
301                        Result, llvm::APFloat::rmTowardZero, &IsExact) &
302                    llvm::APFloat::opInvalidOp;
303   // We warn iff the constant floating point value is not exactly representable.
304   if (Overflows || !IsExact)
305     return diagNarrowConstant(SourceLoc, Lhs, Rhs);
306 
307   if (PedanticMode)
308     return diagConstantCast(SourceLoc, Lhs, Rhs);
309 }
310 
311 void NarrowingConversionsCheck::handleFloatingToBoolean(
312     const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
313     const Expr &Rhs) {
314   return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
315 }
316 
317 void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
318     const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
319     const Expr &Rhs) {
320   // Conversion from Bool to SignedIntegral value is well defined.
321 
322   // We keep this function (even if it is empty) to make sure that
323   // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
324   // and handle the same cases.
325 }
326 
327 void NarrowingConversionsCheck::handleFloatingCast(const ASTContext &Context,
328                                                    SourceLocation SourceLoc,
329                                                    const Expr &Lhs,
330                                                    const Expr &Rhs) {
331   if (WarnOnFloatingPointNarrowingConversion) {
332     const BuiltinType *ToType = getBuiltinType(Lhs);
333     APValue Constant = getConstantExprValue(Context, Rhs);
334     if (Constant.isFloat()) {
335       // From [dcl.init.list]p7.2:
336       // Floating point constant narrowing only takes place when the value is
337       // not within destination range. We convert the value to the destination
338       // type and check if the resulting value is infinity.
339       llvm::APFloat Tmp = Constant.getFloat();
340       bool UnusedLosesInfo;
341       Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
342                   llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
343       if (Tmp.isInfinity())
344         diagNarrowConstant(SourceLoc, Lhs, Rhs);
345       return;
346     }
347     const BuiltinType *FromType = getBuiltinType(Rhs);
348     if (ToType->getKind() < FromType->getKind())
349       diagNarrowType(SourceLoc, Lhs, Rhs);
350   }
351 }
352 
353 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
354                                                      SourceLocation SourceLoc,
355                                                      const Expr &Lhs,
356                                                      const Expr &Rhs) {
357   assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
358          "Dependent types must be check before calling this function");
359   const BuiltinType *LhsType = getBuiltinType(Lhs);
360   const BuiltinType *RhsType = getBuiltinType(Rhs);
361   if (RhsType == nullptr || LhsType == nullptr)
362     return;
363   if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
364     return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
365   if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
366     return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
367   if (RhsType->isInteger() && LhsType->isFloatingPoint())
368     return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
369   if (RhsType->isInteger() && LhsType->isInteger())
370     return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
371   if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool)
372     return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
373   if (RhsType->isFloatingPoint() && LhsType->isInteger())
374     return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
375   if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
376     return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
377 }
378 
379 bool NarrowingConversionsCheck::handleConditionalOperator(
380     const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) {
381   if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
382     // We have an expression like so: `output = cond ? lhs : rhs`
383     // From the point of view of narrowing conversion we treat it as two
384     // expressions `output = lhs` and `output = rhs`.
385     handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
386                          *CO->getLHS());
387     handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
388                          *CO->getRHS());
389     return true;
390   }
391   return false;
392 }
393 
394 void NarrowingConversionsCheck::handleImplicitCast(
395     const ASTContext &Context, const ImplicitCastExpr &Cast) {
396   if (Cast.getExprLoc().isMacroID())
397     return;
398   const Expr &Lhs = Cast;
399   const Expr &Rhs = *Cast.getSubExpr();
400   if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
401     return;
402   if (handleConditionalOperator(Context, Lhs, Rhs))
403     return;
404   SourceLocation SourceLoc = Lhs.getExprLoc();
405   switch (Cast.getCastKind()) {
406   case CK_BooleanToSignedIntegral:
407     return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
408   case CK_IntegralToBoolean:
409     return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
410   case CK_IntegralToFloating:
411     return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
412   case CK_IntegralCast:
413     return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
414   case CK_FloatingToBoolean:
415     return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
416   case CK_FloatingToIntegral:
417     return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
418   case CK_FloatingCast:
419     return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
420   default:
421     break;
422   }
423 }
424 
425 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
426                                                      const BinaryOperator &Op) {
427   if (Op.getBeginLoc().isMacroID())
428     return;
429   const Expr &Lhs = *Op.getLHS();
430   const Expr &Rhs = *Op.getRHS();
431   if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
432     return;
433   if (handleConditionalOperator(Context, Lhs, Rhs))
434     return;
435   handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
436 }
437 
438 void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {
439   if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op"))
440     return handleBinaryOperator(*Result.Context, *Op);
441   if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast"))
442     return handleImplicitCast(*Result.Context, *Cast);
443   llvm_unreachable("must be binary operator or cast expression");
444 }
445 
446 } // namespace cppcoreguidelines
447 } // namespace tidy
448 } // namespace clang
449