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 "llvm/ADT/STLExtras.h" |
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> |
19 | SplitQualifiers(llvm::StringRef StringQualifiers) { |
20 | llvm::SmallVector<llvm::StringRef, 8> Qualifiers; |
21 | StringQualifiers.split(A&: Qualifiers, Separator: "::" ); |
22 | return Qualifiers; |
23 | } |
24 | |
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.starts_with(Prefix: "::" )) |
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(StringQualifiers: RawSymbolName); |
43 | std::string StrippedQualifiers; |
44 | while (!SymbolQualifiers.empty() && |
45 | !llvm::StringRef(QualifiedName).ends_with(Suffix: 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(StringQualifiers: FullyQualifiedName); |
55 | auto ScopedQualifiers = SplitQualifiers(StringQualifiers: 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 | |
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 | llvm::sort(C&: QuerySymbolInfos, |
88 | Comp: [&](const QuerySymbolInfo &A, const QuerySymbolInfo &B) { |
89 | return std::make_pair(x: A.Range.getOffset(), y: A.Range.getLength()) < |
90 | std::make_pair(x: B.Range.getOffset(), y: B.Range.getLength()); |
91 | }); |
92 | QuerySymbolInfos.erase( |
93 | first: std::unique(first: QuerySymbolInfos.begin(), last: QuerySymbolInfos.end(), |
94 | binary_pred: [](const QuerySymbolInfo &A, const QuerySymbolInfo &B) { |
95 | return A.Range == B.Range; |
96 | }), |
97 | last: QuerySymbolInfos.end()); |
98 | for (const auto &Symbol : MatchedSymbols) { |
99 | HeaderInfos.push_back( |
100 | x: {.Header: Symbol.getFilePath().str(), |
101 | .QualifiedName: createQualifiedNameForReplacement( |
102 | RawSymbolName: QuerySymbolInfos.front().RawIdentifier, |
103 | SymbolScopedQualifiersName: QuerySymbolInfos.front().ScopedQualifiers, MatchedSymbol: Symbol)}); |
104 | } |
105 | // Deduplicate header infos. |
106 | HeaderInfos.erase(first: std::unique(first: HeaderInfos.begin(), last: HeaderInfos.end(), |
107 | binary_pred: [](const HeaderInfo &A, const HeaderInfo &B) { |
108 | return A.Header == B.Header && |
109 | A.QualifiedName == B.QualifiedName; |
110 | }), |
111 | last: HeaderInfos.end()); |
112 | } |
113 | |
114 | } // include_fixer |
115 | } // clang |
116 | |