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