1 //===-- IncludeFixerContext.cpp - Include fixer context ---------*- 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 "IncludeFixerContext.h"
10 #include <algorithm>
11 
12 namespace clang {
13 namespace include_fixer {
14 
15 namespace {
16 
17 // Splits a multiply qualified names (e.g. a::b::c).
18 llvm::SmallVector<llvm::StringRef, 8>
SplitQualifiers(llvm::StringRef StringQualifiers)19 SplitQualifiers(llvm::StringRef StringQualifiers) {
20   llvm::SmallVector<llvm::StringRef, 8> Qualifiers;
21   StringQualifiers.split(Qualifiers, "::");
22   return Qualifiers;
23 }
24 
createQualifiedNameForReplacement(llvm::StringRef RawSymbolName,llvm::StringRef SymbolScopedQualifiersName,const find_all_symbols::SymbolInfo & MatchedSymbol)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
30   // operator "::".
31   if (RawSymbolName.startswith("::"))
32     return std::string(RawSymbolName);
33 
34   std::string QualifiedName = MatchedSymbol.getQualifiedName();
35 
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.
40   //
41   // Get stripped qualifiers.
42   auto SymbolQualifiers = SplitQualifiers(RawSymbolName);
43   std::string StrippedQualifiers;
44   while (!SymbolQualifiers.empty() &&
45          !llvm::StringRef(QualifiedName).endswith(SymbolQualifiers.back())) {
46     StrippedQualifiers =
47         "::" + SymbolQualifiers.back().str() + StrippedQualifiers;
48     SymbolQualifiers.pop_back();
49   }
50   // Append the missing stripped qualifiers.
51   std::string FullyQualifiedName = QualifiedName + StrippedQualifiers;
52 
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)
61       break;
62     ++FullySymbolQualifiersIter;
63     ++SymbolScopedQualifiersIter;
64   }
65   std::string Result;
66   for (; FullySymbolQualifiersIter != FullySymbolQualifiers.end();
67        ++FullySymbolQualifiersIter) {
68     if (!Result.empty())
69       Result += "::";
70     Result += *FullySymbolQualifiersIter;
71   }
72   return Result;
73 }
74 
75 } // anonymous namespace
76 
IncludeFixerContext(StringRef FilePath,std::vector<QuerySymbolInfo> QuerySymbols,std::vector<find_all_symbols::SymbolInfo> Symbols)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.
83   //
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   std::sort(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
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());
91             });
92   QuerySymbolInfos.erase(
93       std::unique(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
94                   [](const QuerySymbolInfo &A, const QuerySymbolInfo &B) {
95                     return A.Range == B.Range;
96                   }),
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)});
104   }
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;
110                                 }),
111                     HeaderInfos.end());
112 }
113 
114 } // include_fixer
115 } // clang
116