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