[AMDGPU] Infer amdgpu-no-flat-scratch-init attribute in AMDGPUAttributor (#94647)
[llvm-project.git] / clang-tools-extra / clang-tidy / misc / IncludeCleanerCheck.cpp
blob5e7a0e65690b7aaa704cf5c033eb92c2a11127bc
1 //===--- IncludeCleanerCheck.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 //===----------------------------------------------------------------------===//
9 #include "IncludeCleanerCheck.h"
10 #include "../ClangTidyCheck.h"
11 #include "../ClangTidyDiagnosticConsumer.h"
12 #include "../ClangTidyOptions.h"
13 #include "../utils/OptionsUtils.h"
14 #include "clang-include-cleaner/Analysis.h"
15 #include "clang-include-cleaner/IncludeSpeller.h"
16 #include "clang-include-cleaner/Record.h"
17 #include "clang-include-cleaner/Types.h"
18 #include "clang/AST/ASTContext.h"
19 #include "clang/AST/Decl.h"
20 #include "clang/AST/DeclBase.h"
21 #include "clang/ASTMatchers/ASTMatchFinder.h"
22 #include "clang/ASTMatchers/ASTMatchers.h"
23 #include "clang/Basic/Diagnostic.h"
24 #include "clang/Basic/FileEntry.h"
25 #include "clang/Basic/LLVM.h"
26 #include "clang/Basic/LangOptions.h"
27 #include "clang/Basic/SourceLocation.h"
28 #include "clang/Format/Format.h"
29 #include "clang/Lex/HeaderSearchOptions.h"
30 #include "clang/Lex/Preprocessor.h"
31 #include "clang/Tooling/Core/Replacement.h"
32 #include "clang/Tooling/Inclusions/HeaderIncludes.h"
33 #include "clang/Tooling/Inclusions/StandardLibrary.h"
34 #include "llvm/ADT/DenseSet.h"
35 #include "llvm/ADT/STLExtras.h"
36 #include "llvm/ADT/SmallVector.h"
37 #include "llvm/ADT/StringRef.h"
38 #include "llvm/ADT/StringSet.h"
39 #include "llvm/Support/ErrorHandling.h"
40 #include "llvm/Support/Path.h"
41 #include "llvm/Support/Regex.h"
42 #include <optional>
43 #include <string>
44 #include <vector>
46 using namespace clang::ast_matchers;
48 namespace clang::tidy::misc {
50 namespace {
51 struct MissingIncludeInfo {
52 include_cleaner::SymbolReference SymRef;
53 include_cleaner::Header Missing;
55 } // namespace
57 IncludeCleanerCheck::IncludeCleanerCheck(StringRef Name,
58 ClangTidyContext *Context)
59 : ClangTidyCheck(Name, Context),
60 IgnoreHeaders(utils::options::parseStringList(
61 Options.getLocalOrGlobal("IgnoreHeaders", ""))),
62 DeduplicateFindings(
63 Options.getLocalOrGlobal("DeduplicateFindings", true)) {
64 for (const auto &Header : IgnoreHeaders) {
65 if (!llvm::Regex{Header}.isValid())
66 configurationDiag("Invalid ignore headers regex '%0'") << Header;
67 std::string HeaderSuffix{Header.str()};
68 if (!Header.ends_with("$"))
69 HeaderSuffix += "$";
70 IgnoreHeadersRegex.emplace_back(HeaderSuffix);
74 void IncludeCleanerCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
75 Options.store(Opts, "IgnoreHeaders",
76 utils::options::serializeStringList(IgnoreHeaders));
77 Options.store(Opts, "DeduplicateFindings", DeduplicateFindings);
80 bool IncludeCleanerCheck::isLanguageVersionSupported(
81 const LangOptions &LangOpts) const {
82 return !LangOpts.ObjC;
85 void IncludeCleanerCheck::registerMatchers(MatchFinder *Finder) {
86 Finder->addMatcher(translationUnitDecl().bind("top"), this);
89 void IncludeCleanerCheck::registerPPCallbacks(const SourceManager &SM,
90 Preprocessor *PP,
91 Preprocessor *ModuleExpanderPP) {
92 PP->addPPCallbacks(RecordedPreprocessor.record(*PP));
93 this->PP = PP;
94 RecordedPI.record(*PP);
97 bool IncludeCleanerCheck::shouldIgnore(const include_cleaner::Header &H) {
98 return llvm::any_of(IgnoreHeadersRegex, [&H](const llvm::Regex &R) {
99 switch (H.kind()) {
100 case include_cleaner::Header::Standard:
101 // We don't trim angle brackets around standard library headers
102 // deliberately, so that they are only matched as <vector>, otherwise
103 // having just `.*/vector` might yield false positives.
104 return R.match(H.standard().name());
105 case include_cleaner::Header::Verbatim:
106 return R.match(H.verbatim().trim("<>\""));
107 case include_cleaner::Header::Physical:
108 return R.match(H.physical().getFileEntry().tryGetRealPathName());
110 llvm_unreachable("Unknown Header kind.");
114 void IncludeCleanerCheck::check(const MatchFinder::MatchResult &Result) {
115 const SourceManager *SM = Result.SourceManager;
116 const FileEntry *MainFile = SM->getFileEntryForID(SM->getMainFileID());
117 llvm::DenseSet<const include_cleaner::Include *> Used;
118 std::vector<MissingIncludeInfo> Missing;
119 llvm::SmallVector<Decl *> MainFileDecls;
120 for (Decl *D : Result.Nodes.getNodeAs<TranslationUnitDecl>("top")->decls()) {
121 if (!SM->isWrittenInMainFile(SM->getExpansionLoc(D->getLocation())))
122 continue;
123 // FIXME: Filter out implicit template specializations.
124 MainFileDecls.push_back(D);
126 llvm::DenseSet<include_cleaner::Symbol> SeenSymbols;
127 OptionalDirectoryEntryRef ResourceDir =
128 PP->getHeaderSearchInfo().getModuleMap().getBuiltinDir();
129 // FIXME: Find a way to have less code duplication between include-cleaner
130 // analysis implementation and the below code.
131 walkUsed(MainFileDecls, RecordedPreprocessor.MacroReferences, &RecordedPI,
132 *PP,
133 [&](const include_cleaner::SymbolReference &Ref,
134 llvm::ArrayRef<include_cleaner::Header> Providers) {
135 // Process each symbol once to reduce noise in the findings.
136 // Tidy checks are used in two different workflows:
137 // - Ones that show all the findings for a given file. For such
138 // workflows there is not much point in showing all the occurences,
139 // as one is enough to indicate the issue.
140 // - Ones that show only the findings on changed pieces. For such
141 // workflows it's useful to show findings on every reference of a
142 // symbol as otherwise tools might give incosistent results
143 // depending on the parts of the file being edited. But it should
144 // still help surface findings for "new violations" (i.e.
145 // dependency did not exist in the code at all before).
146 if (DeduplicateFindings && !SeenSymbols.insert(Ref.Target).second)
147 return;
148 bool Satisfied = false;
149 for (const include_cleaner::Header &H : Providers) {
150 if (H.kind() == include_cleaner::Header::Physical &&
151 (H.physical() == MainFile ||
152 H.physical().getDir() == ResourceDir)) {
153 Satisfied = true;
154 continue;
157 for (const include_cleaner::Include *I :
158 RecordedPreprocessor.Includes.match(H)) {
159 Used.insert(I);
160 Satisfied = true;
163 if (!Satisfied && !Providers.empty() &&
164 Ref.RT == include_cleaner::RefType::Explicit &&
165 !shouldIgnore(Providers.front()))
166 Missing.push_back({Ref, Providers.front()});
169 std::vector<const include_cleaner::Include *> Unused;
170 for (const include_cleaner::Include &I :
171 RecordedPreprocessor.Includes.all()) {
172 if (Used.contains(&I) || !I.Resolved || I.Resolved->getDir() == ResourceDir)
173 continue;
174 if (RecordedPI.shouldKeep(*I.Resolved))
175 continue;
176 // Check if main file is the public interface for a private header. If so
177 // we shouldn't diagnose it as unused.
178 if (auto PHeader = RecordedPI.getPublic(*I.Resolved); !PHeader.empty()) {
179 PHeader = PHeader.trim("<>\"");
180 // Since most private -> public mappings happen in a verbatim way, we
181 // check textually here. This might go wrong in presence of symlinks or
182 // header mappings. But that's not different than rest of the places.
183 if (getCurrentMainFile().ends_with(PHeader))
184 continue;
186 auto StdHeader = tooling::stdlib::Header::named(
187 I.quote(), PP->getLangOpts().CPlusPlus ? tooling::stdlib::Lang::CXX
188 : tooling::stdlib::Lang::C);
189 if (StdHeader && shouldIgnore(*StdHeader))
190 continue;
191 if (shouldIgnore(*I.Resolved))
192 continue;
193 Unused.push_back(&I);
196 llvm::StringRef Code = SM->getBufferData(SM->getMainFileID());
197 auto FileStyle =
198 format::getStyle(format::DefaultFormatStyle, getCurrentMainFile(),
199 format::DefaultFallbackStyle, Code,
200 &SM->getFileManager().getVirtualFileSystem());
201 if (!FileStyle)
202 FileStyle = format::getLLVMStyle();
204 for (const auto *Inc : Unused) {
205 diag(Inc->HashLocation, "included header %0 is not used directly")
206 << llvm::sys::path::filename(Inc->Spelled,
207 llvm::sys::path::Style::posix)
208 << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
209 SM->translateLineCol(SM->getMainFileID(), Inc->Line, 1),
210 SM->translateLineCol(SM->getMainFileID(), Inc->Line + 1, 1)));
213 tooling::HeaderIncludes HeaderIncludes(getCurrentMainFile(), Code,
214 FileStyle->IncludeStyle);
215 // Deduplicate insertions when running in bulk fix mode.
216 llvm::StringSet<> InsertedHeaders{};
217 for (const auto &Inc : Missing) {
218 std::string Spelling = include_cleaner::spellHeader(
219 {Inc.Missing, PP->getHeaderSearchInfo(), MainFile});
220 bool Angled = llvm::StringRef{Spelling}.starts_with("<");
221 // We might suggest insertion of an existing include in edge cases, e.g.,
222 // include is present in a PP-disabled region, or spelling of the header
223 // turns out to be the same as one of the unresolved includes in the
224 // main file.
225 if (auto Replacement =
226 HeaderIncludes.insert(llvm::StringRef{Spelling}.trim("\"<>"),
227 Angled, tooling::IncludeDirective::Include)) {
228 DiagnosticBuilder DB =
229 diag(SM->getSpellingLoc(Inc.SymRef.RefLocation),
230 "no header providing \"%0\" is directly included")
231 << Inc.SymRef.Target.name();
232 if (areDiagsSelfContained() ||
233 InsertedHeaders.insert(Replacement->getReplacementText()).second) {
234 DB << FixItHint::CreateInsertion(
235 SM->getComposedLoc(SM->getMainFileID(), Replacement->getOffset()),
236 Replacement->getReplacementText());
242 } // namespace clang::tidy::misc