1 | //===--- AvoidNonConstGlobalVariablesCheck.cpp - clang-tidy ---------------===// |
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-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "AvoidNonConstGlobalVariablesCheck.h" |
10 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
11 | #include "clang/ASTMatchers/ASTMatchers.h" |
12 | |
13 | using namespace clang::ast_matchers; |
14 | |
15 | namespace clang::tidy::cppcoreguidelines { |
16 | |
17 | AvoidNonConstGlobalVariablesCheck::AvoidNonConstGlobalVariablesCheck( |
18 | StringRef Name, ClangTidyContext *Context) |
19 | : ClangTidyCheck(Name, Context), |
20 | AllowInternalLinkage(Options.get(LocalName: "AllowInternalLinkage" , Default: false)) {} |
21 | |
22 | void AvoidNonConstGlobalVariablesCheck::registerMatchers(MatchFinder *Finder) { |
23 | auto NamespaceMatcher = AllowInternalLinkage |
24 | ? namespaceDecl(unless(isAnonymous())) |
25 | : namespaceDecl(); |
26 | auto GlobalContext = |
27 | varDecl(hasGlobalStorage(), |
28 | hasDeclContext(InnerMatcher: anyOf(NamespaceMatcher, translationUnitDecl()))); |
29 | |
30 | auto GlobalVariable = varDecl( |
31 | GlobalContext, |
32 | AllowInternalLinkage ? varDecl(unless(isStaticStorageClass())) |
33 | : varDecl(), |
34 | unless(anyOf( |
35 | isConstexpr(), hasType(InnerMatcher: isConstQualified()), |
36 | hasType(InnerMatcher: referenceType())))); // References can't be changed, only the |
37 | // data they reference can be changed. |
38 | |
39 | auto GlobalReferenceToNonConst = |
40 | varDecl(GlobalContext, hasType(InnerMatcher: referenceType()), |
41 | unless(hasType(InnerMatcher: references(InnerMatcher: qualType(isConstQualified()))))); |
42 | |
43 | auto GlobalPointerToNonConst = varDecl( |
44 | GlobalContext, hasType(InnerMatcher: pointerType(pointee(unless(isConstQualified()))))); |
45 | |
46 | Finder->addMatcher(NodeMatch: GlobalVariable.bind(ID: "non-const_variable" ), Action: this); |
47 | Finder->addMatcher(NodeMatch: GlobalReferenceToNonConst.bind(ID: "indirection_to_non-const" ), |
48 | Action: this); |
49 | Finder->addMatcher(NodeMatch: GlobalPointerToNonConst.bind(ID: "indirection_to_non-const" ), |
50 | Action: this); |
51 | } |
52 | |
53 | void AvoidNonConstGlobalVariablesCheck::check( |
54 | const MatchFinder::MatchResult &Result) { |
55 | if (const auto *Variable = |
56 | Result.Nodes.getNodeAs<VarDecl>(ID: "non-const_variable" )) { |
57 | diag(Variable->getLocation(), "variable %0 is non-const and globally " |
58 | "accessible, consider making it const" ) |
59 | << Variable; // FIXME: Add fix-it hint to Variable |
60 | // Don't return early, a non-const variable may also be a pointer or |
61 | // reference to non-const data. |
62 | } |
63 | |
64 | if (const auto *VD = |
65 | Result.Nodes.getNodeAs<VarDecl>(ID: "indirection_to_non-const" )) { |
66 | diag(VD->getLocation(), |
67 | "variable %0 provides global access to a non-const object; consider " |
68 | "making the %select{referenced|pointed-to}1 data 'const'" ) |
69 | << VD |
70 | << VD->getType()->isPointerType(); // FIXME: Add fix-it hint to Variable |
71 | } |
72 | } |
73 | |
74 | void AvoidNonConstGlobalVariablesCheck::storeOptions( |
75 | ClangTidyOptions::OptionMap &Opts) { |
76 | Options.store(Options&: Opts, LocalName: "AllowInternalLinkage" , Value: AllowInternalLinkage); |
77 | } |
78 | |
79 | } // namespace clang::tidy::cppcoreguidelines |
80 | |