[include-fixer] Add usage count to find-all-symbols.

Summary:
Add usage count to find-all-symbols.

FindAllSymbols now finds (most!) main-file usages of the discovered symbols.
The per-TU map output has NumUses=0 or 1 (only one use per file is counted).
The reducer aggregates these to find the number of files that use a symbol.

The NumOccurrences is now set to 1 in the mapper rather than being inferred by
the reducer, for consistency.

The idea here is to use NumUses for ranking: intuitively number of files that
use a symbol is more meaningful than number of files that include the header.

Reviewers: hokein, bkramer

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D30210

llvm-svn: 296446
This commit is contained in:
Sam McCall 2017-02-28 08:13:15 +00:00
parent 427215ce5e
commit b27dc2245f
23 changed files with 500 additions and 254 deletions

View File

@ -9,18 +9,18 @@
#include "InMemorySymbolIndex.h"
using clang::find_all_symbols::SymbolInfo;
using clang::find_all_symbols::SymbolAndSignals;
namespace clang {
namespace include_fixer {
InMemorySymbolIndex::InMemorySymbolIndex(
const std::vector<SymbolInfo> &Symbols) {
const std::vector<SymbolAndSignals> &Symbols) {
for (const auto &Symbol : Symbols)
LookupTable[Symbol.getName()].push_back(Symbol);
LookupTable[Symbol.Symbol.getName()].push_back(Symbol);
}
std::vector<SymbolInfo>
std::vector<SymbolAndSignals>
InMemorySymbolIndex::search(llvm::StringRef Identifier) {
auto I = LookupTable.find(Identifier);
if (I != LookupTable.end())

View File

@ -21,13 +21,14 @@ namespace include_fixer {
/// Xref database with fixed content.
class InMemorySymbolIndex : public SymbolIndex {
public:
InMemorySymbolIndex(const std::vector<find_all_symbols::SymbolInfo> &Symbols);
InMemorySymbolIndex(
const std::vector<find_all_symbols::SymbolAndSignals> &Symbols);
std::vector<clang::find_all_symbols::SymbolInfo>
std::vector<find_all_symbols::SymbolAndSignals>
search(llvm::StringRef Identifier) override;
private:
std::map<std::string, std::vector<clang::find_all_symbols::SymbolInfo>>
std::map<std::string, std::vector<find_all_symbols::SymbolAndSignals>>
LookupTable;
};

View File

@ -334,8 +334,7 @@ IncludeFixerContext IncludeFixerSemaSource::getIncludeFixerContext(
SourceManager, HeaderSearch);
SymbolCandidates.emplace_back(Symbol.getName(), Symbol.getSymbolKind(),
MinimizedFilePath, Symbol.getLineNumber(),
Symbol.getContexts(),
Symbol.getNumOccurrences());
Symbol.getContexts());
}
return IncludeFixerContext(FilePath, QuerySymbolInfos, SymbolCandidates);
}

View File

@ -28,7 +28,7 @@ public:
/// \returns A list of `SymbolInfo` candidates.
// FIXME: Expose the type name so we can also insert using declarations (or
// fix the usage)
virtual std::vector<clang::find_all_symbols::SymbolInfo>
virtual std::vector<find_all_symbols::SymbolAndSignals>
search(llvm::StringRef Identifier) = 0;
};

View File

