1 //===-- IncludeFixerContext.cpp - Include fixer context ---------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "IncludeFixerContext.h"
11 #include <algorithm>
12
13 namespace clang {
14 namespace include_fixer {
15
16 namespace {
17
18 // Splits a multiply qualified names (e.g. a::b::c).
19 llvm::SmallVector<llvm::StringRef, 8>
SplitQualifiers(llvm::StringRef StringQualifiers)20 SplitQualifiers(llvm::StringRef StringQualifiers) {
21 llvm::SmallVector<llvm::StringRef, 8> Qualifiers;
22 StringQualifiers.split(Qualifiers, "::");
23 return Qualifiers;
24 }
25
createQualifiedNameForReplacement(llvm::StringRef RawSymbolName,llvm::StringRef SymbolScopedQualifiersName,const find_all_symbols::SymbolInfo & MatchedSymbol)26 std::string createQualifiedNameForReplacement(
27 llvm::StringRef RawSymbolName,
28 llvm::StringRef SymbolScopedQualifiersName,
29 const find_all_symbols::SymbolInfo &MatchedSymbol) {
30 // No need to add missing qualifiers if SymbolIndentifer has a global scope
31 // operator "::".
32 if (RawSymbolName.startswith("::"))
33 return RawSymbolName;
34
35 std::string QualifiedName = MatchedSymbol.getQualifiedName();
36
37 // For nested classes, the qualified name constructed from database misses
38 // some stripped qualifiers, because when we search a symbol in database,
39 // we strip qualifiers from the end until we find a result. So append the
40 // missing stripped qualifiers here.
41 //
42 // Get stripped qualifiers.
43 auto SymbolQualifiers = SplitQualifiers(RawSymbolName);
44 std::string StrippedQualifiers;
45 while (!SymbolQualifiers.empty() &&
46 !llvm::StringRef(QualifiedName).endswith(SymbolQualifiers.back())) {
47 StrippedQualifiers =
48 "::" + SymbolQualifiers.back().str() + StrippedQualifiers;
49 SymbolQualifiers.pop_back();
50 }
51 // Append the missing stripped qualifiers.
52 std::string FullyQualifiedName = QualifiedName + StrippedQualifiers;
53
54 // Try to find and skip the common prefix qualifiers.
55 auto FullySymbolQualifiers = SplitQualifiers(FullyQualifiedName);
56 auto ScopedQualifiers = SplitQualifiers(SymbolScopedQualifiersName);
57 auto FullySymbolQualifiersIter = FullySymbolQualifiers.begin();
58 auto SymbolScopedQualifiersIter = ScopedQualifiers.begin();
59 while (FullySymbolQualifiersIter != FullySymbolQualifiers.end() &&
60 SymbolScopedQualifiersIter != ScopedQualifiers.end()) {
61 if (*FullySymbolQualifiersIter != *SymbolScopedQualifiersIter)
62 break;
63 ++FullySymbolQualifiersIter;
64 ++SymbolScopedQualifiersIter;
65 }
66 std::string Result;
67 for (; FullySymbolQualifiersIter != FullySymbolQualifiers.end();
68 ++FullySymbolQualifiersIter) {
69 if (!Result.empty())
70 Result += "::";
71 Result += *FullySymbolQualifiersIter;
72 }
73 return Result;
74 }
75
76 } // anonymous namespace
77
IncludeFixerContext(StringRef FilePath,std::vector<QuerySymbolInfo> QuerySymbols,std::vector<find_all_symbols::SymbolInfo> Symbols)78 IncludeFixerContext::IncludeFixerContext(
79 StringRef FilePath, std::vector<QuerySymbolInfo> QuerySymbols,
80 std::vector<find_all_symbols::SymbolInfo> Symbols)
81 : FilePath(FilePath), QuerySymbolInfos(std::move(QuerySymbols)),
82 MatchedSymbols(std::move(Symbols)) {
83 // Remove replicated QuerySymbolInfos with the same range.
84 //
85 // QuerySymbolInfos may contain replicated elements. Because CorrectTypo
86 // callback doesn't always work as we expected. In somecases, it will be
87 // triggered at the same position or unidentified symbol multiple times.
88 std::sort(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
89 [&](const QuerySymbolInfo &A, const QuerySymbolInfo &B) {
90 return std::make_pair(A.Range.getOffset(), A.Range.getLength()) <
91 std::make_pair(B.Range.getOffset(), B.Range.getLength());
92 });
93 QuerySymbolInfos.erase(
94 std::unique(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
95 [](const QuerySymbolInfo &A, const QuerySymbolInfo &B) {
96 return A.Range == B.Range;
97 }),
98 QuerySymbolInfos.end());
99 for (const auto &Symbol : MatchedSymbols) {
100 HeaderInfos.push_back(
101 {Symbol.getFilePath().str(),
102 createQualifiedNameForReplacement(
103 QuerySymbolInfos.front().RawIdentifier,
104 QuerySymbolInfos.front().ScopedQualifiers, Symbol)});
105 }
106 // Deduplicate header infos.
107 HeaderInfos.erase(std::unique(HeaderInfos.begin(), HeaderInfos.end(),
108 [](const HeaderInfo &A, const HeaderInfo &B) {
109 return A.Header == B.Header &&
110 A.QualifiedName == B.QualifiedName;
111 }),
112 HeaderInfos.end());
113 }
114
115 } // include_fixer
116 } // clang
117