Bug Summary

File:build/source/clang-tools-extra/clangd/IncludeCleaner.cpp
Warning:line 204, column 12
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name IncludeCleaner.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-17/lib/clang/17 -D CLANG_REPOSITORY_STRING="++20230322120329+2b21327fee50-1~exp1~20230322000449.1178" -D _DEBUG -D _GLIBCXX_ASSERTIONS -D _GNU_SOURCE -D _LIBCPP_ENABLE_ASSERTIONS -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/clang/tools/extra/clangd -I /build/source/clang-tools-extra/clangd -I /build/source/clang-tools-extra/clangd/../include-cleaner/include -I tools/clang/tools/extra/clangd/../clang-tidy -I /build/source/clang/include -I tools/clang/include -I include -I /build/source/llvm/include -I /build/source/clang-tools-extra/pseudo/lib/../include -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-17/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/source/= -fcoverage-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/source/= -source-date-epoch 1679443490 -O2 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -Wno-misleading-indentation -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2023-03-22-005342-16304-1 -x c++ /build/source/clang-tools-extra/clangd/IncludeCleaner.cpp
1//===--- IncludeCleaner.cpp - Unused/Missing Headers Analysis ---*- C++ -*-===//
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 "IncludeCleaner.h"
10#include "Config.h"
11#include "Diagnostics.h"
12#include "Headers.h"
13#include "ParsedAST.h"
14#include "Protocol.h"
15#include "SourceCode.h"
16#include "URI.h"
17#include "clang-include-cleaner/Analysis.h"
18#include "clang-include-cleaner/Record.h"
19#include "clang-include-cleaner/Types.h"
20#include "support/Logger.h"
21#include "support/Path.h"
22#include "support/Trace.h"
23#include "clang/AST/ASTContext.h"
24#include "clang/AST/DeclCXX.h"
25#include "clang/AST/Expr.h"
26#include "clang/AST/ExprCXX.h"
27#include "clang/AST/TemplateName.h"
28#include "clang/AST/Type.h"
29#include "clang/Basic/Diagnostic.h"
30#include "clang/Basic/LLVM.h"
31#include "clang/Basic/SourceLocation.h"
32#include "clang/Basic/SourceManager.h"
33#include "clang/Format/Format.h"
34#include "clang/Lex/HeaderSearch.h"
35#include "clang/Lex/Preprocessor.h"
36#include "clang/Tooling/Core/Replacement.h"
37#include "clang/Tooling/Inclusions/HeaderIncludes.h"
38#include "clang/Tooling/Inclusions/StandardLibrary.h"
39#include "clang/Tooling/Syntax/Tokens.h"
40#include "llvm/ADT/ArrayRef.h"
41#include "llvm/ADT/DenseSet.h"
42#include "llvm/ADT/STLExtras.h"
43#include "llvm/ADT/STLFunctionalExtras.h"
44#include "llvm/ADT/SmallString.h"
45#include "llvm/ADT/SmallVector.h"
46#include "llvm/ADT/StringRef.h"
47#include "llvm/ADT/StringSet.h"
48#include "llvm/Support/Casting.h"
49#include "llvm/Support/Error.h"
50#include "llvm/Support/ErrorHandling.h"
51#include "llvm/Support/FormatVariadic.h"
52#include "llvm/Support/Path.h"
53#include "llvm/Support/Regex.h"
54#include <iterator>
55#include <optional>
56#include <string>
57#include <vector>
58
59namespace clang {
60namespace clangd {
61
62static bool AnalyzeStdlib = false;
63void setIncludeCleanerAnalyzesStdlib(bool B) { AnalyzeStdlib = B; }
64
65namespace {
66
67// Returns the range starting at '#' and ending at EOL. Escaped newlines are not
68// handled.
69clangd::Range getDiagnosticRange(llvm::StringRef Code, unsigned HashOffset) {
70 clangd::Range Result;
71 Result.end = Result.start = offsetToPosition(Code, HashOffset);
72
73 // Span the warning until the EOL or EOF.
74 Result.end.character +=
75 lspLength(Code.drop_front(HashOffset).take_until([](char C) {
76 return C == '\n' || C == '\r';
77 }));
78 return Result;
79}
80
81
82bool isFilteredByConfig(const Config &Cfg, llvm::StringRef HeaderPath) {
83 // Convert the path to Unix slashes and try to match against the filter.
84 llvm::SmallString<64> NormalizedPath(HeaderPath);
85 llvm::sys::path::native(NormalizedPath, llvm::sys::path::Style::posix);
86 for (auto &Filter : Cfg.Diagnostics.Includes.IgnoreHeader) {
87 if (Filter(NormalizedPath))
88 return true;
89 }
90 return false;
91}
92
93static bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST,
94 const Config &Cfg,
95 const include_cleaner::PragmaIncludes *PI) {
96 if (PI && PI->shouldKeep(Inc.HashLine + 1))
97 return false;
98 // FIXME(kirillbobyrev): We currently do not support the umbrella headers.
99 // System headers are likely to be standard library headers.
100 // Until we have good support for umbrella headers, don't warn about them.
101 if (Inc.Written.front() == '<') {
102 if (AnalyzeStdlib && tooling::stdlib::Header::named(Inc.Written))
103 return true;
104 return false;
105 }
106 assert(Inc.HeaderID)(static_cast <bool> (Inc.HeaderID) ? void (0) : __assert_fail
("Inc.HeaderID", "clang-tools-extra/clangd/IncludeCleaner.cpp"
, 106, __extension__ __PRETTY_FUNCTION__))
;
107 auto HID = static_cast<IncludeStructure::HeaderID>(*Inc.HeaderID);
108 auto FE = AST.getSourceManager().getFileManager().getFileRef(
109 AST.getIncludeStructure().getRealPath(HID));
110 assert(FE)(static_cast <bool> (FE) ? void (0) : __assert_fail ("FE"
, "clang-tools-extra/clangd/IncludeCleaner.cpp", 110, __extension__
__PRETTY_FUNCTION__))
;
111 // Headers without include guards have side effects and are not
112 // self-contained, skip them.
113 if (!AST.getPreprocessor().getHeaderSearchInfo().isFileMultipleIncludeGuarded(
114 &FE->getFileEntry())) {
115 dlog("{0} doesn't have header guard and will not be considered unused",do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType
(::clang::clangd::detail::debugType("clang-tools-extra/clangd/IncludeCleaner.cpp"
))) { ::clang::clangd::detail::log(Logger::Debug, "{0} doesn't have header guard and will not be considered unused"
, FE->getName()); } } while (false)
116 FE->getName())do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType
(::clang::clangd::detail::debugType("clang-tools-extra/clangd/IncludeCleaner.cpp"
))) { ::clang::clangd::detail::log(Logger::Debug, "{0} doesn't have header guard and will not be considered unused"
, FE->getName()); } } while (false)
;
117 return false;
118 }
119
120 if (isFilteredByConfig(Cfg, Inc.Resolved)) {
121 dlog("{0} header is filtered out by the configuration", FE->getName())do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType
(::clang::clangd::detail::debugType("clang-tools-extra/clangd/IncludeCleaner.cpp"
))) { ::clang::clangd::detail::log(Logger::Debug, "{0} header is filtered out by the configuration"
, FE->getName()); } } while (false)
;
122 return false;
123 }
124 return true;
125}
126
127include_cleaner::Includes
128convertIncludes(const SourceManager &SM,
129 const llvm::ArrayRef<Inclusion> MainFileIncludes) {
130 include_cleaner::Includes Includes;
131 for (const Inclusion &Inc : MainFileIncludes) {
132 include_cleaner::Include TransformedInc;
133 llvm::StringRef WrittenRef = llvm::StringRef(Inc.Written);
134 TransformedInc.Spelled = WrittenRef.trim("\"<>");
135 TransformedInc.HashLocation =
136 SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset);
137 TransformedInc.Line = Inc.HashLine + 1;
138 TransformedInc.Angled = WrittenRef.starts_with("<");
139 auto FE = SM.getFileManager().getFile(Inc.Resolved);
140 if (!FE) {
141 elog("IncludeCleaner: Failed to get an entry for resolved path {0}: {1}",
142 Inc.Resolved, FE.getError().message());
143 continue;
144 }
145 TransformedInc.Resolved = *FE;
146 Includes.add(std::move(TransformedInc));
147 }
148 return Includes;
149}
150
151std::string spellHeader(ParsedAST &AST, const FileEntry *MainFile,
152 include_cleaner::Header Provider) {
153 if (Provider.kind() == include_cleaner::Header::Physical) {
154 if (auto CanonicalPath =
155 getCanonicalPath(Provider.physical(), AST.getSourceManager())) {
156 std::string SpelledHeader =
157 llvm::cantFail(URI::includeSpelling(URI::create(*CanonicalPath)));
158 if (!SpelledHeader.empty())
159 return SpelledHeader;
160 }
161 }
162 return include_cleaner::spellHeader(
163 Provider, AST.getPreprocessor().getHeaderSearchInfo(), MainFile);
164}
165
166std::vector<include_cleaner::SymbolReference>
167collectMacroReferences(ParsedAST &AST) {
168 const auto &SM = AST.getSourceManager();
169 // FIXME: !!this is a hacky way to collect macro references.
170 std::vector<include_cleaner::SymbolReference> Macros;
171 auto &PP = AST.getPreprocessor();
172 for (const syntax::Token &Tok :
173 AST.getTokens().spelledTokens(SM.getMainFileID())) {
174 auto Macro = locateMacroAt(Tok, PP);
175 if (!Macro)
176 continue;
177 if (auto DefLoc = Macro->Info->getDefinitionLoc(); DefLoc.isValid())
178 Macros.push_back(
179 {Tok.location(),
180 include_cleaner::Macro{/*Name=*/PP.getIdentifierInfo(Tok.text(SM)),
181 DefLoc},
182 include_cleaner::RefType::Explicit});
183 }
184 return Macros;
185}
186
187llvm::StringRef getResolvedPath(const include_cleaner::Header &SymProvider) {
188 switch (SymProvider.kind()) {
189 case include_cleaner::Header::Physical:
190 return SymProvider.physical()->tryGetRealPathName();
191 case include_cleaner::Header::Standard:
192 return SymProvider.standard().name().trim("<>\"");
193 case include_cleaner::Header::Verbatim:
194 return SymProvider.verbatim().trim("<>\"");
195 }
196 llvm_unreachable("Unknown header kind")::llvm::llvm_unreachable_internal("Unknown header kind", "clang-tools-extra/clangd/IncludeCleaner.cpp"
, 196)
;
197}
198
199std::string getSymbolName(const include_cleaner::Symbol &Sym) {
200 switch (Sym.kind()) {
17
Control jumps to 'case Declaration:' at line 203
201 case include_cleaner::Symbol::Macro:
202 return Sym.macro().Name->getName().str();
203 case include_cleaner::Symbol::Declaration:
204 return llvm::dyn_cast<NamedDecl>(&Sym.declaration())
18
Assuming the object is not a 'CastReturnType'
19
Called C++ object pointer is null
205 ->getQualifiedNameAsString();
206 }
207 llvm_unreachable("Unknown symbol kind")::llvm::llvm_unreachable_internal("Unknown symbol kind", "clang-tools-extra/clangd/IncludeCleaner.cpp"
, 207)
;
208}
209
210std::vector<Diag> generateMissingIncludeDiagnostics(
211 ParsedAST &AST, llvm::ArrayRef<MissingIncludeDiagInfo> MissingIncludes,
212 llvm::StringRef Code) {
213 std::vector<Diag> Result;
214 const Config &Cfg = Config::current();
215 if (Cfg.Diagnostics.MissingIncludes != Config::IncludesPolicy::Strict ||
7
Assuming field 'MissingIncludes' is equal to Strict
10
Taking false branch
216 Cfg.Diagnostics.SuppressAll ||
8
Assuming field 'SuppressAll' is false
217 Cfg.Diagnostics.Suppress.contains("missing-includes")) {
9
Assuming the condition is false
218 return Result;
219 }
220
221 const SourceManager &SM = AST.getSourceManager();
222 const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID());
223
224 auto FileStyle = format::getStyle(
225 format::DefaultFormatStyle, AST.tuPath(), format::DefaultFallbackStyle,
226 Code, &SM.getFileManager().getVirtualFileSystem());
227 if (!FileStyle) {
11
Taking false branch
228 elog("Couldn't infer style", FileStyle.takeError());
229 FileStyle = format::getLLVMStyle();
230 }
231
232 tooling::HeaderIncludes HeaderIncludes(AST.tuPath(), Code,
233 FileStyle->IncludeStyle);
234 for (const auto &SymbolWithMissingInclude : MissingIncludes) {
12
Assuming '__begin2' is not equal to '__end2'
235 llvm::StringRef ResolvedPath =
236 getResolvedPath(SymbolWithMissingInclude.Providers.front());
237 if (isFilteredByConfig(Cfg, ResolvedPath)) {
13
Taking false branch
238 dlog("IncludeCleaner: not diagnosing missing include {0}, filtered by "do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType
(::clang::clangd::detail::debugType("clang-tools-extra/clangd/IncludeCleaner.cpp"
))) { ::clang::clangd::detail::log(Logger::Debug, "IncludeCleaner: not diagnosing missing include {0}, filtered by "
"config", ResolvedPath); } } while (false)
239 "config",do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType
(::clang::clangd::detail::debugType("clang-tools-extra/clangd/IncludeCleaner.cpp"
))) { ::clang::clangd::detail::log(Logger::Debug, "IncludeCleaner: not diagnosing missing include {0}, filtered by "
"config", ResolvedPath); } } while (false)
240 ResolvedPath)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType
(::clang::clangd::detail::debugType("clang-tools-extra/clangd/IncludeCleaner.cpp"
))) { ::clang::clangd::detail::log(Logger::Debug, "IncludeCleaner: not diagnosing missing include {0}, filtered by "
"config", ResolvedPath); } } while (false)
;
241 continue;
242 }
243
244 std::string Spelling =
245 spellHeader(AST, MainFile, SymbolWithMissingInclude.Providers.front());
246 llvm::StringRef HeaderRef{Spelling};
247 bool Angled = HeaderRef.starts_with("<");
248 // We might suggest insertion of an existing include in edge cases, e.g.,
249 // include is present in a PP-disabled region, or spelling of the header
250 // turns out to be the same as one of the unresolved includes in the
251 // main file.
252 std::optional<tooling::Replacement> Replacement = HeaderIncludes.insert(
253 HeaderRef.trim("\"<>"), Angled, tooling::IncludeDirective::Include);
254 if (!Replacement.has_value())
14
Assuming the condition is false
15
Taking false branch
255 continue;
256
257 Diag &D = Result.emplace_back();
258 D.Message =
259 llvm::formatv("No header providing \"{0}\" is directly included",
260 getSymbolName(SymbolWithMissingInclude.Symbol));
16
Calling 'getSymbolName'
261 D.Name = "missing-includes";
262 D.Source = Diag::DiagSource::Clangd;
263 D.File = AST.tuPath();
264 D.InsideMainFile = true;
265 D.Severity = DiagnosticsEngine::Warning;
266 D.Range = clangd::Range{
267 offsetToPosition(Code,
268 SymbolWithMissingInclude.SymRefRange.beginOffset()),
269 offsetToPosition(Code,
270 SymbolWithMissingInclude.SymRefRange.endOffset())};
271 auto &F = D.Fixes.emplace_back();
272 F.Message = "#include " + Spelling;
273 TextEdit Edit = replacementToEdit(Code, *Replacement);
274 F.Edits.emplace_back(std::move(Edit));
275 }
276 return Result;
277}
278
279std::vector<Diag> generateUnusedIncludeDiagnostics(
280 PathRef FileName, llvm::ArrayRef<const Inclusion *> UnusedIncludes,
281 llvm::StringRef Code) {
282 std::vector<Diag> Result;
283 const Config &Cfg = Config::current();
284 if (Cfg.Diagnostics.UnusedIncludes == Config::IncludesPolicy::None ||
285 Cfg.Diagnostics.SuppressAll ||
286 Cfg.Diagnostics.Suppress.contains("unused-includes")) {
287 return Result;
288 }
289 for (const auto *Inc : UnusedIncludes) {
290 Diag &D = Result.emplace_back();
291 D.Message =
292 llvm::formatv("included header {0} is not used directly",
293 llvm::sys::path::filename(
294 Inc->Written.substr(1, Inc->Written.size() - 2),
295 llvm::sys::path::Style::posix));
296 D.Name = "unused-includes";
297 D.Source = Diag::DiagSource::Clangd;
298 D.File = FileName;
299 D.InsideMainFile = true;
300 D.Severity = DiagnosticsEngine::Warning;
301 D.Tags.push_back(Unnecessary);
302 D.Range = getDiagnosticRange(Code, Inc->HashOffset);
303 // FIXME(kirillbobyrev): Removing inclusion might break the code if the
304 // used headers are only reachable transitively through this one. Suggest
305 // including them directly instead.
306 // FIXME(kirillbobyrev): Add fix suggestion for adding IWYU pragmas
307 // (keep/export) remove the warning once we support IWYU pragmas.
308 auto &F = D.Fixes.emplace_back();
309 F.Message = "remove #include directive";
310 F.Edits.emplace_back();
311 F.Edits.back().range.start.line = Inc->HashLine;
312 F.Edits.back().range.end.line = Inc->HashLine + 1;
313 }
314 return Result;
315}
316} // namespace
317
318
319std::vector<const Inclusion *>
320getUnused(ParsedAST &AST,
321 const llvm::DenseSet<IncludeStructure::HeaderID> &ReferencedFiles,
322 const llvm::StringSet<> &ReferencedPublicHeaders) {
323 trace::Span Tracer("IncludeCleaner::getUnused");
324 const Config &Cfg = Config::current();
325 std::vector<const Inclusion *> Unused;
326 for (const Inclusion &MFI : AST.getIncludeStructure().MainFileIncludes) {
327 if (!MFI.HeaderID)
328 continue;
329 if (ReferencedPublicHeaders.contains(MFI.Written))
330 continue;
331 auto IncludeID = static_cast<IncludeStructure::HeaderID>(*MFI.HeaderID);
332 bool Used = ReferencedFiles.contains(IncludeID);
333 if (!Used && !mayConsiderUnused(MFI, AST, Cfg, AST.getPragmaIncludes())) {
334 dlog("{0} was not used, but is not eligible to be diagnosed as unused",do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType
(::clang::clangd::detail::debugType("clang-tools-extra/clangd/IncludeCleaner.cpp"
))) { ::clang::clangd::detail::log(Logger::Debug, "{0} was not used, but is not eligible to be diagnosed as unused"
, MFI.Written); } } while (false)
335 MFI.Written)do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType
(::clang::clangd::detail::debugType("clang-tools-extra/clangd/IncludeCleaner.cpp"
))) { ::clang::clangd::detail::log(Logger::Debug, "{0} was not used, but is not eligible to be diagnosed as unused"
, MFI.Written); } } while (false)
;
336 continue;
337 }
338 if (!Used)
339 Unused.push_back(&MFI);
340 dlog("{0} is {1}", MFI.Written, Used ? "USED" : "UNUSED")do { if (::llvm::DebugFlag && ::llvm::isCurrentDebugType
(::clang::clangd::detail::debugType("clang-tools-extra/clangd/IncludeCleaner.cpp"
))) { ::clang::clangd::detail::log(Logger::Debug, "{0} is {1}"
, MFI.Written, Used ? "USED" : "UNUSED"); } } while (false)
;
341 }
342 return Unused;
343}
344
345IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST) {
346 const auto &SM = AST.getSourceManager();
347 const auto &Includes = AST.getIncludeStructure();
348 include_cleaner::Includes ConvertedIncludes =
349 convertIncludes(SM, Includes.MainFileIncludes);
350 const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID());
351
352 std::vector<include_cleaner::SymbolReference> Macros =
353 collectMacroReferences(AST);
354 std::vector<MissingIncludeDiagInfo> MissingIncludes;
355 llvm::DenseSet<IncludeStructure::HeaderID> Used;
356 trace::Span Tracer("include_cleaner::walkUsed");
357 include_cleaner::walkUsed(
358 AST.getLocalTopLevelDecls(), /*MacroRefs=*/Macros,
359 AST.getPragmaIncludes(), SM,
360 [&](const include_cleaner::SymbolReference &Ref,
361 llvm::ArrayRef<include_cleaner::Header> Providers) {
362 bool Satisfied = false;
363 for (const auto &H : Providers) {
364 if (H.kind() == include_cleaner::Header::Physical &&
365 H.physical() == MainFile) {
366 Satisfied = true;
367 continue;
368 }
369 for (auto *Inc : ConvertedIncludes.match(H)) {
370 Satisfied = true;
371 auto HeaderID = Includes.getID(Inc->Resolved);
372 assert(HeaderID.has_value() &&(static_cast <bool> (HeaderID.has_value() && "ConvertedIncludes only contains resolved includes."
) ? void (0) : __assert_fail ("HeaderID.has_value() && \"ConvertedIncludes only contains resolved includes.\""
, "clang-tools-extra/clangd/IncludeCleaner.cpp", 373, __extension__
__PRETTY_FUNCTION__))
373 "ConvertedIncludes only contains resolved includes.")(static_cast <bool> (HeaderID.has_value() && "ConvertedIncludes only contains resolved includes."
) ? void (0) : __assert_fail ("HeaderID.has_value() && \"ConvertedIncludes only contains resolved includes.\""
, "clang-tools-extra/clangd/IncludeCleaner.cpp", 373, __extension__
__PRETTY_FUNCTION__))
;
374 Used.insert(*HeaderID);
375 }
376 }
377
378 if (Satisfied || Providers.empty() ||
379 Ref.RT != include_cleaner::RefType::Explicit)
380 return;
381
382 auto &Tokens = AST.getTokens();
383 auto SpelledForExpanded =
384 Tokens.spelledForExpanded(Tokens.expandedTokens(Ref.RefLocation));
385 if (!SpelledForExpanded)
386 return;
387
388 auto Range = syntax::Token::range(SM, SpelledForExpanded->front(),
389 SpelledForExpanded->back());
390 MissingIncludeDiagInfo DiagInfo{Ref.Target, Range, Providers};
391 MissingIncludes.push_back(std::move(DiagInfo));
392 });
393 std::vector<const Inclusion *> UnusedIncludes =
394 getUnused(AST, Used, /*ReferencedPublicHeaders*/ {});
395 return {std::move(UnusedIncludes), std::move(MissingIncludes)};
396}
397
398std::vector<Diag> issueIncludeCleanerDiagnostics(ParsedAST &AST,
399 llvm::StringRef Code) {
400 // Interaction is only polished for C/CPP.
401 if (AST.getLangOpts().ObjC)
1
Assuming field 'ObjC' is 0
2
Taking false branch
402 return {};
403
404 trace::Span Tracer("IncludeCleaner::issueIncludeCleanerDiagnostics");
405
406 const Config &Cfg = Config::current();
407 IncludeCleanerFindings Findings;
408 if (Cfg.Diagnostics.MissingIncludes == Config::IncludesPolicy::Strict ||
3
Assuming field 'MissingIncludes' is not equal to Strict
5
Taking false branch
409 Cfg.Diagnostics.UnusedIncludes == Config::IncludesPolicy::Strict) {
4
Assuming field 'UnusedIncludes' is not equal to Strict
410 // will need include-cleaner results, call it once
411 Findings = computeIncludeCleanerFindings(AST);
412 }
413
414 std::vector<Diag> Result = generateUnusedIncludeDiagnostics(
415 AST.tuPath(), Findings.UnusedIncludes, Code);
416 llvm::move(
417 generateMissingIncludeDiagnostics(AST, Findings.MissingIncludes, Code),
6
Calling 'generateMissingIncludeDiagnostics'
418 std::back_inserter(Result));
419 return Result;
420}
421
422} // namespace clangd
423} // namespace clang