brintos

brintos / llvm-project-archived public Read only

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