summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clangd/IncludeFixer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clangd/IncludeFixer.cpp')
-rw-r--r--clang-tools-extra/clangd/IncludeFixer.cpp113
1 files changed, 113 insertions, 0 deletions
diff --git a/clang-tools-extra/clangd/IncludeFixer.cpp b/clang-tools-extra/clangd/IncludeFixer.cpp
new file mode 100644
index 00000000000..0f498884653
--- /dev/null
+++ b/clang-tools-extra/clangd/IncludeFixer.cpp
@@ -0,0 +1,113 @@
+//===--- IncludeFixer.cpp ----------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeFixer.h"
+#include "AST.h"
+#include "Diagnostics.h"
+#include "Logger.h"
+#include "SourceCode.h"
+#include "Trace.h"
+#include "index/Index.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticSema.h"
+#include "llvm/ADT/None.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+
+namespace clang {
+namespace clangd {
+
+std::vector<Fix> IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel,
+ const clang::Diagnostic &Info) const {
+ if (IndexRequestCount >= IndexRequestLimit)
+ return {}; // Avoid querying index too many times in a single parse.
+ switch (Info.getID()) {
+ case diag::err_incomplete_type:
+ case diag::err_incomplete_member_access:
+ case diag::err_incomplete_base_class:
+ // Incomplete type diagnostics should have a QualType argument for the
+ // incomplete type.
+ for (unsigned Idx = 0; Idx < Info.getNumArgs(); ++Idx) {
+ if (Info.getArgKind(Idx) == DiagnosticsEngine::ak_qualtype) {
+ auto QT = QualType::getFromOpaquePtr((void *)Info.getRawArg(Idx));
+ if (const Type *T = QT.getTypePtrOrNull())
+ if (T->isIncompleteType())
+ return fixIncompleteType(*T);
+ }
+ }
+ }
+ return {};
+}
+
+std::vector<Fix> IncludeFixer::fixIncompleteType(const Type &T) const {
+ // Only handle incomplete TagDecl type.
+ const TagDecl *TD = T.getAsTagDecl();
+ if (!TD)
+ return {};
+ std::string TypeName = printQualifiedName(*TD);
+ trace::Span Tracer("Fix include for incomplete type");
+ SPAN_ATTACH(Tracer, "type", TypeName);
+ vlog("Trying to fix include for incomplete type {0}", TypeName);
+
+ auto ID = getSymbolID(TD);
+ if (!ID)
+ return {};
+ ++IndexRequestCount;
+ // FIXME: consider batching the requests for all diagnostics.
+ // FIXME: consider caching the lookup results.
+ LookupRequest Req;
+ Req.IDs.insert(*ID);
+ llvm::Optional<Symbol> Matched;
+ Index.lookup(Req, [&](const Symbol &Sym) {
+ if (Matched)
+ return;
+ Matched = Sym;
+ });
+
+ if (!Matched || Matched->IncludeHeaders.empty() || !Matched->Definition ||
+ Matched->CanonicalDeclaration.FileURI != Matched->Definition.FileURI)
+ return {};
+ return fixesForSymbol(*Matched);
+}
+
+std::vector<Fix> IncludeFixer::fixesForSymbol(const Symbol &Sym) const {
+ auto Inserted = [&](llvm::StringRef Header)
+ -> llvm::Expected<std::pair<std::string, bool>> {
+ auto ResolvedDeclaring =
+ toHeaderFile(Sym.CanonicalDeclaration.FileURI, File);
+ if (!ResolvedDeclaring)
+ return ResolvedDeclaring.takeError();
+ auto ResolvedInserted = toHeaderFile(Header, File);
+ if (!ResolvedInserted)
+ return ResolvedInserted.takeError();
+ return std::make_pair(
+ Inserter->calculateIncludePath(*ResolvedDeclaring, *ResolvedInserted),
+ Inserter->shouldInsertInclude(*ResolvedDeclaring, *ResolvedInserted));
+ };
+
+ std::vector<Fix> Fixes;
+ for (const auto &Inc : getRankedIncludes(Sym)) {
+ if (auto ToInclude = Inserted(Inc)) {
+ if (ToInclude->second)
+ if (auto Edit = Inserter->insert(ToInclude->first))
+ Fixes.push_back(
+ Fix{llvm::formatv("Add include {0} for symbol {1}{2}",
+ ToInclude->first, Sym.Scope, Sym.Name),
+ {std::move(*Edit)}});
+ } else {
+ vlog("Failed to calculate include insertion for {0} into {1}: {2}", File,
+ Inc, ToInclude.takeError());
+ }
+ }
+ return Fixes;
+}
+
+} // namespace clangd
+} // namespace clang
OpenPOWER on IntegriCloud