xref: /llvm-project/clang-tools-extra/clang-tidy/cppcoreguidelines/InitVariablesCheck.cpp (revision 028ea71fdda0c02cd11421cd1d26bec6f378666e)
1b879fd05SAaron Ballman //===--- InitVariablesCheck.cpp - clang-tidy ------------------------------===//
2b879fd05SAaron Ballman //
3b879fd05SAaron Ballman // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b879fd05SAaron Ballman // See https://llvm.org/LICENSE.txt for license information.
5b879fd05SAaron Ballman // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b879fd05SAaron Ballman //
7b879fd05SAaron Ballman //===----------------------------------------------------------------------===//
8b879fd05SAaron Ballman 
9b879fd05SAaron Ballman #include "InitVariablesCheck.h"
10b879fd05SAaron Ballman 
11*028ea71fSJulian Schmidt #include "../utils/LexerUtils.h"
12b879fd05SAaron Ballman #include "clang/AST/ASTContext.h"
13*028ea71fSJulian Schmidt #include "clang/AST/Type.h"
14b879fd05SAaron Ballman #include "clang/ASTMatchers/ASTMatchFinder.h"
15860aefd0SNathan James #include "clang/Lex/Preprocessor.h"
1671f55735SKazu Hirata #include <optional>
17b879fd05SAaron Ballman 
18b879fd05SAaron Ballman using namespace clang::ast_matchers;
19b879fd05SAaron Ballman 
207d2ea6c4SCarlos Galvez namespace clang::tidy::cppcoreguidelines {
21b879fd05SAaron Ballman 
22b879fd05SAaron Ballman namespace {
23b879fd05SAaron Ballman AST_MATCHER(VarDecl, isLocalVarDecl) { return Node.isLocalVarDecl(); }
24b879fd05SAaron Ballman } // namespace
25b879fd05SAaron Ballman 
26b879fd05SAaron Ballman InitVariablesCheck::InitVariablesCheck(StringRef Name,
27b879fd05SAaron Ballman                                        ClangTidyContext *Context)
28b879fd05SAaron Ballman     : ClangTidyCheck(Name, Context),
2913c9bbc2SNathan James       IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
30b859c39cSNathan James                                                utils::IncludeSorter::IS_LLVM),
31b859c39cSNathan James                       areDiagsSelfContained()),
32fdfe324dSAlexander Kornienko       MathHeader(Options.get("MathHeader", "<math.h>")) {}
33b879fd05SAaron Ballman 
34db90d315SNathan James void InitVariablesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
3513c9bbc2SNathan James   Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
36db90d315SNathan James   Options.store(Opts, "MathHeader", MathHeader);
37db90d315SNathan James }
38db90d315SNathan James 
39b879fd05SAaron Ballman void InitVariablesCheck::registerMatchers(MatchFinder *Finder) {
40efcd09ceSNathan James   std::string BadDecl = "badDecl";
41efcd09ceSNathan James   Finder->addMatcher(
42efcd09ceSNathan James       varDecl(unless(hasInitializer(anything())), unless(isInstantiated()),
43efcd09ceSNathan James               isLocalVarDecl(), unless(isStaticLocal()), isDefinition(),
44669494e9SNathan James               unless(hasParent(cxxCatchStmt())),
45efcd09ceSNathan James               optionally(hasParent(declStmt(hasParent(
46efcd09ceSNathan James                   cxxForRangeStmt(hasLoopVariable(varDecl().bind(BadDecl))))))),
47efcd09ceSNathan James               unless(equalsBoundNode(BadDecl)))
48b879fd05SAaron Ballman           .bind("vardecl"),
49b879fd05SAaron Ballman       this);
50b879fd05SAaron Ballman }
51b879fd05SAaron Ballman 
52b879fd05SAaron Ballman void InitVariablesCheck::registerPPCallbacks(const SourceManager &SM,
53b879fd05SAaron Ballman                                              Preprocessor *PP,
54b879fd05SAaron Ballman                                              Preprocessor *ModuleExpanderPP) {
5513c9bbc2SNathan James   IncludeInserter.registerPreprocessor(PP);
56b879fd05SAaron Ballman }
57b879fd05SAaron Ballman 
58b879fd05SAaron Ballman void InitVariablesCheck::check(const MatchFinder::MatchResult &Result) {
59b879fd05SAaron Ballman   const auto *MatchedDecl = Result.Nodes.getNodeAs<VarDecl>("vardecl");
60b879fd05SAaron Ballman   const ASTContext &Context = *Result.Context;
61b879fd05SAaron Ballman   const SourceManager &Source = Context.getSourceManager();
62b879fd05SAaron Ballman 
63ce6de98bSSockke   // Clang diagnostic error may cause the variable to be an invalid int vardecl
64ce6de98bSSockke   if (MatchedDecl->isInvalidDecl())
65ce6de98bSSockke     return;
66ce6de98bSSockke 
67b879fd05SAaron Ballman   // We want to warn about cases where the type name
68b879fd05SAaron Ballman   // comes from a macro like this:
69b879fd05SAaron Ballman   //
70b879fd05SAaron Ballman   // TYPENAME_FROM_MACRO var;
71b879fd05SAaron Ballman   //
72b879fd05SAaron Ballman   // but not if the entire declaration comes from
73b879fd05SAaron Ballman   // one:
74b879fd05SAaron Ballman   //
75b879fd05SAaron Ballman   // DEFINE_SOME_VARIABLE();
76b879fd05SAaron Ballman   //
77b879fd05SAaron Ballman   // or if the definition comes from a macro like SWAP
78b879fd05SAaron Ballman   // that uses an internal temporary variable.
79b879fd05SAaron Ballman   //
80b879fd05SAaron Ballman   // Thus check that the variable name does
81b879fd05SAaron Ballman   // not come from a macro expansion.
82b879fd05SAaron Ballman   if (MatchedDecl->getEndLoc().isMacroID())
83b879fd05SAaron Ballman     return;
84b879fd05SAaron Ballman 
85b879fd05SAaron Ballman   QualType TypePtr = MatchedDecl->getType();
86f71ffd3bSKazu Hirata   std::optional<const char *> InitializationString;
87b879fd05SAaron Ballman   bool AddMathInclude = false;
88b879fd05SAaron Ballman 
894a097efeSLiuke Gehry   if (TypePtr->isEnumeralType())
904a097efeSLiuke Gehry     InitializationString = nullptr;
9133e21295SDanny Mösch   else if (TypePtr->isBooleanType())
9233e21295SDanny Mösch     InitializationString = " = false";
934a097efeSLiuke Gehry   else if (TypePtr->isIntegerType())
94b879fd05SAaron Ballman     InitializationString = " = 0";
95b879fd05SAaron Ballman   else if (TypePtr->isFloatingType()) {
96b879fd05SAaron Ballman     InitializationString = " = NAN";
97b879fd05SAaron Ballman     AddMathInclude = true;
98b879fd05SAaron Ballman   } else if (TypePtr->isAnyPointerType()) {
99b879fd05SAaron Ballman     if (getLangOpts().CPlusPlus11)
100b879fd05SAaron Ballman       InitializationString = " = nullptr";
101b879fd05SAaron Ballman     else
102b879fd05SAaron Ballman       InitializationString = " = NULL";
103b879fd05SAaron Ballman   }
104b879fd05SAaron Ballman 
105b879fd05SAaron Ballman   if (InitializationString) {
106b879fd05SAaron Ballman     auto Diagnostic =
107b879fd05SAaron Ballman         diag(MatchedDecl->getLocation(), "variable %0 is not initialized")
1084a097efeSLiuke Gehry         << MatchedDecl;
1094a097efeSLiuke Gehry     if (*InitializationString != nullptr)
1104a097efeSLiuke Gehry       Diagnostic << FixItHint::CreateInsertion(
111*028ea71fSJulian Schmidt           utils::lexer::findNextTerminator(MatchedDecl->getLocation(),
112*028ea71fSJulian Schmidt                                            *Result.SourceManager,
113*028ea71fSJulian Schmidt                                            Result.Context->getLangOpts()),
1144a097efeSLiuke Gehry           *InitializationString);
115b879fd05SAaron Ballman     if (AddMathInclude) {
11613c9bbc2SNathan James       Diagnostic << IncludeInserter.createIncludeInsertion(
117fdfe324dSAlexander Kornienko           Source.getFileID(MatchedDecl->getBeginLoc()), MathHeader);
118b879fd05SAaron Ballman     }
119b879fd05SAaron Ballman   }
120b879fd05SAaron Ballman }
1217d2ea6c4SCarlos Galvez } // namespace clang::tidy::cppcoreguidelines
122