|  | //===-- IncludeFixerContext.cpp - Include fixer context ---------*- C++ -*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "IncludeFixerContext.h" | 
|  | #include "llvm/ADT/STLExtras.h" | 
|  |  | 
|  | namespace clang { | 
|  | namespace include_fixer { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Splits a multiply qualified names (e.g. a::b::c). | 
|  | llvm::SmallVector<llvm::StringRef, 8> | 
|  | SplitQualifiers(llvm::StringRef StringQualifiers) { | 
|  | llvm::SmallVector<llvm::StringRef, 8> Qualifiers; | 
|  | StringQualifiers.split(Qualifiers, "::"); | 
|  | return Qualifiers; | 
|  | } | 
|  |  | 
|  | std::string createQualifiedNameForReplacement( | 
|  | llvm::StringRef RawSymbolName, | 
|  | llvm::StringRef SymbolScopedQualifiersName, | 
|  | const find_all_symbols::SymbolInfo &MatchedSymbol) { | 
|  | // No need to add missing qualifiers if SymbolIdentifier has a global scope | 
|  | // operator "::". | 
|  | if (RawSymbolName.starts_with("::")) | 
|  | return std::string(RawSymbolName); | 
|  |  | 
|  | std::string QualifiedName = MatchedSymbol.getQualifiedName(); | 
|  |  | 
|  | // For nested classes, the qualified name constructed from database misses | 
|  | // some stripped qualifiers, because when we search a symbol in database, | 
|  | // we strip qualifiers from the end until we find a result. So append the | 
|  | // missing stripped qualifiers here. | 
|  | // | 
|  | // Get stripped qualifiers. | 
|  | auto SymbolQualifiers = SplitQualifiers(RawSymbolName); | 
|  | std::string StrippedQualifiers; | 
|  | while (!SymbolQualifiers.empty() && | 
|  | !llvm::StringRef(QualifiedName).ends_with(SymbolQualifiers.back())) { | 
|  | StrippedQualifiers = | 
|  | "::" + SymbolQualifiers.back().str() + StrippedQualifiers; | 
|  | SymbolQualifiers.pop_back(); | 
|  | } | 
|  | // Append the missing stripped qualifiers. | 
|  | std::string FullyQualifiedName = QualifiedName + StrippedQualifiers; | 
|  |  | 
|  | // Try to find and skip the common prefix qualifiers. | 
|  | auto FullySymbolQualifiers = SplitQualifiers(FullyQualifiedName); | 
|  | auto ScopedQualifiers = SplitQualifiers(SymbolScopedQualifiersName); | 
|  | auto FullySymbolQualifiersIter = FullySymbolQualifiers.begin(); | 
|  | auto SymbolScopedQualifiersIter = ScopedQualifiers.begin(); | 
|  | while (FullySymbolQualifiersIter != FullySymbolQualifiers.end() && | 
|  | SymbolScopedQualifiersIter != ScopedQualifiers.end()) { | 
|  | if (*FullySymbolQualifiersIter != *SymbolScopedQualifiersIter) | 
|  | break; | 
|  | ++FullySymbolQualifiersIter; | 
|  | ++SymbolScopedQualifiersIter; | 
|  | } | 
|  | std::string Result; | 
|  | for (; FullySymbolQualifiersIter != FullySymbolQualifiers.end(); | 
|  | ++FullySymbolQualifiersIter) { | 
|  | if (!Result.empty()) | 
|  | Result += "::"; | 
|  | Result += *FullySymbolQualifiersIter; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | } // anonymous namespace | 
|  |  | 
|  | IncludeFixerContext::IncludeFixerContext( | 
|  | StringRef FilePath, std::vector<QuerySymbolInfo> QuerySymbols, | 
|  | std::vector<find_all_symbols::SymbolInfo> Symbols) | 
|  | : FilePath(FilePath), QuerySymbolInfos(std::move(QuerySymbols)), | 
|  | MatchedSymbols(std::move(Symbols)) { | 
|  | // Remove replicated QuerySymbolInfos with the same range. | 
|  | // | 
|  | // QuerySymbolInfos may contain replicated elements. Because CorrectTypo | 
|  | // callback doesn't always work as we expected. In somecases, it will be | 
|  | // triggered at the same position or unidentified symbol multiple times. | 
|  | llvm::sort(QuerySymbolInfos, | 
|  | [&](const QuerySymbolInfo &A, const QuerySymbolInfo &B) { | 
|  | return std::make_pair(A.Range.getOffset(), A.Range.getLength()) < | 
|  | std::make_pair(B.Range.getOffset(), B.Range.getLength()); | 
|  | }); | 
|  | QuerySymbolInfos.erase( | 
|  | llvm::unique(QuerySymbolInfos, | 
|  | [](const QuerySymbolInfo &A, const QuerySymbolInfo &B) { | 
|  | return A.Range == B.Range; | 
|  | }), | 
|  | QuerySymbolInfos.end()); | 
|  | for (const auto &Symbol : MatchedSymbols) { | 
|  | HeaderInfos.push_back( | 
|  | {Symbol.getFilePath().str(), | 
|  | createQualifiedNameForReplacement( | 
|  | QuerySymbolInfos.front().RawIdentifier, | 
|  | QuerySymbolInfos.front().ScopedQualifiers, Symbol)}); | 
|  | } | 
|  | // Deduplicate header infos. | 
|  | HeaderInfos.erase(llvm::unique(HeaderInfos, | 
|  | [](const HeaderInfo &A, const HeaderInfo &B) { | 
|  | return A.Header == B.Header && | 
|  | A.QualifiedName == B.QualifiedName; | 
|  | }), | 
|  | HeaderInfos.end()); | 
|  | } | 
|  |  | 
|  | } // include_fixer | 
|  | } // clang |