clang-tools  16.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 #include "clang/Lex/PPCallbacks.h"
15 #include "clang/Lex/Preprocessor.h"
16 #include "clang/Lex/Token.h"
17 
18 using namespace clang::ast_matchers;
19 
20 namespace clang {
21 namespace tidy {
22 namespace cppcoreguidelines {
23 
24 const internal::VariadicDynCastAllOfMatcher<Stmt, VAArgExpr> VAArgExpr;
25 
26 static constexpr StringRef AllowedVariadics[] = {
27  // clang-format off
28  "__builtin_isgreater",
29  "__builtin_isgreaterequal",
30  "__builtin_isless",
31  "__builtin_islessequal",
32  "__builtin_islessgreater",
33  "__builtin_isunordered",
34  "__builtin_fpclassify",
35  "__builtin_isfinite",
36  "__builtin_isinf",
37  "__builtin_isinf_sign",
38  "__builtin_isnan",
39  "__builtin_isnormal",
40  "__builtin_signbit",
41  "__builtin_constant_p",
42  "__builtin_classify_type",
43  "__builtin_va_start",
44  "__builtin_assume_aligned", // Documented as variadic to support default
45  // parameters.
46  "__builtin_prefetch", // Documented as variadic to support default
47  // parameters.
48  "__builtin_shufflevector", // Documented as variadic but with a defined
49  // number of args based on vector size.
50  "__builtin_convertvector",
51  "__builtin_call_with_static_chain",
52  "__builtin_annotation",
53  "__builtin_add_overflow",
54  "__builtin_sub_overflow",
55  "__builtin_mul_overflow",
56  "__builtin_preserve_access_index",
57  "__builtin_nontemporal_store",
58  "__builtin_nontemporal_load",
59  "__builtin_ms_va_start",
60  // clang-format on
61 };
62 
63 static constexpr StringRef VaArgWarningMessage =
64  "do not use va_arg to define c-style vararg functions; "
65  "use variadic templates instead";
66 
67 namespace {
68 AST_MATCHER(QualType, isVAList) {
69  ASTContext &Context = Finder->getASTContext();
70  QualType Desugar = Node.getDesugaredType(Context);
71  QualType NodeTy = Node.getUnqualifiedType();
72 
73  auto CheckVaList = [](QualType NodeTy, QualType Expected,
74  const ASTContext &Context) {
75  if (NodeTy == Expected)
76  return true;
77  QualType Desugar = NodeTy;
78  QualType Ty;
79  do {
80  Ty = Desugar;
81  Desugar = Ty.getSingleStepDesugaredType(Context);
82  if (Desugar == Expected)
83  return true;
84  } while (Desugar != Ty);
85  return false;
86  };
87 
88  // The internal implementation of __builtin_va_list depends on the target
89  // type. Some targets implements va_list as 'char *' or 'void *'.
90  // In these cases we need to remove all typedefs one by one to check this.
91  using BuiltinVaListKind = TargetInfo::BuiltinVaListKind;
92  BuiltinVaListKind VaListKind = Context.getTargetInfo().getBuiltinVaListKind();
93  if (VaListKind == BuiltinVaListKind::CharPtrBuiltinVaList ||
94  VaListKind == BuiltinVaListKind::VoidPtrBuiltinVaList) {
95  if (CheckVaList(NodeTy, Context.getBuiltinVaListType(), Context))
96  return true;
97  } else if (Desugar ==
98  Context.getBuiltinVaListType().getDesugaredType(Context)) {
99  return true;
100  }
101 
102  // We also need to check the implementation of __builtin_ms_va_list in the
103  // same way, because it may differ from the va_list implementation.
104  if (Desugar == Context.getBuiltinMSVaListType().getDesugaredType(Context) &&
105  CheckVaList(NodeTy, Context.getBuiltinMSVaListType(), Context)) {
106  return true;
107  }
108 
109  return false;
110 }
111 
112 AST_MATCHER_P(AdjustedType, hasOriginalType,
113  ast_matchers::internal::Matcher<QualType>, InnerType) {
114  return InnerType.matches(Node.getOriginalType(), Finder, Builder);
115 }
116 
117 class VaArgPPCallbacks : public PPCallbacks {
118 public:
119  VaArgPPCallbacks(ProTypeVarargCheck *Check) : Check(Check) {}
120 
121  void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
122  SourceRange Range, const MacroArgs *Args) override {
123  if (MacroNameTok.getIdentifierInfo()->getName() == "va_arg") {
124  Check->diag(MacroNameTok.getLocation(), VaArgWarningMessage);
125  }
126  }
127 
128 private:
129  ProTypeVarargCheck *Check;
130 };
131 } // namespace
132 
133 void ProTypeVarargCheck::registerMatchers(MatchFinder *Finder) {
134  Finder->addMatcher(VAArgExpr().bind("va_use"), this);
135 
136  Finder->addMatcher(
137  callExpr(callee(functionDecl(isVariadic(),
138  unless(hasAnyName(AllowedVariadics)))))
139  .bind("callvararg"),
140  this);
141 
142  Finder->addMatcher(
143  varDecl(unless(parmVarDecl()),
144  hasType(qualType(
145  anyOf(isVAList(), decayedType(hasOriginalType(isVAList()))))))
146  .bind("va_list"),
147  this);
148 }
149 
150 void ProTypeVarargCheck::registerPPCallbacks(const SourceManager &SM,
151  Preprocessor *PP,
152  Preprocessor *ModuleExpanderPP) {
153  PP->addPPCallbacks(std::make_unique<VaArgPPCallbacks>(this));
154 }
155 
156 static bool hasSingleVariadicArgumentWithValue(const CallExpr *C, uint64_t I) {
157  const auto *FDecl = dyn_cast<FunctionDecl>(C->getCalleeDecl());
158  if (!FDecl)
159  return false;
160 
161  auto N = FDecl->getNumParams(); // Number of parameters without '...'
162  if (C->getNumArgs() != N + 1)
163  return false; // more/less than one argument passed to '...'
164 
165  const auto *IntLit =
166  dyn_cast<IntegerLiteral>(C->getArg(N)->IgnoreParenImpCasts());
167  if (!IntLit)
168  return false;
169 
170  if (IntLit->getValue() != I)
171  return false;
172 
173  return true;
174 }
175 
176 void ProTypeVarargCheck::check(const MatchFinder::MatchResult &Result) {
177  if (const auto *Matched = Result.Nodes.getNodeAs<CallExpr>("callvararg")) {
178  if (hasSingleVariadicArgumentWithValue(Matched, 0))
179  return;
180  diag(Matched->getExprLoc(), "do not call c-style vararg functions");
181  }
182 
183  if (const auto *Matched = Result.Nodes.getNodeAs<Expr>("va_use")) {
184  diag(Matched->getExprLoc(), VaArgWarningMessage);
185  }
186 
187  if (const auto *Matched = Result.Nodes.getNodeAs<VarDecl>("va_list")) {
188  auto SR = Matched->getSourceRange();
189  if (SR.isInvalid())
190  return; // some implicitly generated builtins take va_list
191  diag(SR.getBegin(), "do not declare variables of type va_list; "
192  "use variadic templates instead");
193  }
194 }
195 
196 } // namespace cppcoreguidelines
197 } // namespace tidy
198 } // namespace clang
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:39
Expected
std::vector< const char * > Expected
Definition: PrintASTTests.cpp:26
clang::doc::MD
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
ProTypeVarargCheck.h
clang::tidy::cppcoreguidelines::VaArgWarningMessage
static constexpr StringRef VaArgWarningMessage
Definition: ProTypeVarargCheck.cpp:63
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::cppcoreguidelines::hasSingleVariadicArgumentWithValue
static bool hasSingleVariadicArgumentWithValue(const CallExpr *C, uint64_t I)
Definition: ProTypeVarargCheck.cpp:156
clang::ast_matchers::AST_MATCHER
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
Definition: InfiniteLoopCheck.cpp:24
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:36
Args
llvm::json::Object Args
Definition: Trace.cpp:138
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:29
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::check
bool check(llvm::StringRef File, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts)
Definition: Check.cpp:418
clang::tidy::cppcoreguidelines::ProTypeVarargCheck
This check flags all calls to c-style variadic functions and all use of va_arg.
Definition: ProTypeVarargCheck.h:23
clang::tidy::cppcoreguidelines::VAArgExpr
const internal::VariadicDynCastAllOfMatcher< Stmt, VAArgExpr > VAArgExpr
Definition: ProTypeVarargCheck.cpp:24
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:1886
clang::tidy::cppcoreguidelines::AllowedVariadics
static constexpr StringRef AllowedVariadics[]
Definition: ProTypeVarargCheck.cpp:26