xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp (revision 0b8866d15ac5806a980d2ff2ea63240d8acfa778)
1f27c8ac8SGergely Fűtő //===--- UnsafeFunctionsCheck.cpp - clang-tidy ----------------------------===//
2f27c8ac8SGergely Fűtő //
3f27c8ac8SGergely Fűtő // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f27c8ac8SGergely Fűtő // See https://llvm.org/LICENSE.txt for license information.
5f27c8ac8SGergely Fűtő // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f27c8ac8SGergely Fűtő //
7f27c8ac8SGergely Fűtő //===----------------------------------------------------------------------===//
8f27c8ac8SGergely Fűtő 
9f27c8ac8SGergely Fűtő #include "UnsafeFunctionsCheck.h"
10*0b8866d1SDiscookie #include "../utils/OptionsUtils.h"
11f27c8ac8SGergely Fűtő #include "clang/AST/ASTContext.h"
12f27c8ac8SGergely Fűtő #include "clang/ASTMatchers/ASTMatchFinder.h"
13f27c8ac8SGergely Fűtő #include "clang/Lex/PPCallbacks.h"
14f27c8ac8SGergely Fűtő #include "clang/Lex/Preprocessor.h"
15f27c8ac8SGergely Fűtő #include <cassert>
16f27c8ac8SGergely Fűtő 
17f27c8ac8SGergely Fűtő using namespace clang::ast_matchers;
18f27c8ac8SGergely Fűtő using namespace llvm;
19f27c8ac8SGergely Fűtő 
20d57cf05fSPiotr Zegar namespace clang::tidy::bugprone {
21f27c8ac8SGergely Fűtő 
22*0b8866d1SDiscookie static constexpr llvm::StringLiteral OptionNameCustomFunctions =
23*0b8866d1SDiscookie     "CustomFunctions";
24*0b8866d1SDiscookie static constexpr llvm::StringLiteral OptionNameReportDefaultFunctions =
25*0b8866d1SDiscookie     "ReportDefaultFunctions";
26f27c8ac8SGergely Fűtő static constexpr llvm::StringLiteral OptionNameReportMoreUnsafeFunctions =
27f27c8ac8SGergely Fűtő     "ReportMoreUnsafeFunctions";
28f27c8ac8SGergely Fűtő 
29f27c8ac8SGergely Fűtő static constexpr llvm::StringLiteral FunctionNamesWithAnnexKReplacementId =
30f27c8ac8SGergely Fűtő     "FunctionNamesWithAnnexKReplacement";
31f27c8ac8SGergely Fűtő static constexpr llvm::StringLiteral FunctionNamesId = "FunctionsNames";
32f27c8ac8SGergely Fűtő static constexpr llvm::StringLiteral AdditionalFunctionNamesId =
33f27c8ac8SGergely Fűtő     "AdditionalFunctionsNames";
34*0b8866d1SDiscookie static constexpr llvm::StringLiteral CustomFunctionNamesId =
35*0b8866d1SDiscookie     "CustomFunctionNames";
36f27c8ac8SGergely Fűtő static constexpr llvm::StringLiteral DeclRefId = "DRE";
37f27c8ac8SGergely Fűtő 
38f27c8ac8SGergely Fűtő static std::optional<std::string>
39f27c8ac8SGergely Fűtő getAnnexKReplacementFor(StringRef FunctionName) {
40f27c8ac8SGergely Fűtő   return StringSwitch<std::string>(FunctionName)
41f27c8ac8SGergely Fűtő       .Case("strlen", "strnlen_s")
42f27c8ac8SGergely Fűtő       .Case("wcslen", "wcsnlen_s")
43f27c8ac8SGergely Fűtő       .Default((Twine{FunctionName} + "_s").str());
44f27c8ac8SGergely Fűtő }
45f27c8ac8SGergely Fűtő 
46f27c8ac8SGergely Fűtő static StringRef getReplacementFor(StringRef FunctionName,
47f27c8ac8SGergely Fűtő                                    bool IsAnnexKAvailable) {
48f27c8ac8SGergely Fűtő   if (IsAnnexKAvailable) {
49f27c8ac8SGergely Fűtő     // Try to find a better replacement from Annex K first.
50f27c8ac8SGergely Fűtő     StringRef AnnexKReplacementFunction =
51f27c8ac8SGergely Fűtő         StringSwitch<StringRef>(FunctionName)
52f27c8ac8SGergely Fűtő             .Cases("asctime", "asctime_r", "asctime_s")
53f27c8ac8SGergely Fűtő             .Case("gets", "gets_s")
54f27c8ac8SGergely Fűtő             .Default({});
55f27c8ac8SGergely Fűtő     if (!AnnexKReplacementFunction.empty())
56f27c8ac8SGergely Fűtő       return AnnexKReplacementFunction;
57f27c8ac8SGergely Fűtő   }
58f27c8ac8SGergely Fűtő 
59f27c8ac8SGergely Fűtő   // FIXME: Some of these functions are available in C++ under "std::", and
60f27c8ac8SGergely Fűtő   // should be matched and suggested.
61f27c8ac8SGergely Fűtő   return StringSwitch<StringRef>(FunctionName)
62f27c8ac8SGergely Fűtő       .Cases("asctime", "asctime_r", "strftime")
63f27c8ac8SGergely Fűtő       .Case("gets", "fgets")
64f27c8ac8SGergely Fűtő       .Case("rewind", "fseek")
65f27c8ac8SGergely Fűtő       .Case("setbuf", "setvbuf");
66f27c8ac8SGergely Fűtő }
67f27c8ac8SGergely Fűtő 
68f27c8ac8SGergely Fűtő static StringRef getReplacementForAdditional(StringRef FunctionName,
69f27c8ac8SGergely Fűtő                                              bool IsAnnexKAvailable) {
70f27c8ac8SGergely Fűtő   if (IsAnnexKAvailable) {
71f27c8ac8SGergely Fűtő     // Try to find a better replacement from Annex K first.
72f27c8ac8SGergely Fűtő     StringRef AnnexKReplacementFunction = StringSwitch<StringRef>(FunctionName)
73f27c8ac8SGergely Fűtő                                               .Case("bcopy", "memcpy_s")
74f27c8ac8SGergely Fűtő                                               .Case("bzero", "memset_s")
75f27c8ac8SGergely Fűtő                                               .Default({});
76f27c8ac8SGergely Fűtő 
77f27c8ac8SGergely Fűtő     if (!AnnexKReplacementFunction.empty())
78f27c8ac8SGergely Fűtő       return AnnexKReplacementFunction;
79f27c8ac8SGergely Fűtő   }
80f27c8ac8SGergely Fűtő 
81f27c8ac8SGergely Fűtő   return StringSwitch<StringRef>(FunctionName)
82f27c8ac8SGergely Fűtő       .Case("bcmp", "memcmp")
83f27c8ac8SGergely Fűtő       .Case("bcopy", "memcpy")
84f27c8ac8SGergely Fűtő       .Case("bzero", "memset")
85f27c8ac8SGergely Fűtő       .Case("getpw", "getpwuid")
86f27c8ac8SGergely Fűtő       .Case("vfork", "posix_spawn");
87f27c8ac8SGergely Fűtő }
88f27c8ac8SGergely Fűtő 
89f27c8ac8SGergely Fűtő /// \returns The rationale for replacing the function \p FunctionName with the
90f27c8ac8SGergely Fűtő /// safer alternative.
91f27c8ac8SGergely Fűtő static StringRef getRationaleFor(StringRef FunctionName) {
92f27c8ac8SGergely Fűtő   return StringSwitch<StringRef>(FunctionName)
93f27c8ac8SGergely Fűtő       .Cases("asctime", "asctime_r", "ctime",
94f27c8ac8SGergely Fűtő              "is not bounds-checking and non-reentrant")
95f27c8ac8SGergely Fűtő       .Cases("bcmp", "bcopy", "bzero", "is deprecated")
96f27c8ac8SGergely Fűtő       .Cases("fopen", "freopen", "has no exclusive access to the opened file")
97f27c8ac8SGergely Fűtő       .Case("gets", "is insecure, was deprecated and removed in C11 and C++14")
98f27c8ac8SGergely Fűtő       .Case("getpw", "is dangerous as it may overflow the provided buffer")
99f27c8ac8SGergely Fűtő       .Cases("rewind", "setbuf", "has no error detection")
100f27c8ac8SGergely Fűtő       .Case("vfork", "is insecure as it can lead to denial of service "
101f27c8ac8SGergely Fűtő                      "situations in the parent process")
102f27c8ac8SGergely Fűtő       .Default("is not bounds-checking");
103f27c8ac8SGergely Fűtő }
104f27c8ac8SGergely Fűtő 
105f27c8ac8SGergely Fűtő /// Calculates whether Annex K is available for the current translation unit
106f27c8ac8SGergely Fűtő /// based on the macro definitions and the language options.
107f27c8ac8SGergely Fűtő ///
108f27c8ac8SGergely Fűtő /// The result is cached and saved in \p CacheVar.
109f27c8ac8SGergely Fűtő static bool isAnnexKAvailable(std::optional<bool> &CacheVar, Preprocessor *PP,
110f27c8ac8SGergely Fűtő                               const LangOptions &LO) {
111f27c8ac8SGergely Fűtő   if (CacheVar.has_value())
112f27c8ac8SGergely Fűtő     return *CacheVar;
113f27c8ac8SGergely Fűtő 
114f27c8ac8SGergely Fűtő   if (!LO.C11)
115f27c8ac8SGergely Fűtő     // TODO: How is "Annex K" available in C++ mode?
116f27c8ac8SGergely Fűtő     return (CacheVar = false).value();
117f27c8ac8SGergely Fűtő 
118f27c8ac8SGergely Fűtő   assert(PP && "No Preprocessor registered.");
119f27c8ac8SGergely Fűtő 
120f27c8ac8SGergely Fűtő   if (!PP->isMacroDefined("__STDC_LIB_EXT1__") ||
121f27c8ac8SGergely Fűtő       !PP->isMacroDefined("__STDC_WANT_LIB_EXT1__"))
122f27c8ac8SGergely Fűtő     return (CacheVar = false).value();
123f27c8ac8SGergely Fűtő 
124f27c8ac8SGergely Fűtő   const auto *MI =
125f27c8ac8SGergely Fűtő       PP->getMacroInfo(PP->getIdentifierInfo("__STDC_WANT_LIB_EXT1__"));
126f27c8ac8SGergely Fűtő   if (!MI || MI->tokens_empty())
127f27c8ac8SGergely Fűtő     return (CacheVar = false).value();
128f27c8ac8SGergely Fűtő 
129f27c8ac8SGergely Fűtő   const Token &T = MI->tokens().back();
130f27c8ac8SGergely Fűtő   if (!T.isLiteral() || !T.getLiteralData())
131f27c8ac8SGergely Fűtő     return (CacheVar = false).value();
132f27c8ac8SGergely Fűtő 
133f27c8ac8SGergely Fűtő   CacheVar = StringRef(T.getLiteralData(), T.getLength()) == "1";
134f27c8ac8SGergely Fűtő   return CacheVar.value();
135f27c8ac8SGergely Fűtő }
136f27c8ac8SGergely Fűtő 
137*0b8866d1SDiscookie static std::vector<UnsafeFunctionsCheck::CheckedFunction>
138*0b8866d1SDiscookie parseCheckedFunctions(StringRef Option, ClangTidyContext *Context) {
139*0b8866d1SDiscookie   const std::vector<StringRef> Functions =
140*0b8866d1SDiscookie       utils::options::parseStringList(Option);
141*0b8866d1SDiscookie   std::vector<UnsafeFunctionsCheck::CheckedFunction> Result;
142*0b8866d1SDiscookie   Result.reserve(Functions.size());
143*0b8866d1SDiscookie 
144*0b8866d1SDiscookie   for (StringRef Function : Functions) {
145*0b8866d1SDiscookie     if (Function.empty())
146*0b8866d1SDiscookie       continue;
147*0b8866d1SDiscookie 
148*0b8866d1SDiscookie     const auto [Name, Rest] = Function.split(',');
149*0b8866d1SDiscookie     const auto [Replacement, Reason] = Rest.split(',');
150*0b8866d1SDiscookie 
151*0b8866d1SDiscookie     if (Name.trim().empty()) {
152*0b8866d1SDiscookie       Context->configurationDiag("invalid configuration value for option '%0'; "
153*0b8866d1SDiscookie                                  "expected the name of an unsafe function")
154*0b8866d1SDiscookie           << OptionNameCustomFunctions;
155*0b8866d1SDiscookie       continue;
156*0b8866d1SDiscookie     }
157*0b8866d1SDiscookie 
158*0b8866d1SDiscookie     Result.push_back(
159*0b8866d1SDiscookie         {Name.trim().str(),
160*0b8866d1SDiscookie          matchers::MatchesAnyListedNameMatcher::NameMatcher(Name.trim()),
161*0b8866d1SDiscookie          Replacement.trim().str(), Reason.trim().str()});
162*0b8866d1SDiscookie   }
163*0b8866d1SDiscookie 
164*0b8866d1SDiscookie   return Result;
165*0b8866d1SDiscookie }
166*0b8866d1SDiscookie 
167*0b8866d1SDiscookie static std::string serializeCheckedFunctions(
168*0b8866d1SDiscookie     const std::vector<UnsafeFunctionsCheck::CheckedFunction> &Functions) {
169*0b8866d1SDiscookie   std::vector<std::string> Result;
170*0b8866d1SDiscookie   Result.reserve(Functions.size());
171*0b8866d1SDiscookie 
172*0b8866d1SDiscookie   for (const auto &Entry : Functions) {
173*0b8866d1SDiscookie     if (Entry.Reason.empty())
174*0b8866d1SDiscookie       Result.push_back(Entry.Name + "," + Entry.Replacement);
175*0b8866d1SDiscookie     else
176*0b8866d1SDiscookie       Result.push_back(Entry.Name + "," + Entry.Replacement + "," +
177*0b8866d1SDiscookie                        Entry.Reason);
178*0b8866d1SDiscookie   }
179*0b8866d1SDiscookie 
180*0b8866d1SDiscookie   return llvm::join(Result, ";");
181*0b8866d1SDiscookie }
182*0b8866d1SDiscookie 
183f27c8ac8SGergely Fűtő UnsafeFunctionsCheck::UnsafeFunctionsCheck(StringRef Name,
184f27c8ac8SGergely Fűtő                                            ClangTidyContext *Context)
185f27c8ac8SGergely Fűtő     : ClangTidyCheck(Name, Context),
186*0b8866d1SDiscookie       CustomFunctions(parseCheckedFunctions(
187*0b8866d1SDiscookie           Options.get(OptionNameCustomFunctions, ""), Context)),
188*0b8866d1SDiscookie       ReportDefaultFunctions(
189*0b8866d1SDiscookie           Options.get(OptionNameReportDefaultFunctions, true)),
190f27c8ac8SGergely Fűtő       ReportMoreUnsafeFunctions(
191f27c8ac8SGergely Fűtő           Options.get(OptionNameReportMoreUnsafeFunctions, true)) {}
192f27c8ac8SGergely Fűtő 
193f27c8ac8SGergely Fűtő void UnsafeFunctionsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
194*0b8866d1SDiscookie   Options.store(Opts, OptionNameCustomFunctions,
195*0b8866d1SDiscookie                 serializeCheckedFunctions(CustomFunctions));
196*0b8866d1SDiscookie   Options.store(Opts, OptionNameReportDefaultFunctions, ReportDefaultFunctions);
197f27c8ac8SGergely Fűtő   Options.store(Opts, OptionNameReportMoreUnsafeFunctions,
198f27c8ac8SGergely Fűtő                 ReportMoreUnsafeFunctions);
199f27c8ac8SGergely Fűtő }
200f27c8ac8SGergely Fűtő 
201f27c8ac8SGergely Fűtő void UnsafeFunctionsCheck::registerMatchers(MatchFinder *Finder) {
202*0b8866d1SDiscookie   if (ReportDefaultFunctions) {
203f27c8ac8SGergely Fűtő     if (getLangOpts().C11) {
204f27c8ac8SGergely Fűtő       // Matching functions with safe replacements only in Annex K.
205f27c8ac8SGergely Fűtő       auto FunctionNamesWithAnnexKReplacementMatcher = hasAnyName(
206*0b8866d1SDiscookie           "::bsearch", "::ctime", "::fopen", "::fprintf", "::freopen",
207*0b8866d1SDiscookie           "::fscanf", "::fwprintf", "::fwscanf", "::getenv", "::gmtime",
208*0b8866d1SDiscookie           "::localtime", "::mbsrtowcs", "::mbstowcs", "::memcpy", "::memmove",
209*0b8866d1SDiscookie           "::memset", "::printf", "::qsort", "::scanf", "::snprintf",
210*0b8866d1SDiscookie           "::sprintf", "::sscanf", "::strcat", "::strcpy", "::strerror",
211*0b8866d1SDiscookie           "::strlen", "::strncat", "::strncpy", "::strtok", "::swprintf",
212*0b8866d1SDiscookie           "::swscanf", "::vfprintf", "::vfscanf", "::vfwprintf", "::vfwscanf",
213*0b8866d1SDiscookie           "::vprintf", "::vscanf", "::vsnprintf", "::vsprintf", "::vsscanf",
214*0b8866d1SDiscookie           "::vswprintf", "::vswscanf", "::vwprintf", "::vwscanf", "::wcrtomb",
215*0b8866d1SDiscookie           "::wcscat", "::wcscpy", "::wcslen", "::wcsncat", "::wcsncpy",
216*0b8866d1SDiscookie           "::wcsrtombs", "::wcstok", "::wcstombs", "::wctomb", "::wmemcpy",
217*0b8866d1SDiscookie           "::wmemmove", "::wprintf", "::wscanf");
218f27c8ac8SGergely Fűtő       Finder->addMatcher(
219f27c8ac8SGergely Fűtő           declRefExpr(to(functionDecl(FunctionNamesWithAnnexKReplacementMatcher)
220f27c8ac8SGergely Fűtő                              .bind(FunctionNamesWithAnnexKReplacementId)))
221f27c8ac8SGergely Fűtő               .bind(DeclRefId),
222f27c8ac8SGergely Fűtő           this);
223f27c8ac8SGergely Fűtő     }
224f27c8ac8SGergely Fűtő 
225f27c8ac8SGergely Fűtő     // Matching functions with replacements without Annex K.
226f27c8ac8SGergely Fűtő     auto FunctionNamesMatcher =
227f27c8ac8SGergely Fűtő         hasAnyName("::asctime", "asctime_r", "::gets", "::rewind", "::setbuf");
228f27c8ac8SGergely Fűtő     Finder->addMatcher(
229*0b8866d1SDiscookie         declRefExpr(
230*0b8866d1SDiscookie             to(functionDecl(FunctionNamesMatcher).bind(FunctionNamesId)))
231f27c8ac8SGergely Fűtő             .bind(DeclRefId),
232f27c8ac8SGergely Fűtő         this);
233f27c8ac8SGergely Fűtő 
234f27c8ac8SGergely Fűtő     if (ReportMoreUnsafeFunctions) {
235f27c8ac8SGergely Fűtő       // Matching functions with replacements without Annex K, at user request.
236f27c8ac8SGergely Fűtő       auto AdditionalFunctionNamesMatcher =
237f27c8ac8SGergely Fűtő           hasAnyName("::bcmp", "::bcopy", "::bzero", "::getpw", "::vfork");
238f27c8ac8SGergely Fűtő       Finder->addMatcher(
239f27c8ac8SGergely Fűtő           declRefExpr(to(functionDecl(AdditionalFunctionNamesMatcher)
240f27c8ac8SGergely Fűtő                              .bind(AdditionalFunctionNamesId)))
241f27c8ac8SGergely Fűtő               .bind(DeclRefId),
242f27c8ac8SGergely Fűtő           this);
243f27c8ac8SGergely Fűtő     }
244f27c8ac8SGergely Fűtő   }
245f27c8ac8SGergely Fűtő 
246*0b8866d1SDiscookie   if (!CustomFunctions.empty()) {
247*0b8866d1SDiscookie     std::vector<llvm::StringRef> FunctionNames;
248*0b8866d1SDiscookie     FunctionNames.reserve(CustomFunctions.size());
249*0b8866d1SDiscookie 
250*0b8866d1SDiscookie     for (const auto &Entry : CustomFunctions)
251*0b8866d1SDiscookie       FunctionNames.push_back(Entry.Name);
252*0b8866d1SDiscookie 
253*0b8866d1SDiscookie     auto CustomFunctionsMatcher = matchers::matchesAnyListedName(FunctionNames);
254*0b8866d1SDiscookie 
255*0b8866d1SDiscookie     Finder->addMatcher(declRefExpr(to(functionDecl(CustomFunctionsMatcher)
256*0b8866d1SDiscookie                                           .bind(CustomFunctionNamesId)))
257*0b8866d1SDiscookie                            .bind(DeclRefId),
258*0b8866d1SDiscookie                        this);
259*0b8866d1SDiscookie   }
260*0b8866d1SDiscookie }
261*0b8866d1SDiscookie 
262f27c8ac8SGergely Fűtő void UnsafeFunctionsCheck::check(const MatchFinder::MatchResult &Result) {
263f27c8ac8SGergely Fűtő   const auto *DeclRef = Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefId);
264f27c8ac8SGergely Fűtő   const auto *FuncDecl = cast<FunctionDecl>(DeclRef->getDecl());
265f27c8ac8SGergely Fűtő   assert(DeclRef && FuncDecl && "No valid matched node in check()");
266f27c8ac8SGergely Fűtő 
267*0b8866d1SDiscookie   // Only one of these are matched at a time.
268f27c8ac8SGergely Fűtő   const auto *AnnexK = Result.Nodes.getNodeAs<FunctionDecl>(
269f27c8ac8SGergely Fűtő       FunctionNamesWithAnnexKReplacementId);
270f27c8ac8SGergely Fűtő   const auto *Normal = Result.Nodes.getNodeAs<FunctionDecl>(FunctionNamesId);
271f27c8ac8SGergely Fűtő   const auto *Additional =
272f27c8ac8SGergely Fűtő       Result.Nodes.getNodeAs<FunctionDecl>(AdditionalFunctionNamesId);
273*0b8866d1SDiscookie   const auto *Custom =
274*0b8866d1SDiscookie       Result.Nodes.getNodeAs<FunctionDecl>(CustomFunctionNamesId);
275*0b8866d1SDiscookie   assert((AnnexK || Normal || Additional || Custom) &&
276*0b8866d1SDiscookie          "No valid match category.");
277f27c8ac8SGergely Fűtő 
278f27c8ac8SGergely Fűtő   bool AnnexKIsAvailable =
279f27c8ac8SGergely Fűtő       isAnnexKAvailable(IsAnnexKAvailable, PP, getLangOpts());
280f27c8ac8SGergely Fűtő   StringRef FunctionName = FuncDecl->getName();
281*0b8866d1SDiscookie 
282*0b8866d1SDiscookie   if (Custom) {
283*0b8866d1SDiscookie     for (const auto &Entry : CustomFunctions) {
284*0b8866d1SDiscookie       if (Entry.Pattern.match(*FuncDecl)) {
285*0b8866d1SDiscookie         StringRef Reason =
286*0b8866d1SDiscookie             Entry.Reason.empty() ? "is marked as unsafe" : Entry.Reason.c_str();
287*0b8866d1SDiscookie 
288*0b8866d1SDiscookie         if (Entry.Replacement.empty()) {
289*0b8866d1SDiscookie           diag(DeclRef->getExprLoc(), "function %0 %1; it should not be used")
290*0b8866d1SDiscookie               << FuncDecl << Reason << Entry.Replacement
291*0b8866d1SDiscookie               << DeclRef->getSourceRange();
292*0b8866d1SDiscookie         } else {
293*0b8866d1SDiscookie           diag(DeclRef->getExprLoc(),
294*0b8866d1SDiscookie                "function %0 %1; '%2' should be used instead")
295*0b8866d1SDiscookie               << FuncDecl << Reason << Entry.Replacement
296*0b8866d1SDiscookie               << DeclRef->getSourceRange();
297*0b8866d1SDiscookie         }
298*0b8866d1SDiscookie 
299*0b8866d1SDiscookie         return;
300*0b8866d1SDiscookie       }
301*0b8866d1SDiscookie     }
302*0b8866d1SDiscookie 
303*0b8866d1SDiscookie     llvm_unreachable("No custom function was matched.");
304*0b8866d1SDiscookie     return;
305*0b8866d1SDiscookie   }
306*0b8866d1SDiscookie 
307f27c8ac8SGergely Fűtő   const std::optional<std::string> ReplacementFunctionName =
308f27c8ac8SGergely Fűtő       [&]() -> std::optional<std::string> {
309f27c8ac8SGergely Fűtő     if (AnnexK) {
310f27c8ac8SGergely Fűtő       if (AnnexKIsAvailable)
311f27c8ac8SGergely Fűtő         return getAnnexKReplacementFor(FunctionName);
312f27c8ac8SGergely Fűtő       return std::nullopt;
313f27c8ac8SGergely Fűtő     }
314f27c8ac8SGergely Fűtő 
315f27c8ac8SGergely Fűtő     if (Normal)
316f27c8ac8SGergely Fűtő       return getReplacementFor(FunctionName, AnnexKIsAvailable).str();
317f27c8ac8SGergely Fűtő 
318f27c8ac8SGergely Fűtő     if (Additional)
319f27c8ac8SGergely Fűtő       return getReplacementForAdditional(FunctionName, AnnexKIsAvailable).str();
320f27c8ac8SGergely Fűtő 
321f27c8ac8SGergely Fűtő     llvm_unreachable("Unhandled match category");
322f27c8ac8SGergely Fűtő   }();
323f27c8ac8SGergely Fűtő   if (!ReplacementFunctionName)
324f27c8ac8SGergely Fűtő     return;
325f27c8ac8SGergely Fűtő 
326f27c8ac8SGergely Fűtő   diag(DeclRef->getExprLoc(), "function %0 %1; '%2' should be used instead")
327f27c8ac8SGergely Fűtő       << FuncDecl << getRationaleFor(FunctionName)
328f27c8ac8SGergely Fűtő       << ReplacementFunctionName.value() << DeclRef->getSourceRange();
329f27c8ac8SGergely Fűtő }
330f27c8ac8SGergely Fűtő 
331f27c8ac8SGergely Fűtő void UnsafeFunctionsCheck::registerPPCallbacks(
332f27c8ac8SGergely Fűtő     const SourceManager &SM, Preprocessor *PP,
333f27c8ac8SGergely Fűtő     Preprocessor * /*ModuleExpanderPP*/) {
334f27c8ac8SGergely Fűtő   this->PP = PP;
3359225d08cSWhisperity }
3369225d08cSWhisperity 
3379225d08cSWhisperity void UnsafeFunctionsCheck::onEndOfTranslationUnit() {
3389225d08cSWhisperity   this->PP = nullptr;
339f27c8ac8SGergely Fűtő   IsAnnexKAvailable.reset();
340f27c8ac8SGergely Fűtő }
341f27c8ac8SGergely Fűtő 
342d57cf05fSPiotr Zegar } // namespace clang::tidy::bugprone
343