| //===-- 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( | |
| std::unique(QuerySymbolInfos.begin(), QuerySymbolInfos.end(), | |
| [](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(std::unique(HeaderInfos.begin(), HeaderInfos.end(), | |
| [](const HeaderInfo &A, const HeaderInfo &B) { | |
| return A.Header == B.Header && | |
| A.QualifiedName == B.QualifiedName; | |
| }), | |
| HeaderInfos.end()); | |
| } | |
| } // include_fixer | |
| } // clang |