[AMDGPU][AsmParser][NFC] Translate parsed MIMG instructions to MCInsts automatically.
[llvm-project.git] / clang-tools-extra / clang-tidy / misc / IncludeCleanerCheck.cpp
blob064eccd9cd66784597a95b80de88fd73d152b163
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/Preprocessor.h"
30 #include "clang/Tooling/Core/Replacement.h"
31 #include "clang/Tooling/Inclusions/HeaderIncludes.h"
32 #include "llvm/ADT/DenseSet.h"
33 #include "llvm/ADT/STLExtras.h"
34 #include "llvm/ADT/SmallVector.h"
35 #include "llvm/ADT/StringRef.h"
36 #include "llvm/Support/ErrorHandling.h"
37 #include "llvm/Support/Path.h"
38 #include "llvm/Support/Regex.h"
39 #include <optional>
40 #include <string>
41 #include <vector>
43 using namespace clang::ast_matchers;
45 namespace clang::tidy::misc {
47 namespace {
48 struct MissingIncludeInfo {
49 include_cleaner::SymbolReference SymRef;
50 include_cleaner::Header Missing;
52 } // namespace
54 IncludeCleanerCheck::IncludeCleanerCheck(StringRef Name,
55 ClangTidyContext *Context)
56 : ClangTidyCheck(Name, Context),
57 IgnoreHeaders(utils::options::parseStringList(
58 Options.getLocalOrGlobal("IgnoreHeaders", ""))) {
59 for (const auto &Header : IgnoreHeaders) {
60 if (!llvm::Regex{Header}.isValid())
61 configurationDiag("Invalid ignore headers regex '%0'") << Header;
62 std::string HeaderSuffix{Header.str()};
63 if (!Header.ends_with("$"))
64 HeaderSuffix += "$";
65 IgnoreHeadersRegex.emplace_back(HeaderSuffix);
69 void IncludeCleanerCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
70 Options.store(Opts, "IgnoreHeaders",
71 utils::options::serializeStringList(IgnoreHeaders));
74 bool IncludeCleanerCheck::isLanguageVersionSupported(
75 const LangOptions &LangOpts) const {
76 return !LangOpts.ObjC;
79 void IncludeCleanerCheck::registerMatchers(MatchFinder *Finder) {
80 Finder->addMatcher(translationUnitDecl().bind("top"), this);
83 void IncludeCleanerCheck::registerPPCallbacks(const SourceManager &SM,
84 Preprocessor *PP,
85 Preprocessor *ModuleExpanderPP) {
86 PP->addPPCallbacks(RecordedPreprocessor.record(*PP));
87 HS = &PP->getHeaderSearchInfo();
88 RecordedPI.record(*PP);
91 bool IncludeCleanerCheck::shouldIgnore(const include_cleaner::Header &H) {
92 return llvm::any_of(IgnoreHeadersRegex, [&H](const llvm::Regex &R) {
93 switch (H.kind()) {
94 case include_cleaner::Header::Standard:
95 return R.match(H.standard().name());
96 case include_cleaner::Header::Verbatim:
97 return R.match(H.verbatim());
98 case include_cleaner::Header::Physical:
99 return R.match(H.physical()->tryGetRealPathName());
101 llvm_unreachable("Unknown Header kind.");
105 void IncludeCleanerCheck::check(const MatchFinder::MatchResult &Result) {
106 const SourceManager *SM = Result.SourceManager;
107 const FileEntry *MainFile = SM->getFileEntryForID(SM->getMainFileID());
108 llvm::DenseSet<const include_cleaner::Include *> Used;
109 std::vector<MissingIncludeInfo> Missing;
110 llvm::SmallVector<Decl *> MainFileDecls;
111 for (Decl *D : Result.Nodes.getNodeAs<TranslationUnitDecl>("top")->decls()) {
112 if (!SM->isWrittenInMainFile(SM->getExpansionLoc(D->getLocation())))
113 continue;
114 // FIXME: Filter out implicit template specializations.
115 MainFileDecls.push_back(D);
117 // FIXME: Find a way to have less code duplication between include-cleaner
118 // analysis implementation and the below code.
119 walkUsed(MainFileDecls, RecordedPreprocessor.MacroReferences, &RecordedPI,
120 *SM,
121 [&](const include_cleaner::SymbolReference &Ref,
122 llvm::ArrayRef<include_cleaner::Header> Providers) {
123 bool Satisfied = false;
124 for (const include_cleaner::Header &H : Providers) {
125 if (H.kind() == include_cleaner::Header::Physical &&
126 H.physical() == MainFile)
127 Satisfied = true;
129 for (const include_cleaner::Include *I :
130 RecordedPreprocessor.Includes.match(H)) {
131 Used.insert(I);
132 Satisfied = true;
135 if (!Satisfied && !Providers.empty() &&
136 Ref.RT == include_cleaner::RefType::Explicit &&
137 !shouldIgnore(Providers.front()))
138 Missing.push_back({Ref, Providers.front()});
141 std::vector<const include_cleaner::Include *> Unused;
142 for (const include_cleaner::Include &I :
143 RecordedPreprocessor.Includes.all()) {
144 if (Used.contains(&I) || !I.Resolved)
145 continue;
146 if (RecordedPI.shouldKeep(I.Line))
147 continue;
148 // Check if main file is the public interface for a private header. If so
149 // we shouldn't diagnose it as unused.
150 if (auto PHeader = RecordedPI.getPublic(I.Resolved); !PHeader.empty()) {
151 PHeader = PHeader.trim("<>\"");
152 // Since most private -> public mappings happen in a verbatim way, we
153 // check textually here. This might go wrong in presence of symlinks or
154 // header mappings. But that's not different than rest of the places.
155 if (getCurrentMainFile().endswith(PHeader))
156 continue;
159 if (llvm::none_of(IgnoreHeadersRegex,
160 [Resolved = I.Resolved->tryGetRealPathName()](
161 const llvm::Regex &R) { return R.match(Resolved); }))
162 Unused.push_back(&I);
165 llvm::StringRef Code = SM->getBufferData(SM->getMainFileID());
166 auto FileStyle =
167 format::getStyle(format::DefaultFormatStyle, getCurrentMainFile(),
168 format::DefaultFallbackStyle, Code,
169 &SM->getFileManager().getVirtualFileSystem());
170 if (!FileStyle)
171 FileStyle = format::getLLVMStyle();
173 for (const auto *Inc : Unused) {
174 diag(Inc->HashLocation, "included header %0 is not used directly")
175 << llvm::sys::path::filename(Inc->Spelled,
176 llvm::sys::path::Style::posix)
177 << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
178 SM->translateLineCol(SM->getMainFileID(), Inc->Line, 1),
179 SM->translateLineCol(SM->getMainFileID(), Inc->Line + 1, 1)));
182 tooling::HeaderIncludes HeaderIncludes(getCurrentMainFile(), Code,
183 FileStyle->IncludeStyle);
184 for (const auto &Inc : Missing) {
185 std::string Spelling =
186 include_cleaner::spellHeader({Inc.Missing, *HS, MainFile});
187 bool Angled = llvm::StringRef{Spelling}.starts_with("<");
188 // We might suggest insertion of an existing include in edge cases, e.g.,
189 // include is present in a PP-disabled region, or spelling of the header
190 // turns out to be the same as one of the unresolved includes in the
191 // main file.
192 if (auto Replacement =
193 HeaderIncludes.insert(llvm::StringRef{Spelling}.trim("\"<>"),
194 Angled, tooling::IncludeDirective::Include))
195 diag(SM->getSpellingLoc(Inc.SymRef.RefLocation),
196 "no header providing \"%0\" is directly included")
197 << Inc.SymRef.Target.name()
198 << FixItHint::CreateInsertion(
199 SM->getComposedLoc(SM->getMainFileID(),
200 Replacement->getOffset()),
201 Replacement->getReplacementText());
205 } // namespace clang::tidy::misc