122 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "InitVariablesCheck.h"10 11#include "../utils/LexerUtils.h"12#include "clang/AST/ASTContext.h"13#include "clang/AST/Type.h"14#include "clang/ASTMatchers/ASTMatchFinder.h"15#include "clang/Lex/Preprocessor.h"16#include <optional>17 18using namespace clang::ast_matchers;19 20namespace clang::tidy::cppcoreguidelines {21 22namespace {23AST_MATCHER(VarDecl, isLocalVarDecl) { return Node.isLocalVarDecl(); }24} // namespace25 26InitVariablesCheck::InitVariablesCheck(StringRef Name,27 ClangTidyContext *Context)28 : ClangTidyCheck(Name, Context),29 IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",30 utils::IncludeSorter::IS_LLVM),31 areDiagsSelfContained()),32 MathHeader(Options.get("MathHeader", "<math.h>")) {}33 34void InitVariablesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {35 Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());36 Options.store(Opts, "MathHeader", MathHeader);37}38 39void InitVariablesCheck::registerMatchers(MatchFinder *Finder) {40 const std::string BadDecl = "badDecl";41 Finder->addMatcher(42 varDecl(unless(hasInitializer(anything())), unless(isInstantiated()),43 isLocalVarDecl(), unless(isStaticLocal()), isDefinition(),44 unless(hasParent(cxxCatchStmt())),45 optionally(hasParent(declStmt(hasParent(46 cxxForRangeStmt(hasLoopVariable(varDecl().bind(BadDecl))))))),47 unless(equalsBoundNode(BadDecl)))48 .bind("vardecl"),49 this);50}51 52void InitVariablesCheck::registerPPCallbacks(const SourceManager &SM,53 Preprocessor *PP,54 Preprocessor *ModuleExpanderPP) {55 IncludeInserter.registerPreprocessor(PP);56}57 58void InitVariablesCheck::check(const MatchFinder::MatchResult &Result) {59 const auto *MatchedDecl = Result.Nodes.getNodeAs<VarDecl>("vardecl");60 const ASTContext &Context = *Result.Context;61 const SourceManager &Source = Context.getSourceManager();62 63 // Clang diagnostic error may cause the variable to be an invalid int vardecl64 if (MatchedDecl->isInvalidDecl())65 return;66 67 // We want to warn about cases where the type name68 // comes from a macro like this:69 //70 // TYPENAME_FROM_MACRO var;71 //72 // but not if the entire declaration comes from73 // one:74 //75 // DEFINE_SOME_VARIABLE();76 //77 // or if the definition comes from a macro like SWAP78 // that uses an internal temporary variable.79 //80 // Thus check that the variable name does81 // not come from a macro expansion.82 if (MatchedDecl->getEndLoc().isMacroID())83 return;84 85 const QualType TypePtr = MatchedDecl->getType();86 std::optional<const char *> InitializationString;87 bool AddMathInclude = false;88 89 if (TypePtr->isEnumeralType())90 InitializationString = nullptr;91 else if (TypePtr->isBooleanType())92 InitializationString = " = false";93 else if (TypePtr->isIntegerType())94 InitializationString = " = 0";95 else if (TypePtr->isFloatingType()) {96 InitializationString = " = NAN";97 AddMathInclude = true;98 } else if (TypePtr->isAnyPointerType()) {99 if (getLangOpts().CPlusPlus11)100 InitializationString = " = nullptr";101 else102 InitializationString = " = NULL";103 }104 105 if (InitializationString) {106 auto Diagnostic =107 diag(MatchedDecl->getLocation(), "variable %0 is not initialized")108 << MatchedDecl;109 if (*InitializationString != nullptr)110 Diagnostic << FixItHint::CreateInsertion(111 utils::lexer::findNextTerminator(MatchedDecl->getEndLoc(),112 *Result.SourceManager,113 Result.Context->getLangOpts()),114 *InitializationString);115 if (AddMathInclude) {116 Diagnostic << IncludeInserter.createIncludeInsertion(117 Source.getFileID(MatchedDecl->getBeginLoc()), MathHeader);118 }119 }120}121} // namespace clang::tidy::cppcoreguidelines122