summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/include-fixer/SymbolIndexManager.cpp
blob: 1800fab41999ab17b3f88e7eb8d20c8c74d0e25f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//===-- SymbolIndexManager.cpp - Managing multiple SymbolIndices-*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "SymbolIndexManager.h"
#include "find-all-symbols/SymbolInfo.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Debug.h"

#define DEBUG_TYPE "include-fixer"

namespace clang {
namespace include_fixer {

using clang::find_all_symbols::SymbolInfo;

/// Sorts and uniques SymbolInfos based on the popularity info in SymbolInfo.
static void rankByPopularity(std::vector<SymbolInfo> &Symbols) {
  // First collect occurrences per header file.
  std::map<llvm::StringRef, unsigned> HeaderPopularity;
  for (const SymbolInfo &Symbol : Symbols) {
    unsigned &Popularity = HeaderPopularity[Symbol.getFilePath()];
    Popularity = std::max(Popularity, Symbol.getNumOccurrences());
  }

  // Sort by the gathered popularities. Use file name as a tie breaker so we can
  // deduplicate.
  std::sort(Symbols.begin(), Symbols.end(),
            [&](const SymbolInfo &A, const SymbolInfo &B) {
              auto APop = HeaderPopularity[A.getFilePath()];
              auto BPop = HeaderPopularity[B.getFilePath()];
              if (APop != BPop)
                return APop > BPop;
              return A.getFilePath() < B.getFilePath();
            });

  // Deduplicate based on the file name. They will have the same popularity and
  // we don't want to suggest the same header twice.
  Symbols.erase(std::unique(Symbols.begin(), Symbols.end(),
                            [](const SymbolInfo &A, const SymbolInfo &B) {
                              return A.getFilePath() == B.getFilePath();
                            }),
                Symbols.end());
}

std::vector<std::string>
SymbolIndexManager::search(llvm::StringRef Identifier) const {
  // The identifier may be fully qualified, so split it and get all the context
  // names.
  llvm::SmallVector<llvm::StringRef, 8> Names;
  Identifier.split(Names, "::");

  bool IsFullyQualified = false;
  if (Identifier.startswith("::")) {
    Names.erase(Names.begin()); // Drop first (empty) element.
    IsFullyQualified = true;
  }

  // As long as we don't find a result keep stripping name parts from the end.
  // This is to support nested classes which aren't recorded in the database.
  // Eventually we will either hit a class (namespaces aren't in the database
  // either) and can report that result.
  std::vector<std::string> Results;
  while (Results.empty() && !Names.empty()) {
    std::vector<clang::find_all_symbols::SymbolInfo> Symbols;
    for (const auto &DB : SymbolIndices) {
      auto Res = DB->search(Names.back().str());
      Symbols.insert(Symbols.end(), Res.begin(), Res.end());
    }

    DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got "
                       << Symbols.size() << " results...\n");

    rankByPopularity(Symbols);

    for (const auto &Symbol : Symbols) {
      // Match the identifier name without qualifier.
      if (Symbol.getName() == Names.back()) {
        bool IsMatched = true;
        auto SymbolContext = Symbol.getContexts().begin();
        auto IdentiferContext = Names.rbegin() + 1; // Skip identifier name.
        // Match the remaining context names.
        while (IdentiferContext != Names.rend() &&
               SymbolContext != Symbol.getContexts().end()) {
          if (SymbolContext->second == *IdentiferContext) {
            ++IdentiferContext;
            ++SymbolContext;
          } else if (SymbolContext->first ==
                     find_all_symbols::SymbolInfo::ContextType::EnumDecl) {
            // Skip non-scoped enum context.
            ++SymbolContext;
          } else {
            IsMatched = false;
            break;
          }
        }

        // If the name was qualified we only want to add results if we evaluated
        // all contexts.
        if (IsFullyQualified)
          IsMatched &= (SymbolContext == Symbol.getContexts().end());

        // FIXME: Support full match. At this point, we only find symbols in
        // database which end with the same contexts with the identifier.
        if (IsMatched && IdentiferContext == Names.rend()) {
          // FIXME: file path should never be in the form of <...> or "...", but
          // the unit test with fixed database use <...> file path, which might
          // need to be changed.
          // FIXME: if the file path is a system header name, we want to use
          // angle brackets.
          std::string FilePath = Symbol.getFilePath().str();
          Results.push_back((FilePath[0] == '"' || FilePath[0] == '<')
                                ? FilePath
                                : "\"" + FilePath + "\"");
        }
      }
    }
    Names.pop_back();
  }

  return Results;
}

} // namespace include_fixer
} // namespace clang
OpenPOWER on IntegriCloud