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> 19 SplitQualifiers(llvm::StringRef StringQualifiers) { 20 llvm::SmallVector<llvm::StringRef, 8> Qualifiers; 21 StringQualifiers.split(Qualifiers, "::"); 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 SymbolIndentifer has a global scope 30 // operator "::". 31 if (RawSymbolName.startswith("::")) 32 return 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 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