1 //===-- IncludeFixerContext.cpp - Include fixer context ---------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "IncludeFixerContext.h"
10 #include "llvm/ADT/STLExtras.h"
13 namespace include_fixer
{
17 // Splits a multiply qualified names (e.g. a::b::c).
18 llvm::SmallVector
<llvm::StringRef
, 8>
19 SplitQualifiers(llvm::StringRef StringQualifiers
) {
20 llvm::SmallVector
<llvm::StringRef
, 8> Qualifiers
;
21 StringQualifiers
.split(Qualifiers
, "::");
25 std::string
createQualifiedNameForReplacement(
26 llvm::StringRef RawSymbolName
,
27 llvm::StringRef SymbolScopedQualifiersName
,
28 const find_all_symbols::SymbolInfo
&MatchedSymbol
) {
29 // No need to add missing qualifiers if SymbolIdentifier has a global scope
31 if (RawSymbolName
.starts_with("::"))
32 return std::string(RawSymbolName
);
34 std::string QualifiedName
= MatchedSymbol
.getQualifiedName();
36 // For nested classes, the qualified name constructed from database misses
37 // some stripped qualifiers, because when we search a symbol in database,
38 // we strip qualifiers from the end until we find a result. So append the
39 // missing stripped qualifiers here.
41 // Get stripped qualifiers.
42 auto SymbolQualifiers
= SplitQualifiers(RawSymbolName
);
43 std::string StrippedQualifiers
;
44 while (!SymbolQualifiers
.empty() &&
45 !llvm::StringRef(QualifiedName
).ends_with(SymbolQualifiers
.back())) {
47 "::" + SymbolQualifiers
.back().str() + StrippedQualifiers
;
48 SymbolQualifiers
.pop_back();
50 // Append the missing stripped qualifiers.
51 std::string FullyQualifiedName
= QualifiedName
+ StrippedQualifiers
;
53 // Try to find and skip the common prefix qualifiers.
54 auto FullySymbolQualifiers
= SplitQualifiers(FullyQualifiedName
);
55 auto ScopedQualifiers
= SplitQualifiers(SymbolScopedQualifiersName
);
56 auto FullySymbolQualifiersIter
= FullySymbolQualifiers
.begin();
57 auto SymbolScopedQualifiersIter
= ScopedQualifiers
.begin();
58 while (FullySymbolQualifiersIter
!= FullySymbolQualifiers
.end() &&
59 SymbolScopedQualifiersIter
!= ScopedQualifiers
.end()) {
60 if (*FullySymbolQualifiersIter
!= *SymbolScopedQualifiersIter
)
62 ++FullySymbolQualifiersIter
;
63 ++SymbolScopedQualifiersIter
;
66 for (; FullySymbolQualifiersIter
!= FullySymbolQualifiers
.end();
67 ++FullySymbolQualifiersIter
) {
70 Result
+= *FullySymbolQualifiersIter
;
75 } // anonymous namespace
77 IncludeFixerContext::IncludeFixerContext(
78 StringRef FilePath
, std::vector
<QuerySymbolInfo
> QuerySymbols
,
79 std::vector
<find_all_symbols::SymbolInfo
> Symbols
)
80 : FilePath(FilePath
), QuerySymbolInfos(std::move(QuerySymbols
)),
81 MatchedSymbols(std::move(Symbols
)) {
82 // Remove replicated QuerySymbolInfos with the same range.
84 // QuerySymbolInfos may contain replicated elements. Because CorrectTypo
85 // callback doesn't always work as we expected. In somecases, it will be
86 // triggered at the same position or unidentified symbol multiple times.
87 llvm::sort(QuerySymbolInfos
,
88 [&](const QuerySymbolInfo
&A
, const QuerySymbolInfo
&B
) {
89 return std::make_pair(A
.Range
.getOffset(), A
.Range
.getLength()) <
90 std::make_pair(B
.Range
.getOffset(), B
.Range
.getLength());
92 QuerySymbolInfos
.erase(
93 std::unique(QuerySymbolInfos
.begin(), QuerySymbolInfos
.end(),
94 [](const QuerySymbolInfo
&A
, const QuerySymbolInfo
&B
) {
95 return A
.Range
== B
.Range
;
97 QuerySymbolInfos
.end());
98 for (const auto &Symbol
: MatchedSymbols
) {
99 HeaderInfos
.push_back(
100 {Symbol
.getFilePath().str(),
101 createQualifiedNameForReplacement(
102 QuerySymbolInfos
.front().RawIdentifier
,
103 QuerySymbolInfos
.front().ScopedQualifiers
, Symbol
)});
105 // Deduplicate header infos.
106 HeaderInfos
.erase(std::unique(HeaderInfos
.begin(), HeaderInfos
.end(),
107 [](const HeaderInfo
&A
, const HeaderInfo
&B
) {
108 return A
.Header
== B
.Header
&&
109 A
.QualifiedName
== B
.QualifiedName
;