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