122 const SourceManager *SM = Result.SourceManager;
123 const FileEntry *MainFile = SM->getFileEntryForID(SM->getMainFileID());
124 llvm::DenseSet<const include_cleaner::Include *> Used;
125 std::vector<MissingIncludeInfo> Missing;
126 llvm::SmallVector<Decl *> MainFileDecls;
127 for (Decl *D : Result.Nodes.getNodeAs<TranslationUnitDecl>(
"top")->decls()) {
128 if (!SM->isWrittenInMainFile(SM->getExpansionLoc(D->getLocation())))
131 MainFileDecls.push_back(D);
133 llvm::DenseSet<include_cleaner::Symbol> SeenSymbols;
134 OptionalDirectoryEntryRef ResourceDir =
135 PP->getHeaderSearchInfo().getModuleMap().getBuiltinDir();
138 walkUsed(MainFileDecls, RecordedPreprocessor.MacroReferences, &RecordedPI,
140 [&](
const include_cleaner::SymbolReference &Ref,
141 llvm::ArrayRef<include_cleaner::Header> Providers) {
153 if (DeduplicateFindings && !SeenSymbols.insert(Ref.Target).second)
155 bool Satisfied = false;
156 for (const include_cleaner::Header &H : Providers) {
157 if (H.kind() == include_cleaner::Header::Physical &&
158 (H.physical() == MainFile ||
159 H.physical().getDir() == ResourceDir)) {
164 for (const include_cleaner::Include *I :
165 RecordedPreprocessor.Includes.match(H)) {
170 if (!Satisfied && !Providers.empty() &&
171 Ref.RT == include_cleaner::RefType::Explicit &&
172 !shouldIgnore(Providers.front()))
173 Missing.push_back({Ref, Providers.front()});
176 std::vector<const include_cleaner::Include *> Unused;
177 for (
const include_cleaner::Include &I :
178 RecordedPreprocessor.Includes.all()) {
179 if (Used.contains(&I) || !I.Resolved || I.Resolved->getDir() == ResourceDir)
181 if (RecordedPI.shouldKeep(*I.Resolved))
185 if (
auto PHeader = RecordedPI.getPublic(*I.Resolved); !PHeader.empty()) {
186 PHeader = PHeader.trim(
"<>\"");
190 if (getCurrentMainFile().ends_with(PHeader))
193 auto StdHeader = tooling::stdlib::Header::named(
194 I.quote(), PP->getLangOpts().CPlusPlus ? tooling::stdlib::Lang::CXX
195 : tooling::stdlib::Lang::C);
196 if (StdHeader && shouldIgnore(*StdHeader))
198 if (shouldIgnore(*I.Resolved))
200 Unused.push_back(&I);
203 llvm::StringRef Code = SM->getBufferData(SM->getMainFileID());
205 format::getStyle(format::DefaultFormatStyle, getCurrentMainFile(),
206 format::DefaultFallbackStyle, Code,
207 &SM->getFileManager().getVirtualFileSystem());
209 FileStyle = format::getLLVMStyle();
211 if (UnusedIncludes) {
212 for (
const auto *Inc : Unused) {
213 diag(Inc->HashLocation,
"included header %0 is not used directly")
214 << llvm::sys::path::filename(Inc->Spelled,
215 llvm::sys::path::Style::posix)
216 << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
217 SM->translateLineCol(SM->getMainFileID(), Inc->Line, 1),
218 SM->translateLineCol(SM->getMainFileID(), Inc->Line + 1, 1)));
222 if (MissingIncludes) {
223 tooling::HeaderIncludes HeaderIncludes(getCurrentMainFile(), Code,
224 FileStyle->IncludeStyle);
226 llvm::StringSet<> InsertedHeaders{};
227 for (
const auto &Inc : Missing) {
228 std::string Spelling = include_cleaner::spellHeader(
229 {Inc.Missing, PP->getHeaderSearchInfo(), MainFile});
230 bool Angled = llvm::StringRef{Spelling}.starts_with(
"<");
235 if (
auto Replacement = HeaderIncludes.insert(
236 llvm::StringRef{Spelling}.trim(
"\"<>"), Angled,
237 tooling::IncludeDirective::Include)) {
238 DiagnosticBuilder DB =
239 diag(SM->getSpellingLoc(Inc.SymRef.RefLocation),
240 "no header providing \"%0\" is directly included")
241 << Inc.SymRef.Target.name();
242 if (areDiagsSelfContained() ||
243 InsertedHeaders.insert(Replacement->getReplacementText()).second) {
244 DB << FixItHint::CreateInsertion(
245 SM->getComposedLoc(SM->getMainFileID(), Replacement->getOffset()),
246 Replacement->getReplacementText());
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.