@ -19,7 +19,8 @@
namespace clang {
namespace include_fixer {
using clang::find_all_symbols::SymbolInfo;
using find_all_symbols::SymbolInfo;
using find_all_symbols::SymbolAndSignals;
// Calculate a score based on whether we think the given header is closely
// related to the given source file.
@ -45,26 +46,26 @@ static double similarityScore(llvm::StringRef FileName,
return MaxSegments;
}
static void rank(std::vector<SymbolInfo> &Symbols,
static void rank(std::vector<SymbolAndSignals> &Symbols,
llvm::StringRef FileName) {
llvm::DenseMap<llvm::StringRef, double> Score;
for (const SymbolInfo &Symbol : Symbols) {
for (const auto &Symbol : Symbols) {
// Calculate a score from the similarity of the header the symbol is in
// with the current file and the popularity of the symbol.
double NewScore = similarityScore(FileName, Symbol.getFilePath()) *
(1.0 + std::log2(1 + Symbol.getNumOccurrences()));
double &S = Score[Symbol.getFilePath()];
double NewScore = similarityScore(FileName, Symbol.Symbol.getFilePath()) *
(1.0 + std::log2(1 + Symbol.Signals.Seen));
double &S = Score[Symbol.Symbol.getFilePath()];
S = std::max(S, NewScore);
}
// Sort by the gathered scores. Use file name as a tie breaker so we can
// deduplicate.
std::sort(Symbols.begin(), Symbols.end(),
[&](const SymbolInfo &A, const SymbolInfo &B) {
auto AS = Score[A.getFilePath()];
auto BS = Score[B.getFilePath()];
[&](const SymbolAndSignals &A, const SymbolAndSignals &B) {
auto AS = Score[A.Symbol.getFilePath()];
auto BS = Score[B.Symbol.getFilePath()];
if (AS != BS)
return AS > BS;
return A.getFilePath() < B.getFilePath();
return A.Symbol.getFilePath() < B.Symbol.getFilePath();
});
}
@ -88,9 +89,9 @@ SymbolIndexManager::search(llvm::StringRef Identifier,
// Eventually we will either hit a class (namespaces aren't in the database
// either) and can report that result.
bool TookPrefix = false;
std::vector<clang::find_all_symbols::SymbolInfo> MatchedSymbols;
std::vector<SymbolAndSignals> MatchedSymbols;
do {
std::vector<clang::find_all_symbols::SymbolInfo> Symbols;
std::vector<SymbolAndSignals> Symbols;
for (const auto &DB : SymbolIndices) {
auto Res = DB.get()->search(Names.back());
Symbols.insert(Symbols.end(), Res.begin(), Res.end());
@ -99,7 +100,8 @@ SymbolIndexManager::search(llvm::StringRef Identifier,
DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got "
<< Symbols.size() << " results...\n");
for (const auto &Symbol : Symbols) {
for (auto &SymAndSig : Symbols) {
const SymbolInfo &Symbol = SymAndSig.Symbol;
// Match the identifier name without qualifier.
if (Symbol.getName() == Names.back()) {
bool IsMatched = true;
@ -139,7 +141,7 @@ SymbolIndexManager::search(llvm::StringRef Identifier,
Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Macro))
continue;
MatchedSymbols.push_back(Symbol);
MatchedSymbols.push_back(std::move(SymAndSig));
}
}
}
@ -148,7 +150,11 @@ SymbolIndexManager::search(llvm::StringRef Identifier,
} while (MatchedSymbols.empty() && !Names.empty() && IsNestedSearch);
rank(MatchedSymbols, FileName);
return MatchedSymbols;
// Strip signals, they are no longer needed.
std::vector<SymbolInfo> Res;
for (const auto &SymAndSig : MatchedSymbols)
Res.push_back(std::move(SymAndSig.Symbol));
return Res;
}
} // namespace include_fixer

View File

@ -17,6 +17,7 @@
#include <vector>
using clang::find_all_symbols::SymbolInfo;
using clang::find_all_symbols::SymbolAndSignals;
namespace clang {
namespace include_fixer {
@ -27,9 +28,8 @@ YamlSymbolIndex::createFromFile(llvm::StringRef FilePath) {
if (!Buffer)
return Buffer.getError();
return std::unique_ptr<YamlSymbolIndex>(
new YamlSymbolIndex(clang::find_all_symbols::ReadSymbolInfosFromYAML(
Buffer.get()->getBuffer())));
return std::unique_ptr<YamlSymbolIndex>(new YamlSymbolIndex(
find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer())));
}
llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
@ -47,10 +47,11 @@ YamlSymbolIndex::createFromDirectory(llvm::StringRef Directory,
return llvm::make_error_code(llvm::errc::no_such_file_or_directory);
}
std::vector<SymbolInfo> YamlSymbolIndex::search(llvm::StringRef Identifier) {
std::vector<SymbolInfo> Results;
std::vector<SymbolAndSignals>
YamlSymbolIndex::search(llvm::StringRef Identifier) {
std::vector<SymbolAndSignals> Results;
for (const auto &Symbol : Symbols) {
if (Symbol.getName() == Identifier)
if (Symbol.Symbol.getName() == Identifier)
Results.push_back(Symbol);
}
return Results;

View File

@ -29,15 +29,15 @@ public:
static llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
createFromDirectory(llvm::StringRef Directory, llvm::StringRef Name);
std::vector<clang::find_all_symbols::SymbolInfo>
std::vector<find_all_symbols::SymbolAndSignals>
search(llvm::StringRef Identifier) override;
private:
explicit YamlSymbolIndex(
std::vector<clang::find_all_symbols::SymbolInfo> Symbols)
std::vector<find_all_symbols::SymbolAndSignals> Symbols)
: Symbols(std::move(Symbols)) {}
std::vector<clang::find_all_symbols::SymbolInfo> Symbols;
std::vector<find_all_symbols::SymbolAndSignals> Symbols;
};
} // namespace include_fixer

View File

@ -13,24 +13,58 @@
#include "SymbolInfo.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Token.h"
#include "llvm/Support/Path.h"
namespace clang {
namespace find_all_symbols {
llvm::Optional<SymbolInfo>
FindAllMacros::CreateMacroSymbol(const Token &MacroNameTok,
const MacroInfo *info) {
std::string FilePath =
getIncludePath(*SM, info->getDefinitionLoc(), Collector);
if (FilePath.empty())
return llvm::None;
return SymbolInfo(MacroNameTok.getIdentifierInfo()->getName(),
SymbolInfo::SymbolKind::Macro, FilePath,
SM->getSpellingLineNumber(info->getDefinitionLoc()), {});
}
void FindAllMacros::MacroDefined(const Token &MacroNameTok,
const MacroDirective *MD) {
SourceLocation Loc = SM->getExpansionLoc(MacroNameTok.getLocation());
std::string FilePath = getIncludePath(*SM, Loc, Collector);
if (FilePath.empty()) return;
if (auto Symbol = CreateMacroSymbol(MacroNameTok, MD->getMacroInfo()))
++FileSymbols[*Symbol].Seen;
}
SymbolInfo Symbol(MacroNameTok.getIdentifierInfo()->getName(),
SymbolInfo::SymbolKind::Macro, FilePath,
SM->getSpellingLineNumber(Loc), {});
void FindAllMacros::MacroUsed(const Token &Name, const MacroDefinition &MD) {
if (!MD || !SM->isInMainFile(SM->getExpansionLoc(Name.getLocation())))
return;
if (auto Symbol = CreateMacroSymbol(Name, MD.getMacroInfo()))
++FileSymbols[*Symbol].Used;
}
Reporter->reportSymbol(SM->getFileEntryForID(SM->getMainFileID())->getName(),
Symbol);
void FindAllMacros::MacroExpands(const Token &MacroNameTok,
const MacroDefinition &MD, SourceRange Range,
const MacroArgs *Args) {
MacroUsed(MacroNameTok, MD);
}
void FindAllMacros::Ifdef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) {
MacroUsed(MacroNameTok, MD);
}
void FindAllMacros::Ifndef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) {
MacroUsed(MacroNameTok, MD);
}
void FindAllMacros::EndOfMainFile() {
Reporter->reportSymbols(SM->getFileEntryForID(SM->getMainFileID())->getName(),
FileSymbols);
FileSymbols.clear();
}
} // namespace find_all_symbols

View File

@ -16,6 +16,7 @@
#include "clang/Lex/PPCallbacks.h"
namespace clang {
class MacroInfo;
namespace find_all_symbols {
class HeaderMapCollector;
@ -32,7 +33,24 @@ public:
void MacroDefined(const Token &MacroNameTok,
const MacroDirective *MD) override;
void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
SourceRange Range, const MacroArgs *Args) override;
void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override;
void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
const MacroDefinition &MD) override;
void EndOfMainFile() override;
private:
llvm::Optional<SymbolInfo> CreateMacroSymbol(const Token &MacroNameTok,
const MacroInfo *MD);
// Not a callback, just a common path for all usage types.
void MacroUsed(const Token &Name, const MacroDefinition &MD);
SymbolInfo::SignalMap FileSymbols;
// Reporter for SymbolInfo.
SymbolReporter *const Reporter;
SourceManager *const SM;

View File

@ -154,64 +154,84 @@ void FindAllSymbols::registerMatchers(MatchFinder *MatchFinder) {
// The float parameter of the function pointer has an empty name, and its
// declaration context is an anonymous namespace; therefore, it won't be
// filtered out by our matchers above.
MatchFinder->addMatcher(varDecl(CommonFilter,
anyOf(ExternCMatcher, CCMatcher),
unless(parmVarDecl()))
.bind("decl"),
this);
auto Vars = varDecl(CommonFilter, anyOf(ExternCMatcher, CCMatcher),
unless(parmVarDecl()));
// Matchers for C-style record declarations in extern "C" {...}.
MatchFinder->addMatcher(
recordDecl(CommonFilter, ExternCMatcher, isDefinition()).bind("decl"),
this);
auto CRecords = recordDecl(CommonFilter, ExternCMatcher, isDefinition());
// Matchers for C++ record declarations.
auto CxxRecordDecl =
cxxRecordDecl(CommonFilter, CCMatcher, isDefinition());
MatchFinder->addMatcher(CxxRecordDecl.bind("decl"), this);
auto CXXRecords = cxxRecordDecl(CommonFilter, CCMatcher, isDefinition());
// Matchers for function declarations.
// We want to exclude friend declaration, but the `DeclContext` of a friend
// function declaration is not the class in which it is declared, so we need
// to explicitly check if the parent is a `friendDecl`.
MatchFinder->addMatcher(functionDecl(CommonFilter,
unless(hasParent(friendDecl())),
anyOf(ExternCMatcher, CCMatcher))
.bind("decl"),
this);
auto Functions = functionDecl(CommonFilter, unless(hasParent(friendDecl())),
anyOf(ExternCMatcher, CCMatcher));
// Matcher for typedef and type alias declarations.
//
// typedef and type alias can come from C-style headers and C++ heaeders.
// For C-style header, `DeclContxet` can be either `TranslationUnitDecl`
// typedef and type alias can come from C-style headers and C++ headers.
// For C-style headers, `DeclContxet` can be either `TranslationUnitDecl`
// or `LinkageSpecDecl`.
// For C++ header, `DeclContext ` can be one of `TranslationUnitDecl`,
// `NamespaceDecl`.
// For C++ headers, `DeclContext ` can be either `TranslationUnitDecl`
// or `NamespaceDecl`.
// With the following context matcher, we can match `typedefNameDecl` from
// both C-style header and C++ header (except for those in classes).
// both C-style headers and C++ headers (except for those in classes).
// "cc_matchers" are not included since template-related matchers are not
// applicable on `TypedefNameDecl`.
MatchFinder->addMatcher(
auto Typedefs =
typedefNameDecl(CommonFilter, anyOf(HasNSOrTUCtxMatcher,
hasDeclContext(linkageSpecDecl())))
.bind("decl"),
this);
hasDeclContext(linkageSpecDecl())));
// Matchers for enum declarations.
MatchFinder->addMatcher(enumDecl(CommonFilter, isDefinition(),
anyOf(HasNSOrTUCtxMatcher, ExternCMatcher))
.bind("decl"),
this);
auto Enums = enumDecl(CommonFilter, isDefinition(),
anyOf(HasNSOrTUCtxMatcher, ExternCMatcher));
// Matchers for enum constant declarations.
// We only match the enum constants in non-scoped enum declarations which are
// inside toplevel translation unit or a namespace.
auto EnumConstants = enumConstantDecl(
CommonFilter, unless(isInScopedEnum()),
anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher));
// Most of the time we care about all matchable decls, or all types.
auto Types = namedDecl(anyOf(CRecords, CXXRecords, Enums, Typedefs));
auto Decls = namedDecl(anyOf(CRecords, CXXRecords, Enums, Typedefs, Vars,
EnumConstants, Functions));
// We want eligible decls bound to "decl"...
MatchFinder->addMatcher(Decls.bind("decl"), this);
// ... and all uses of them bound to "use". These have many cases:
// Uses of values/functions: these generate a declRefExpr.
MatchFinder->addMatcher(
enumConstantDecl(
CommonFilter,
unless(isInScopedEnum()),
anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher))
.bind("decl"),
declRefExpr(isExpansionInMainFile(), to(Decls.bind("use"))), this);
// Uses of function templates:
MatchFinder->addMatcher(
declRefExpr(isExpansionInMainFile(),
to(functionDecl(hasParent(
functionTemplateDecl(has(Functions.bind("use"))))))),
this);
// Uses of most types: just look at what the typeLoc refers to.
MatchFinder->addMatcher(
typeLoc(isExpansionInMainFile(),
loc(qualType(hasDeclaration(Types.bind("use"))))),
this);
// Uses of typedefs: these are transparent to hasDeclaration, so we need to
// handle them explicitly.
MatchFinder->addMatcher(
typeLoc(isExpansionInMainFile(),
loc(typedefType(hasDeclaration(Typedefs.bind("use"))))),
this);
// Uses of class templates:
// The typeLoc names the templateSpecializationType. Its declaration is the
// ClassTemplateDecl, which contains the CXXRecordDecl we want.
MatchFinder->addMatcher(
typeLoc(isExpansionInMainFile(),
loc(templateSpecializationType(hasDeclaration(
classTemplateDecl(has(CXXRecords.bind("use"))))))),
this);
}
@ -221,15 +241,28 @@ void FindAllSymbols::run(const MatchFinder::MatchResult &Result) {
return;
}
const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("decl");
assert(ND && "Matched declaration must be a NamedDecl!");
const SourceManager *SM = Result.SourceManager;
SymbolInfo::Signals Signals;
const NamedDecl *ND;
if ((ND = Result.Nodes.getNodeAs<NamedDecl>("use")))
Signals.Used = 1;
else if ((ND = Result.Nodes.getNodeAs<NamedDecl>("decl")))
Signals.Seen = 1;
else
assert(false && "Must match a NamedDecl!");
llvm::Optional<SymbolInfo> Symbol =
CreateSymbolInfo(ND, *SM, Collector);
if (Symbol)
Reporter->reportSymbol(
SM->getFileEntryForID(SM->getMainFileID())->getName(), *Symbol);
const SourceManager *SM = Result.SourceManager;
if (auto Symbol = CreateSymbolInfo(ND, *SM, Collector)) {
Filename = SM->getFileEntryForID(SM->getMainFileID())->getName();
FileSymbols[*Symbol] += Signals;
}
}
void FindAllSymbols::onEndOfTranslationUnit() {
if (Filename != "") {
Reporter->reportSymbols(Filename, FileSymbols);
FileSymbols.clear();
Filename = "";
}
}
} // namespace find_all_symbols

