1 //===--- FoldInitTypeCheck.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 "FoldInitTypeCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang { 17 namespace tidy { 18 namespace bugprone { 19 20 void FoldInitTypeCheck::registerMatchers(MatchFinder *Finder) { 21 // We match functions of interest and bind the iterator and init value types. 22 // Note: Right now we check only builtin types. 23 const auto BuiltinTypeWithId = [](const char *ID) { 24 return hasCanonicalType(builtinType().bind(ID)); 25 }; 26 const auto IteratorWithValueType = [&BuiltinTypeWithId](const char *ID) { 27 return anyOf( 28 // Pointer types. 29 pointsTo(BuiltinTypeWithId(ID)), 30 // Iterator types. 31 recordType(hasDeclaration(has(typedefNameDecl( 32 hasName("value_type"), hasType(BuiltinTypeWithId(ID))))))); 33 }; 34 35 const auto IteratorParam = parmVarDecl( 36 hasType(hasCanonicalType(IteratorWithValueType("IterValueType")))); 37 const auto Iterator2Param = parmVarDecl( 38 hasType(hasCanonicalType(IteratorWithValueType("Iter2ValueType")))); 39 const auto InitParam = parmVarDecl(hasType(BuiltinTypeWithId("InitType"))); 40 41 // std::accumulate, std::reduce. 42 Finder->addMatcher( 43 callExpr(callee(functionDecl( 44 hasAnyName("::std::accumulate", "::std::reduce"), 45 hasParameter(0, IteratorParam), hasParameter(2, InitParam))), 46 argumentCountIs(3)) 47 .bind("Call"), 48 this); 49 // std::inner_product. 50 Finder->addMatcher( 51 callExpr(callee(functionDecl(hasName("::std::inner_product"), 52 hasParameter(0, IteratorParam), 53 hasParameter(2, Iterator2Param), 54 hasParameter(3, InitParam))), 55 argumentCountIs(4)) 56 .bind("Call"), 57 this); 58 // std::reduce with a policy. 59 Finder->addMatcher( 60 callExpr(callee(functionDecl(hasName("::std::reduce"), 61 hasParameter(1, IteratorParam), 62 hasParameter(3, InitParam))), 63 argumentCountIs(4)) 64 .bind("Call"), 65 this); 66 // std::inner_product with a policy. 67 Finder->addMatcher( 68 callExpr(callee(functionDecl(hasName("::std::inner_product"), 69 hasParameter(1, IteratorParam), 70 hasParameter(3, Iterator2Param), 71 hasParameter(4, InitParam))), 72 argumentCountIs(5)) 73 .bind("Call"), 74 this); 75 } 76 77 /// Returns true if ValueType is allowed to fold into InitType, i.e. if: 78 /// static_cast<InitType>(ValueType{some_value}) 79 /// does not result in trucation. 80 static bool isValidBuiltinFold(const BuiltinType &ValueType, 81 const BuiltinType &InitType, 82 const ASTContext &Context) { 83 const auto ValueTypeSize = Context.getTypeSize(&ValueType); 84 const auto InitTypeSize = Context.getTypeSize(&InitType); 85 // It's OK to fold a float into a float of bigger or equal size, but not OK to 86 // fold into an int. 87 if (ValueType.isFloatingPoint()) 88 return InitType.isFloatingPoint() && InitTypeSize >= ValueTypeSize; 89 // It's OK to fold an int into: 90 // - an int of the same size and signedness. 91 // - a bigger int, regardless of signedness. 92 // - FIXME: should it be a warning to fold into floating point? 93 if (ValueType.isInteger()) { 94 if (InitType.isInteger()) { 95 if (InitType.isSignedInteger() == ValueType.isSignedInteger()) 96 return InitTypeSize >= ValueTypeSize; 97 return InitTypeSize > ValueTypeSize; 98 } 99 if (InitType.isFloatingPoint()) 100 return InitTypeSize >= ValueTypeSize; 101 } 102 return false; 103 } 104 105 /// Prints a diagnostic if IterValueType doe snot fold into IterValueType (see 106 // isValidBuiltinFold for details). 107 void FoldInitTypeCheck::doCheck(const BuiltinType &IterValueType, 108 const BuiltinType &InitType, 109 const ASTContext &Context, 110 const CallExpr &CallNode) { 111 if (!isValidBuiltinFold(IterValueType, InitType, Context)) { 112 diag(CallNode.getExprLoc(), "folding type %0 into type %1 might result in " 113 "loss of precision") 114 << IterValueType.desugar() << InitType.desugar(); 115 } 116 } 117 118 void FoldInitTypeCheck::check(const MatchFinder::MatchResult &Result) { 119 // Given the iterator and init value type retreived by the matchers, 120 // we check that the ::value_type of the iterator is compatible with 121 // the init value type. 122 const auto *InitType = Result.Nodes.getNodeAs<BuiltinType>("InitType"); 123 const auto *IterValueType = 124 Result.Nodes.getNodeAs<BuiltinType>("IterValueType"); 125 assert(InitType != nullptr); 126 assert(IterValueType != nullptr); 127 128 const auto *CallNode = Result.Nodes.getNodeAs<CallExpr>("Call"); 129 assert(CallNode != nullptr); 130 131 doCheck(*IterValueType, *InitType, *Result.Context, *CallNode); 132 133 if (const auto *Iter2ValueType = 134 Result.Nodes.getNodeAs<BuiltinType>("Iter2ValueType")) 135 doCheck(*Iter2ValueType, *InitType, *Result.Context, *CallNode); 136 } 137 138 } // namespace bugprone 139 } // namespace tidy 140 } // namespace clang 141