clang-tools  14.0.0git
ProTypeVarargCheck.cpp
Go to the documentation of this file.
1 //===--- ProTypeVarargCheck.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 "ProTypeVarargCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Basic/TargetInfo.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace cppcoreguidelines {
20 
21 const internal::VariadicDynCastAllOfMatcher<Stmt, VAArgExpr> VAArgExpr;
22 
23 static constexpr StringRef AllowedVariadics[] = {
24  // clang-format off
25  "__builtin_isgreater",
26  "__builtin_isgreaterequal",
27  "__builtin_isless",
28  "__builtin_islessequal",
29  "__builtin_islessgreater",
30  "__builtin_isunordered",
31  "__builtin_fpclassify",
32  "__builtin_isfinite",
33  "__builtin_isinf",
34  "__builtin_isinf_sign",
35  "__builtin_isnan",
36  "__builtin_isnormal",
37  "__builtin_signbit",
38  "__builtin_constant_p",
39  "__builtin_classify_type",
40  "__builtin_va_start",
41  "__builtin_assume_aligned", // Documented as variadic to support default
42  // parameters.
43  "__builtin_prefetch", // Documented as variadic to support default
44  // parameters.
45  "__builtin_shufflevector", // Documented as variadic but with a defined
46  // number of args based on vector size.
47  "__builtin_convertvector",
48  "__builtin_call_with_static_chain",
49  "__builtin_annotation",
50  "__builtin_add_overflow",
51  "__builtin_sub_overflow",
52  "__builtin_mul_overflow",
53  "__builtin_preserve_access_index",
54  "__builtin_nontemporal_store",
55  "__builtin_nontemporal_load",
56  "__builtin_ms_va_start",
57  // clang-format on
58 };
59 
60 namespace {
61 AST_MATCHER(QualType, isVAList) {
62  ASTContext &Context = Finder->getASTContext();
63  QualType Desugar = Node.getDesugaredType(Context);
64  QualType NodeTy = Node.getUnqualifiedType();
65 
66  auto CheckVaList = [](QualType NodeTy, QualType Expected,
67  const ASTContext &Context) {
68  if (NodeTy == Expected)
69  return true;
70  QualType Desugar = NodeTy;
71  QualType Ty;
72  do {
73  Ty = Desugar;
74  Desugar = Ty.getSingleStepDesugaredType(Context);
75  if (Desugar == Expected)
76  return true;
77  } while (Desugar != Ty);
78  return false;
79  };
80 
81  // The internal implementation of __builtin_va_list depends on the target
82  // type. Some targets implements va_list as 'char *' or 'void *'.
83  // In these cases we need to remove all typedefs one by one to check this.
84  using BuiltinVaListKind = TargetInfo::BuiltinVaListKind;
85  BuiltinVaListKind VaListKind = Context.getTargetInfo().getBuiltinVaListKind();
86  if (VaListKind == BuiltinVaListKind::CharPtrBuiltinVaList ||
87  VaListKind == BuiltinVaListKind::VoidPtrBuiltinVaList) {
88  if (CheckVaList(NodeTy, Context.getBuiltinVaListType(), Context))
89  return true;
90  } else if (Desugar ==
91  Context.getBuiltinVaListType().getDesugaredType(Context)) {
92  return true;
93  }
94 
95  // We also need to check the implementation of __builtin_ms_va_list in the
96  // same way, because it may differ from the va_list implementation.
97  if (Desugar == Context.getBuiltinMSVaListType().getDesugaredType(Context) &&
98  CheckVaList(NodeTy, Context.getBuiltinMSVaListType(), Context)) {
99  return true;
100  }
101 
102  return false;
103 }
104 
105 AST_MATCHER_P(AdjustedType, hasOriginalType,
106  ast_matchers::internal::Matcher<QualType>, InnerType) {
107  return InnerType.matches(Node.getOriginalType(), Finder, Builder);
108 }
109 } // namespace
110 
111 void ProTypeVarargCheck::registerMatchers(MatchFinder *Finder) {
112  Finder->addMatcher(VAArgExpr().bind("va_use"), this);
113 
114  Finder->addMatcher(
115  callExpr(callee(functionDecl(isVariadic(),
116  unless(hasAnyName(AllowedVariadics)))))
117  .bind("callvararg"),
118  this);
119 
120  Finder->addMatcher(
121  varDecl(unless(parmVarDecl()),
122  hasType(qualType(
123  anyOf(isVAList(), decayedType(hasOriginalType(isVAList()))))))
124  .bind("va_list"),
125  this);
126 }
127 
128 static bool hasSingleVariadicArgumentWithValue(const CallExpr *C, uint64_t I) {
129  const auto *FDecl = dyn_cast<FunctionDecl>(C->getCalleeDecl());
130  if (!FDecl)
131  return false;
132 
133  auto N = FDecl->getNumParams(); // Number of parameters without '...'
134  if (C->getNumArgs() != N + 1)
135  return false; // more/less than one argument passed to '...'
136 
137  const auto *IntLit =
138  dyn_cast<IntegerLiteral>(C->getArg(N)->IgnoreParenImpCasts());
139  if (!IntLit)
140  return false;
141 
142  if (IntLit->getValue() != I)
143  return false;
144 
145  return true;
146 }
147 
148 void ProTypeVarargCheck::check(const MatchFinder::MatchResult &Result) {
149  if (const auto *Matched = Result.Nodes.getNodeAs<CallExpr>("callvararg")) {
150  if (hasSingleVariadicArgumentWithValue(Matched, 0))
151  return;
152  diag(Matched->getExprLoc(), "do not call c-style vararg functions");
153  }
154 
155  if (const auto *Matched = Result.Nodes.getNodeAs<Expr>("va_use")) {
156  diag(Matched->getExprLoc(),
157  "do not use va_arg to define c-style vararg functions; "
158  "use variadic templates instead");
159  }
160 
161  if (const auto *Matched = Result.Nodes.getNodeAs<VarDecl>("va_list")) {
162  auto SR = Matched->getSourceRange();
163  if (SR.isInvalid())
164  return; // some implicitly generated builtins take va_list
165  diag(SR.getBegin(), "do not declare variables of type va_list; "
166  "use variadic templates instead");
167  }
168 }
169 
170 } // namespace cppcoreguidelines
171 } // namespace tidy
172 } // namespace clang
Expected
std::vector< const char * > Expected
Definition: PrintASTTests.cpp:27
ProTypeVarargCheck.h
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::cppcoreguidelines::hasSingleVariadicArgumentWithValue
static bool hasSingleVariadicArgumentWithValue(const CallExpr *C, uint64_t I)
Definition: ProTypeVarargCheck.cpp:128
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:36
clang::ast_matchers::AST_MATCHER
AST_MATCHER(Expr, isMacroID)
Definition: PreferIsaOrDynCastInConditionalsCheck.cpp:19
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:253
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::cppcoreguidelines::VAArgExpr
const internal::VariadicDynCastAllOfMatcher< Stmt, VAArgExpr > VAArgExpr
Definition: ProTypeVarargCheck.cpp:21
clang::tidy::bugprone::AST_MATCHER_P
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
Definition: EasilySwappableParametersCheck.cpp:1877
clang::tidy::cppcoreguidelines::AllowedVariadics
static constexpr StringRef AllowedVariadics[]
Definition: ProTypeVarargCheck.cpp:23