View File

@ -32,18 +32,24 @@ class HeaderMapCollector;
/// through the class. #include fixer only needs the class name to find
/// headers.
///
class FindAllSymbols : public clang::ast_matchers::MatchFinder::MatchCallback {
class FindAllSymbols : public ast_matchers::MatchFinder::MatchCallback {
public:
explicit FindAllSymbols(SymbolReporter *Reporter,
HeaderMapCollector *Collector = nullptr)
: Reporter(Reporter), Collector(Collector) {}
void registerMatchers(clang::ast_matchers::MatchFinder *MatchFinder);
void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
void
run(const clang::ast_matchers::MatchFinder::MatchResult &result) override;
void run(const ast_matchers::MatchFinder::MatchResult &result) override;
protected:
void onEndOfTranslationUnit() override;
private:
// Current source file being processed, filled by first symbol found.
std::string Filename;
// Findings for the current source file, flushed on onEndOfTranslationUnit.
SymbolInfo::SignalMap FileSymbols;
// Reporter for SymbolInfo.
SymbolReporter *const Reporter;
// A remapping header file collector allowing clients include a different

View File

@ -24,8 +24,8 @@ FindAllSymbolsAction::FindAllSymbolsAction(
Matcher.registerMatchers(&MatchFinder);
}
std::unique_ptr<clang::ASTConsumer>
FindAllSymbolsAction::CreateASTConsumer(clang::CompilerInstance &Compiler,
std::unique_ptr<ASTConsumer>
FindAllSymbolsAction::CreateASTConsumer(CompilerInstance &Compiler,
StringRef InFile) {
Compiler.getPreprocessor().addCommentHandler(&Handler);
Compiler.getPreprocessor().addPPCallbacks(llvm::make_unique<FindAllMacros>(

View File

@ -18,22 +18,24 @@ using llvm::yaml::IO;
using llvm::yaml::Input;
using ContextType = clang::find_all_symbols::SymbolInfo::ContextType;
using clang::find_all_symbols::SymbolInfo;
using clang::find_all_symbols::SymbolAndSignals;
using SymbolKind = clang::find_all_symbols::SymbolInfo::SymbolKind;
LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(SymbolInfo)
LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(SymbolAndSignals)
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(std::string)
LLVM_YAML_IS_SEQUENCE_VECTOR(SymbolInfo::Context)
namespace llvm {
namespace yaml {
template <> struct MappingTraits<SymbolInfo> {
static void mapping(IO &io, SymbolInfo &Symbol) {
io.mapRequired("Name", Symbol.Name);
io.mapRequired("Contexts", Symbol.Contexts);
io.mapRequired("FilePath", Symbol.FilePath);
io.mapRequired("LineNumber", Symbol.LineNumber);
io.mapRequired("Type", Symbol.Type);
io.mapRequired("NumOccurrences", Symbol.NumOccurrences);
template <> struct MappingTraits<SymbolAndSignals> {
static void mapping(IO &io, SymbolAndSignals &Symbol) {
io.mapRequired("Name", Symbol.Symbol.Name);
io.mapRequired("Contexts", Symbol.Symbol.Contexts);
io.mapRequired("FilePath", Symbol.Symbol.FilePath);
io.mapRequired("LineNumber", Symbol.Symbol.LineNumber);
io.mapRequired("Type", Symbol.Symbol.Type);
io.mapRequired("Seen", Symbol.Signals.Seen);
io.mapRequired("Used", Symbol.Signals.Used);
}
};
@ -73,10 +75,9 @@ namespace find_all_symbols {
SymbolInfo::SymbolInfo(llvm::StringRef Name, SymbolKind Type,
llvm::StringRef FilePath, int LineNumber,
const std::vector<Context> &Contexts,
unsigned NumOccurrences)
const std::vector<Context> &Contexts)
: Name(Name), Type(Type), FilePath(FilePath), Contexts(Contexts),
LineNumber(LineNumber), NumOccurrences(NumOccurrences) {}
LineNumber(LineNumber) {}
bool SymbolInfo::operator==(const SymbolInfo &Symbol) const {
return std::tie(Name, Type, FilePath, LineNumber, Contexts) ==
@ -100,16 +101,30 @@ std::string SymbolInfo::getQualifiedName() const {
return QualifiedName;
}
SymbolInfo::Signals &SymbolInfo::Signals::operator+=(const Signals &RHS) {
Seen += RHS.Seen;
Used += RHS.Used;
return *this;
}
SymbolInfo::Signals SymbolInfo::Signals::operator+(const Signals &RHS) const {
Signals Result = *this;
Result += RHS;
return Result;
}
bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
const std::set<SymbolInfo> &Symbols) {
const SymbolInfo::SignalMap &Symbols) {
llvm::yaml::Output yout(OS);
for (auto Symbol : Symbols)
yout << Symbol;
for (const auto &Symbol : Symbols) {
SymbolAndSignals S{Symbol.first, Symbol.second};
yout << S;
}
return true;
}
std::vector<SymbolInfo> ReadSymbolInfosFromYAML(llvm::StringRef Yaml) {
std::vector<SymbolInfo> Symbols;
std::vector<SymbolAndSignals> ReadSymbolInfosFromYAML(llvm::StringRef Yaml) {
std::vector<SymbolAndSignals> Symbols;
llvm::yaml::Input yin(Yaml);
yin >> Symbols;
return Symbols;

View File

@ -20,7 +20,6 @@
namespace clang {
namespace find_all_symbols {
/// \brief Contains all information for a Symbol.
class SymbolInfo {
public:
@ -46,13 +45,30 @@ public:
/// \brief A pair of <ContextType, ContextName>.
typedef std::pair<ContextType, std::string> Context;
// \brief Signals are signals gathered by observing how a symbol is used.
// These are used to rank results.
struct Signals {
Signals() {}
Signals(unsigned Seen, unsigned Used) : Seen(Seen), Used(Used) {}
// Number of times this symbol was visible to a TU.
unsigned Seen = 0;
// Number of times this symbol was referenced a TU's main file.
unsigned Used = 0;
Signals &operator+=(const Signals &RHS);
Signals operator+(const Signals &RHS) const;
};
using SignalMap = std::map<SymbolInfo, Signals>;
// The default constructor is required by YAML traits in
// LLVM_YAML_IS_DOCUMENT_LIST_VECTOR.
SymbolInfo() : Type(SymbolKind::Unknown), LineNumber(-1) {}
SymbolInfo(llvm::StringRef Name, SymbolKind Type, llvm::StringRef FilePath,
int LineNumber, const std::vector<Context> &Contexts,
unsigned NumOccurrences = 0);
int LineNumber, const std::vector<Context> &Contexts);
void SetFilePath(llvm::StringRef Path) { FilePath = Path; }
@ -76,15 +92,12 @@ public:
/// \brief Get a 1-based line number of the symbol's declaration.
int getLineNumber() const { return LineNumber; }
/// \brief The number of times this symbol was found during an indexing run.
unsigned getNumOccurrences() const { return NumOccurrences; }
bool operator<(const SymbolInfo &Symbol) const;
bool operator==(const SymbolInfo &Symbol) const;
private:
friend struct llvm::yaml::MappingTraits<SymbolInfo>;
friend struct llvm::yaml::MappingTraits<struct SymbolAndSignals>;
/// \brief Identifier name.
std::string Name;
@ -110,18 +123,19 @@ private:
/// \brief The 1-based line number of of the symbol's declaration.
int LineNumber;
};
/// \brief The number of times this symbol was found during an indexing
/// run. Populated by the reducer and used to rank results.
unsigned NumOccurrences;
struct SymbolAndSignals {
SymbolInfo Symbol;
SymbolInfo::Signals Signals;
};
/// \brief Write SymbolInfos to a stream (YAML format).
bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
const std::set<SymbolInfo> &Symbols);
const SymbolInfo::SignalMap &Symbols);
/// \brief Read SymbolInfos from a YAML document.
std::vector<SymbolInfo> ReadSymbolInfosFromYAML(llvm::StringRef Yaml);
std::vector<SymbolAndSignals> ReadSymbolInfosFromYAML(llvm::StringRef Yaml);
} // namespace find_all_symbols
} // namespace clang

View File

@ -20,8 +20,8 @@ class SymbolReporter {
public:
virtual ~SymbolReporter() = default;
virtual void reportSymbol(llvm::StringRef FileName,
const SymbolInfo &Symbol) = 0;
virtual void reportSymbols(llvm::StringRef FileName,
const SymbolInfo::SignalMap &Symbols) = 0;
};
} // namespace find_all_symbols

View File

@ -62,38 +62,30 @@ The directory for merging symbols.)"),
namespace clang {
namespace find_all_symbols {
class YamlReporter : public clang::find_all_symbols::SymbolReporter {
class YamlReporter : public SymbolReporter {
public:
~YamlReporter() override {
for (const auto &Symbol : Symbols) {
int FD;
SmallString<128> ResultPath;
llvm::sys::fs::createUniqueFile(
OutputDir + "/" + llvm::sys::path::filename(Symbol.first) +
"-%%%%%%.yaml",
FD, ResultPath);
llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
WriteSymbolInfosToStream(OS, Symbol.second);
}
void reportSymbols(StringRef FileName,
const SymbolInfo::SignalMap &Symbols) override {
int FD;
SmallString<128> ResultPath;
llvm::sys::fs::createUniqueFile(
OutputDir + "/" + llvm::sys::path::filename(FileName) + "-%%%%%%.yaml",
FD, ResultPath);
llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
WriteSymbolInfosToStream(OS, Symbols);
}
void reportSymbol(StringRef FileName, const SymbolInfo &Symbol) override {
Symbols[FileName].insert(Symbol);
}
private:
std::map<std::string, std::set<SymbolInfo>> Symbols;
};
bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) {
std::error_code EC;
std::map<SymbolInfo, int> SymbolToNumOccurrences;
SymbolInfo::SignalMap Symbols;
std::mutex SymbolMutex;
auto AddSymbols = [&](ArrayRef<SymbolInfo> Symbols) {
auto AddSymbols = [&](ArrayRef<SymbolAndSignals> NewSymbols) {
// Synchronize set accesses.
std::unique_lock<std::mutex> LockGuard(SymbolMutex);
for (const auto &Symbol : Symbols)
++SymbolToNumOccurrences[Symbol];
for (const auto &Symbol : NewSymbols) {
Symbols[Symbol.Symbol] += Symbol.Signals;
}
};
// Load all symbol files in MergeDir.
@ -109,8 +101,13 @@ bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) {
llvm::errs() << "Can't open " << Path << "\n";
return;
}
std::vector<SymbolInfo> Symbols =
std::vector<SymbolAndSignals> Symbols =
ReadSymbolInfosFromYAML(Buffer.get()->getBuffer());
for (auto &Symbol : Symbols) {
// Only count one occurrence per file, to avoid spam.
Symbol.Signals.Seen = std::min(Symbol.Signals.Seen, 1u);
Symbol.Signals.Used = std::min(Symbol.Signals.Used, 1u);
}
// FIXME: Merge without creating such a heavy contention point.
AddSymbols(Symbols);
},
@ -124,14 +121,7 @@ bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) {
<< '\n';
return false;
}
std::set<SymbolInfo> Result;
for (const auto &Entry : SymbolToNumOccurrences) {
const auto &Symbol = Entry.first;
Result.insert(SymbolInfo(Symbol.getName(), Symbol.getSymbolKind(),
Symbol.getFilePath(), Symbol.getLineNumber(),
Symbol.getContexts(), Entry.second));
}
WriteSymbolInfosToStream(OS, Result);
WriteSymbolInfosToStream(OS, Symbols);
return true;
}

View File

@ -158,6 +158,8 @@ cl::opt<std::string>
std::unique_ptr<include_fixer::SymbolIndexManager>
createSymbolIndexManager(StringRef FilePath) {
using find_all_symbols::SymbolInfo;
auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
switch (DatabaseFormat) {
case fixed: {
@ -167,17 +169,19 @@ createSymbolIndexManager(StringRef FilePath) {
std::map<std::string, std::vector<std::string>> SymbolsMap;
SmallVector<StringRef, 4> SemicolonSplits;
StringRef(Input).split(SemicolonSplits, ";");
std::vector<find_all_symbols::SymbolInfo> Symbols;
std::vector<find_all_symbols::SymbolAndSignals> Symbols;
for (StringRef Pair : SemicolonSplits) {
auto Split = Pair.split('=');
std::vector<std::string> Headers;
SmallVector<StringRef, 4> CommaSplits;
Split.second.split(CommaSplits, ",");
for (size_t I = 0, E = CommaSplits.size(); I != E; ++I)
Symbols.push_back(find_all_symbols::SymbolInfo(
Split.first.trim(),
find_all_symbols::SymbolInfo::SymbolKind::Unknown,
CommaSplits[I].trim(), 1, {}, /*NumOccurrences=*/E - I));
Symbols.push_back(
{SymbolInfo(Split.first.trim(), SymbolInfo::SymbolKind::Unknown,
CommaSplits[I].trim(), 1, {}),
// Use fake "seen" signal for tests, so first header wins.
SymbolInfo::Signals(/*Seen=*/static_cast<unsigned>(E - I),
/*Used=*/0)});
}
SymbolIndexMgr->addSymbolIndex([=]() {
return llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols);

View File

@ -8,7 +8,8 @@ Contexts:
FilePath: foo.h
LineNumber: 1
Type: Class
NumOccurrences: 1
Seen: 1
Used: 0
---
Name: bar
Contexts:
@ -19,7 +20,8 @@ Contexts:
FilePath: ../include/bar.h
LineNumber: 1
Type: Class
NumOccurrences: 1
Seen: 1
Used: 0
---
Name: bar
Contexts:
@ -30,7 +32,8 @@ Contexts:
FilePath: ../include/bar.h
LineNumber: 2
Type: Class
NumOccurrences: 3
Seen: 3
Used: 0
---
Name: bar
Contexts:
@ -41,14 +44,16 @@ Contexts:
FilePath: ../include/zbar.h
LineNumber: 1
Type: Class
NumOccurrences: 3
Seen: 3
Used: 0
---
Name: b
Contexts:
FilePath: var.h
LineNumber: 1
Type: Variable
NumOccurrences: 1
Seen: 1
Used: 0
---
Name: bar
Contexts:
@ -57,4 +62,5 @@ Contexts:
FilePath: test/include-fixer/baz.h
LineNumber: 1
Type: Class
NumOccurrences: 1
Seen: 1
Used: 0

View File

@ -6,7 +6,8 @@ Contexts:
FilePath: foo.h
LineNumber: 1
Type: Class
NumOccurrences: 1
Seen: 1
Used: 1
...
---
Name: bar
@ -16,5 +17,6 @@ Contexts:
FilePath: ../include/bar.h
LineNumber: 1
Type: Class
NumOccurrences: 1
Seen: 1
Used: 2
...

View File

@ -6,7 +6,8 @@ Contexts:
FilePath: foo.h
LineNumber: 1
Type: Class
NumOccurrences: 1
Seen: 1
Used: 2
...
---
Name: bar
@ -16,5 +17,6 @@ Contexts:
FilePath: ../include/barbar.h
LineNumber: 1
Type: Class
NumOccurrences: 1
Seen: 1
Used: 0
...

View File

@ -9,7 +9,8 @@ Contexts:
FilePath: ../include/bar.h
LineNumber: 1
Type: Class
NumOccurrences: 1
Seen: 1
Used: 1
...
---
Name: bar
@ -19,7 +20,8 @@ Contexts:
FilePath: ../include/barbar.h
LineNumber: 1
Type: Class
NumOccurrences: 1
Seen: 1
Used: 0
...
---
Name: foo
@ -29,5 +31,6 @@ Contexts:
FilePath: foo.h
LineNumber: 1
Type: Class
NumOccurrences: 2
Seen: 2
Used: 2
...

View File

@ -19,6 +19,7 @@ namespace include_fixer {
namespace {
using find_all_symbols::SymbolInfo;
using find_all_symbols::SymbolAndSignals;
static bool runOnCode(tooling::ToolAction *ToolAction, StringRef Code,
StringRef FileName,
@ -52,42 +53,49 @@ static bool runOnCode(tooling::ToolAction *ToolAction, StringRef Code,
static std::string runIncludeFixer(
StringRef Code,
const std::vector<std::string> &ExtraArgs = std::vector<std::string>()) {
std::vector<SymbolInfo> Symbols = {
SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>", 1,
{{SymbolInfo::ContextType::Namespace, "std"}}),
SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"", 1,
{{SymbolInfo::ContextType::Namespace, "std"}}),
SymbolInfo("foo", SymbolInfo::SymbolKind::Class, "\"dir/otherdir/qux.h\"",
1, {{SymbolInfo::ContextType::Namespace, "b"},
{SymbolInfo::ContextType::Namespace, "a"}}),
SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"", 1,
{{SymbolInfo::ContextType::Namespace, "b"},
{SymbolInfo::ContextType::Namespace, "a"}}),
SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar2.h\"", 1,
{{SymbolInfo::ContextType::Namespace, "c"},
{SymbolInfo::ContextType::Namespace, "a"}}),
SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"", 1,
{{SymbolInfo::ContextType::EnumDecl, "Color"},
{SymbolInfo::ContextType::Namespace, "b"},
{SymbolInfo::ContextType::Namespace, "a"}}),
SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 1,
{{SymbolInfo::ContextType::Namespace, "__a"},
{SymbolInfo::ContextType::Namespace, "a"}},
/*num_occurrences=*/2),
SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 2,
{{SymbolInfo::ContextType::Namespace, "a"}},
/*num_occurrences=*/1),
SymbolInfo("StrCat", SymbolInfo::SymbolKind::Class, "\"strcat.h\"",
1, {{SymbolInfo::ContextType::Namespace, "str"}}),
SymbolInfo("str", SymbolInfo::SymbolKind::Class, "\"str.h\"",
1, {}),
SymbolInfo("foo2", SymbolInfo::SymbolKind::Class, "\"foo2.h\"",
1, {}),
std::vector<SymbolAndSignals> Symbols = {
{SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>", 1,
{{SymbolInfo::ContextType::Namespace, "std"}}),
SymbolInfo::Signals{}},
{SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"", 1,
{{SymbolInfo::ContextType::Namespace, "std"}}),
SymbolInfo::Signals{}},
{SymbolInfo("foo", SymbolInfo::SymbolKind::Class,
"\"dir/otherdir/qux.h\"", 1,
{{SymbolInfo::ContextType::Namespace, "b"},
{SymbolInfo::ContextType::Namespace, "a"}}),
SymbolInfo::Signals{}},
{SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"", 1,
{{SymbolInfo::ContextType::Namespace, "b"},
{SymbolInfo::ContextType::Namespace, "a"}}),
SymbolInfo::Signals{}},
{SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar2.h\"", 1,
{{SymbolInfo::ContextType::Namespace, "c"},
{SymbolInfo::ContextType::Namespace, "a"}}),
SymbolInfo::Signals{}},
{SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"", 1,
{{SymbolInfo::ContextType::EnumDecl, "Color"},
{SymbolInfo::ContextType::Namespace, "b"},
{SymbolInfo::ContextType::Namespace, "a"}}),
SymbolInfo::Signals{}},
{SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 1,
{{SymbolInfo::ContextType::Namespace, "__a"},
{SymbolInfo::ContextType::Namespace, "a"}}),
SymbolInfo::Signals{/*Seen=*/2, 0}},
{SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 2,
{{SymbolInfo::ContextType::Namespace, "a"}}),
SymbolInfo::Signals{/*Seen=*/2, 0}},
{SymbolInfo("StrCat", SymbolInfo::SymbolKind::Class, "\"strcat.h\"", 1,
{{SymbolInfo::ContextType::Namespace, "str"}}),
SymbolInfo::Signals{}},
{SymbolInfo("str", SymbolInfo::SymbolKind::Class, "\"str.h\"", 1, {}),
SymbolInfo::Signals{}},
{SymbolInfo("foo2", SymbolInfo::SymbolKind::Class, "\"foo2.h\"", 1, {}),
SymbolInfo::Signals{}},
};
auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
SymbolIndexMgr->addSymbolIndex([=]() {
return llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols);
});
auto SymbolIndexMgr = llvm::make_unique<SymbolIndexManager>();
SymbolIndexMgr->addSymbolIndex(
[=]() { return llvm::make_unique<InMemorySymbolIndex>(Symbols); });
std::vector<IncludeFixerContext> FixerContexts;
IncludeFixerActionFactory Factory(*SymbolIndexMgr, FixerContexts, "llvm");
@ -96,15 +104,14 @@ static std::string runIncludeFixer(
assert(FixerContexts.size() == 1);
if (FixerContexts.front().getHeaderInfos().empty())
return Code;
auto Replaces = clang::include_fixer::createIncludeFixerReplacements(
Code, FixerContexts.front());
auto Replaces = createIncludeFixerReplacements(Code, FixerContexts.front());
EXPECT_TRUE(static_cast<bool>(Replaces))
<< llvm::toString(Replaces.takeError()) << "\n";
if (!Replaces)
return "";
clang::RewriterTestContext Context;
clang::FileID ID = Context.createInMemoryFile(FakeFileName, Code);
clang::tooling::applyAllReplacements(*Replaces, Context.Rewrite);
RewriterTestContext Context;
FileID ID = Context.createInMemoryFile(FakeFileName, Code);
tooling::applyAllReplacements(*Replaces, Context.Rewrite);
return Context.getRewrittenText(ID);
}

View File

@ -19,6 +19,7 @@
#include "clang/Frontend/PCHContainerOperations.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
#include "gtest/gtest.h"
@ -31,25 +32,28 @@ namespace find_all_symbols {
static const char HeaderName[] = "symbols.h";
class TestSymbolReporter : public clang::find_all_symbols::SymbolReporter {
class TestSymbolReporter : public SymbolReporter {
public:
~TestSymbolReporter() override {}
void reportSymbol(llvm::StringRef FileName,
const SymbolInfo &Symbol) override {
Symbols.push_back(Symbol);
void reportSymbols(llvm::StringRef FileName,
const SymbolInfo::SignalMap &NewSymbols) override {
for (const auto &Entry : NewSymbols)
Symbols[Entry.first] += Entry.second;
}
bool hasSymbol(const SymbolInfo &Symbol) const {
for (const auto &S : Symbols) {
if (S == Symbol)
return true;
}
return false;
auto it = Symbols.find(Symbol);
return it != Symbols.end() && it->second.Seen > 0;
}
bool hasUse(const SymbolInfo &Symbol) const {
auto it = Symbols.find(Symbol);
return it != Symbols.end() && it->second.Used > 0;
}
private:
std::vector<SymbolInfo> Symbols;
SymbolInfo::SignalMap Symbols;
};
class FindAllSymbolsTest : public ::testing::Test {
@ -58,7 +62,9 @@ public:
return Reporter.hasSymbol(Symbol);
}
bool runFindAllSymbols(StringRef Code) {
bool hasUse(const SymbolInfo &Symbol) { return Reporter.hasUse(Symbol); }
bool runFindAllSymbols(StringRef HeaderCode, StringRef MainCode) {
llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
new vfs::InMemoryFileSystem);
llvm::IntrusiveRefCntPtr<FileManager> Files(
@ -88,7 +94,7 @@ public:
InMemoryFileSystem->addFile(InternalHeader, 0,
llvm::MemoryBuffer::getMemBuffer(InternalCode));
std::unique_ptr<clang::tooling::FrontendActionFactory> Factory(
std::unique_ptr<tooling::FrontendActionFactory> Factory(
new FindAllSymbolsActionFactory(&Reporter, &RegexMap));
tooling::ToolInvocation Invocation(
@ -98,7 +104,7 @@ public:
std::make_shared<PCHContainerOperations>());
InMemoryFileSystem->addFile(HeaderName, 0,
llvm::MemoryBuffer::getMemBuffer(Code));
llvm::MemoryBuffer::getMemBuffer(HeaderCode));
std::string Content = "#include\"" + std::string(HeaderName) +
"\"\n"
@ -118,6 +124,7 @@ public:
SymbolInfo DirtySymbol("ExtraInternal", SymbolInfo::SymbolKind::Class,
CleanHeader, 2, {});
#endif // _MSC_VER && __MINGW32__
Content += "\n" + MainCode.str();
InMemoryFileSystem->addFile(FileName, 0,
llvm::MemoryBuffer::getMemBuffer(Content));
Invocation.run();
@ -135,49 +142,64 @@ protected:
};
TEST_F(FindAllSymbolsTest, VariableSymbols) {
static const char Code[] = R"(
static const char Header[] = R"(
extern int xargc;
namespace na {
static bool SSSS = false;
namespace nb { const long long *XXXX; }
})";
runFindAllSymbols(Code);
static const char Main[] = R"(
auto y = &na::nb::XXXX;
int main() { if (na::SSSS) return xargc; }
)";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol =
SymbolInfo("xargc", SymbolInfo::SymbolKind::Variable, HeaderName, 2, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("SSSS", SymbolInfo::SymbolKind::Variable, HeaderName, 4,
{{SymbolInfo::ContextType::Namespace, "na"}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("XXXX", SymbolInfo::SymbolKind::Variable, HeaderName, 5,
{{SymbolInfo::ContextType::Namespace, "nb"},
{SymbolInfo::ContextType::Namespace, "na"}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
}
TEST_F(FindAllSymbolsTest, ExternCSymbols) {
static const char Code[] = R"(
static const char Header[] = R"(
extern "C" {
int C_Func() { return 0; }
struct C_struct {
int Member;
};
})";
runFindAllSymbols(Code);
static const char Main[] = R"(
C_struct q() {
int(*ptr)() = C_Func;
return {0};
}
)";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol =
SymbolInfo("C_Func", SymbolInfo::SymbolKind::Function, HeaderName, 3, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol =
SymbolInfo("C_struct", SymbolInfo::SymbolKind::Class, HeaderName, 4, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
}
TEST_F(FindAllSymbolsTest, CXXRecordSymbols) {
static const char Code[] = R"(
static const char Header[] = R"(
struct Glob {};
struct A; // Not a defintion, ignored.
class NOP; // Not a defintion, ignored
@ -190,26 +212,33 @@ TEST_F(FindAllSymbolsTest, CXXRecordSymbols) {
};
}; //
)";
runFindAllSymbols(Code);
static const char Main[] = R"(
static Glob glob;
static na::A::AAAA* a;
)";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol =
SymbolInfo("Glob", SymbolInfo::SymbolKind::Class, HeaderName, 2, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("A", SymbolInfo::SymbolKind::Class, HeaderName, 6,
{{SymbolInfo::ContextType::Namespace, "na"}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("AAA", SymbolInfo::SymbolKind::Class, HeaderName, 7,
{{SymbolInfo::ContextType::Record, "A"},
{SymbolInfo::ContextType::Namespace, "na"}});
EXPECT_FALSE(hasSymbol(Symbol));
EXPECT_FALSE(hasUse(Symbol));
}
TEST_F(FindAllSymbolsTest, CXXRecordSymbolsTemplate) {
static const char Code[] = R"(
static const char Header[] = R"(
template <typename T>
class T_TEMP {
struct T_TEMP {
template <typename _Tp1>
struct rebind { typedef T_TEMP<_Tp1> other; };
};
@ -222,11 +251,15 @@ TEST_F(FindAllSymbolsTest, CXXRecordSymbolsTemplate) {
// Ignore specialization.
template <> class Observer<int> {};
)";
runFindAllSymbols(Code);
static const char Main[] = R"(
extern T_TEMP<int>::rebind<char> weirdo;
)";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol =
SymbolInfo("T_TEMP", SymbolInfo::SymbolKind::Class, HeaderName, 3, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
}
TEST_F(FindAllSymbolsTest, DontIgnoreTemplatePartialSpecialization) {
@ -239,7 +272,7 @@ TEST_F(FindAllSymbolsTest, DontIgnoreTemplatePartialSpecialization) {
template<class T> void f() {};
template<> void f<int>() {};
)";
runFindAllSymbols(Code);
runFindAllSymbols(Code, "");
SymbolInfo Symbol =
SymbolInfo("Class", SymbolInfo::SymbolKind::Class, HeaderName, 4, {});
EXPECT_TRUE(hasSymbol(Symbol));
@ -252,7 +285,7 @@ TEST_F(FindAllSymbolsTest, DontIgnoreTemplatePartialSpecialization) {
}
TEST_F(FindAllSymbolsTest, FunctionSymbols) {
static const char Code[] = R"(
static const char Header[] = R"(
namespace na {
int gg(int);
int f(const int &a) { int Local; static int StaticLocal; return 0; }
@ -265,91 +298,127 @@ TEST_F(FindAllSymbolsTest, FunctionSymbols) {
} // namespace nb
} // namespace na";
)";
runFindAllSymbols(Code);
static const char Main[] = R"(
int(*gg)(int) = &na::gg;
int main() {
(void)na::SSSFFF;
na::nb::fun(0);
return na::f(gg(0));
}
)";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol =
SymbolInfo("gg", SymbolInfo::SymbolKind::Function, HeaderName, 3,
{{SymbolInfo::ContextType::Namespace, "na"}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName, 4,
{{SymbolInfo::ContextType::Namespace, "na"}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("SSSFFF", SymbolInfo::SymbolKind::Function, HeaderName, 5,
{{SymbolInfo::ContextType::Namespace, "na"}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("fun", SymbolInfo::SymbolKind::Function, HeaderName, 10,
{{SymbolInfo::ContextType::Namespace, "nb"},
{SymbolInfo::ContextType::Namespace, "na"}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
}
TEST_F(FindAllSymbolsTest, NamespaceTest) {
static const char Code[] = R"(
static const char Header[] = R"(
int X1;
namespace { int X2; }
namespace { namespace { int X3; } }
namespace { namespace nb { int X4; } }
namespace na { inline namespace __1 { int X5; } }
)";
runFindAllSymbols(Code);
static const char Main[] = R"(
using namespace nb;
int main() {
X1 = X2;
X3 = X4;
(void)na::X5;
}
)";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol =
SymbolInfo("X1", SymbolInfo::SymbolKind::Variable, HeaderName, 2, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("X2", SymbolInfo::SymbolKind::Variable, HeaderName, 3,
{{SymbolInfo::ContextType::Namespace, ""}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("X3", SymbolInfo::SymbolKind::Variable, HeaderName, 4,
{{SymbolInfo::ContextType::Namespace, ""},
{SymbolInfo::ContextType::Namespace, ""}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("X4", SymbolInfo::SymbolKind::Variable, HeaderName, 5,
{{SymbolInfo::ContextType::Namespace, "nb"},
{SymbolInfo::ContextType::Namespace, ""}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("X5", SymbolInfo::SymbolKind::Variable, HeaderName, 6,
{{SymbolInfo::ContextType::Namespace, "na"}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
}
TEST_F(FindAllSymbolsTest, DecayedTypeTest) {
static const char Code[] = "void DecayedFunc(int x[], int y[10]) {}";
runFindAllSymbols(Code);
static const char Header[] = "void DecayedFunc(int x[], int y[10]) {}";
static const char Main[] = R"(int main() { DecayedFunc(nullptr, nullptr); })";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol = SymbolInfo(
"DecayedFunc", SymbolInfo::SymbolKind::Function, HeaderName, 1, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
}
TEST_F(FindAllSymbolsTest, CTypedefTest) {
static const char Code[] = R"(
static const char Header[] = R"(
typedef unsigned size_t_;
typedef struct { int x; } X;
using XX = X;
)";
runFindAllSymbols(Code);
static const char Main[] = R"(
size_t_ f;
template<typename T> struct vector{};
vector<X> list;
void foo(const XX&){}
)";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol = SymbolInfo("size_t_", SymbolInfo::SymbolKind::TypedefName,
HeaderName, 2, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol =
SymbolInfo("X", SymbolInfo::SymbolKind::TypedefName, HeaderName, 3, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol =
SymbolInfo("XX", SymbolInfo::SymbolKind::TypedefName, HeaderName, 4, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
}
TEST_F(FindAllSymbolsTest, EnumTest) {
static const char Code[] = R"(
static const char Header[] = R"(
enum Glob_E { G1, G2 };
enum class Altitude { high='h', low='l'};
enum { A1, A2 };
@ -359,42 +428,58 @@ TEST_F(FindAllSymbolsTest, EnumTest) {
};
enum DECL : int;
)";
runFindAllSymbols(Code);
static const char Main[] = R"(
static auto flags = G1 | G2;
static auto alt = Altitude::high;
static auto nested = A::X1;
extern DECL whatever;
static auto flags2 = A1 | A2;
)";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol =
SymbolInfo("Glob_E", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 2, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_FALSE(hasUse(Symbol));
Symbol =
SymbolInfo("G1", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName, 2,
{{SymbolInfo::ContextType::EnumDecl, "Glob_E"}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol =
SymbolInfo("G2", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName, 2,
{{SymbolInfo::ContextType::EnumDecl, "Glob_E"}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("Altitude", SymbolInfo::SymbolKind::EnumDecl, HeaderName,
3, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol =
SymbolInfo("high", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
3, {{SymbolInfo::ContextType::EnumDecl, "Altitude"}});
EXPECT_FALSE(hasSymbol(Symbol));
EXPECT_FALSE(hasUse(Symbol));
Symbol = SymbolInfo("A1", SymbolInfo::SymbolKind::EnumConstantDecl,
HeaderName, 4, {{SymbolInfo::ContextType::EnumDecl, ""}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("A2", SymbolInfo::SymbolKind::EnumConstantDecl,
HeaderName, 4, {{SymbolInfo::ContextType::EnumDecl, ""}});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 4, {});
EXPECT_FALSE(hasSymbol(Symbol));
EXPECT_FALSE(hasUse(Symbol));
Symbol = SymbolInfo("A_ENUM", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 7,
{{SymbolInfo::ContextType::Record, "A"}});
EXPECT_FALSE(hasSymbol(Symbol));
EXPECT_FALSE(hasUse(Symbol));
Symbol = SymbolInfo("X1", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 7,
{{SymbolInfo::ContextType::EnumDecl, "A_ENUM"},
@ -407,63 +492,83 @@ TEST_F(FindAllSymbolsTest, EnumTest) {
}
TEST_F(FindAllSymbolsTest, IWYUPrivatePragmaTest) {
static const char Code[] = R"(
static const char Header[] = R"(
// IWYU pragma: private, include "bar.h"
struct Bar {
};
)";
runFindAllSymbols(Code);
static const char Main[] = R"(
Bar bar;
)";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol =
SymbolInfo("Bar", SymbolInfo::SymbolKind::Class, "bar.h", 3, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
}
TEST_F(FindAllSymbolsTest, MacroTest) {
static const char Code[] = R"(
static const char Header[] = R"(
#define X
#define Y 1
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
)";
runFindAllSymbols(Code);
static const char Main[] = R"(
#ifdef X
int main() { return MAX(0,Y); }
#endif
)";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol =
SymbolInfo("X", SymbolInfo::SymbolKind::Macro, HeaderName, 2, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, HeaderName, 3, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, HeaderName, 4, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
}
TEST_F(FindAllSymbolsTest, MacroTestWithIWYU) {
static const char Code[] = R"(
static const char Header[] = R"(
// IWYU pragma: private, include "bar.h"
#define X
#define X 1
#define Y 1
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
)";
runFindAllSymbols(Code);
static const char Main[] = R"(
#ifdef X
int main() { return MAX(0,Y); }
#endif
)";
runFindAllSymbols(Header, Main);
SymbolInfo Symbol =
SymbolInfo("X", SymbolInfo::SymbolKind::Macro, "bar.h", 3, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, "bar.h", 4, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, "bar.h", 5, {});
EXPECT_TRUE(hasSymbol(Symbol));
EXPECT_TRUE(hasUse(Symbol));
}
TEST_F(FindAllSymbolsTest, NoFriendTest) {
static const char Code[] = R"(
static const char Header[] = R"(
class WorstFriend {
friend void Friend();
friend class BestFriend;
};
)";
runFindAllSymbols(Code);
runFindAllSymbols(Header, "");
SymbolInfo Symbol = SymbolInfo("WorstFriend", SymbolInfo::SymbolKind::Class,
HeaderName, 2, {});
EXPECT_TRUE(hasSymbol(Symbol));