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