xref: /llvm-project/clang-tools-extra/clang-tidy/cppcoreguidelines/InterfacesGlobalInitCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
1477e5d8dSAlexander Kornienko //===--- InterfacesGlobalInitCheck.cpp - clang-tidy------------------------===//
2477e5d8dSAlexander Kornienko //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6477e5d8dSAlexander Kornienko //
7477e5d8dSAlexander Kornienko //===----------------------------------------------------------------------===//
8477e5d8dSAlexander Kornienko 
9477e5d8dSAlexander Kornienko #include "InterfacesGlobalInitCheck.h"
10477e5d8dSAlexander Kornienko #include "clang/AST/ASTContext.h"
11477e5d8dSAlexander Kornienko #include "clang/ASTMatchers/ASTMatchFinder.h"
12477e5d8dSAlexander Kornienko 
13477e5d8dSAlexander Kornienko using namespace clang::ast_matchers;
14477e5d8dSAlexander Kornienko 
15*7d2ea6c4SCarlos Galvez namespace clang::tidy::cppcoreguidelines {
16477e5d8dSAlexander Kornienko 
registerMatchers(MatchFinder * Finder)17477e5d8dSAlexander Kornienko void InterfacesGlobalInitCheck::registerMatchers(MatchFinder *Finder) {
18976e0c07SAlexander Kornienko   const auto GlobalVarDecl =
19976e0c07SAlexander Kornienko       varDecl(hasGlobalStorage(),
20477e5d8dSAlexander Kornienko               hasDeclContext(anyOf(translationUnitDecl(), // Global scope.
21477e5d8dSAlexander Kornienko                                    namespaceDecl(),       // Namespace scope.
22477e5d8dSAlexander Kornienko                                    recordDecl())),        // Class scope.
23477e5d8dSAlexander Kornienko               unless(isConstexpr()));
24477e5d8dSAlexander Kornienko 
25477e5d8dSAlexander Kornienko   const auto ReferencesUndefinedGlobalVar = declRefExpr(hasDeclaration(
26976e0c07SAlexander Kornienko       varDecl(GlobalVarDecl, unless(isDefinition())).bind("referencee")));
27477e5d8dSAlexander Kornienko 
28027899daSAlexander Kornienko   Finder->addMatcher(
29027899daSAlexander Kornienko       traverse(TK_AsIs, varDecl(GlobalVarDecl, isDefinition(),
30a72307c3SStephen Kelly                                 hasInitializer(expr(hasDescendant(
31a72307c3SStephen Kelly                                     ReferencesUndefinedGlobalVar))))
32a72307c3SStephen Kelly                             .bind("var")),
33477e5d8dSAlexander Kornienko       this);
34477e5d8dSAlexander Kornienko }
35477e5d8dSAlexander Kornienko 
check(const MatchFinder::MatchResult & Result)36477e5d8dSAlexander Kornienko void InterfacesGlobalInitCheck::check(const MatchFinder::MatchResult &Result) {
37477e5d8dSAlexander Kornienko   const auto *const Var = Result.Nodes.getNodeAs<VarDecl>("var");
38477e5d8dSAlexander Kornienko   // For now assume that people who write macros know what they're doing.
39477e5d8dSAlexander Kornienko   if (Var->getLocation().isMacroID())
40477e5d8dSAlexander Kornienko     return;
41477e5d8dSAlexander Kornienko   const auto *const Referencee = Result.Nodes.getNodeAs<VarDecl>("referencee");
42477e5d8dSAlexander Kornienko   // If the variable has been defined, we're good.
43477e5d8dSAlexander Kornienko   const auto *const ReferenceeDef = Referencee->getDefinition();
44477e5d8dSAlexander Kornienko   if (ReferenceeDef != nullptr &&
45477e5d8dSAlexander Kornienko       Result.SourceManager->isBeforeInTranslationUnit(
46477e5d8dSAlexander Kornienko           ReferenceeDef->getLocation(), Var->getLocation())) {
47477e5d8dSAlexander Kornienko     return;
48477e5d8dSAlexander Kornienko   }
49477e5d8dSAlexander Kornienko   diag(Var->getLocation(),
50477e5d8dSAlexander Kornienko        "initializing non-local variable with non-const expression depending on "
51477e5d8dSAlexander Kornienko        "uninitialized non-local variable %0")
52477e5d8dSAlexander Kornienko       << Referencee;
53477e5d8dSAlexander Kornienko }
54477e5d8dSAlexander Kornienko 
55*7d2ea6c4SCarlos Galvez } // namespace clang::tidy::cppcoreguidelines
56