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