Index: include-fixer/InMemorySymbolIndex.h =================================================================== --- include-fixer/InMemorySymbolIndex.h +++ include-fixer/InMemorySymbolIndex.h @@ -21,13 +21,14 @@ /// Xref database with fixed content. class InMemorySymbolIndex : public SymbolIndex { public: - InMemorySymbolIndex(const std::vector &Symbols); + InMemorySymbolIndex( + const std::vector &Symbols); - std::vector + std::vector search(llvm::StringRef Identifier) override; private: - std::map> + std::map> LookupTable; }; Index: include-fixer/InMemorySymbolIndex.cpp =================================================================== --- include-fixer/InMemorySymbolIndex.cpp +++ include-fixer/InMemorySymbolIndex.cpp @@ -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 &Symbols) { + const std::vector &Symbols) { for (const auto &Symbol : Symbols) - LookupTable[Symbol.getName()].push_back(Symbol); + LookupTable[Symbol.Symbol.getName()].push_back(Symbol); } -std::vector +std::vector InMemorySymbolIndex::search(llvm::StringRef Identifier) { auto I = LookupTable.find(Identifier); if (I != LookupTable.end()) Index: include-fixer/IncludeFixer.cpp =================================================================== --- include-fixer/IncludeFixer.cpp +++ include-fixer/IncludeFixer.cpp @@ -334,8 +334,7 @@ SourceManager, HeaderSearch); SymbolCandidates.emplace_back(Symbol.getName(), Symbol.getSymbolKind(), MinimizedFilePath, Symbol.getLineNumber(), - Symbol.getContexts(), - Symbol.getNumOccurrences()); + Symbol.getContexts()); } return IncludeFixerContext(FilePath, QuerySymbolInfos, SymbolCandidates); } Index: include-fixer/SymbolIndex.h =================================================================== --- include-fixer/SymbolIndex.h +++ include-fixer/SymbolIndex.h @@ -28,7 +28,7 @@ /// \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 + virtual std::vector search(llvm::StringRef Identifier) = 0; }; Index: include-fixer/SymbolIndexManager.cpp =================================================================== --- include-fixer/SymbolIndexManager.cpp +++ include-fixer/SymbolIndexManager.cpp @@ -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 @@ return MaxSegments; } -static void rank(std::vector &Symbols, +static void rank(std::vector &Symbols, llvm::StringRef FileName) { llvm::DenseMap 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 @@ // Eventually we will either hit a class (namespaces aren't in the database // either) and can report that result. bool TookPrefix = false; - std::vector MatchedSymbols; + std::vector MatchedSymbols; do { - std::vector Symbols; + std::vector 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 @@ DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got " << Symbols.size() << " results...\n"); - for (const auto &Symbol : Symbols) { + for (const 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 @@ Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Macro)) continue; - MatchedSymbols.push_back(Symbol); + MatchedSymbols.push_back(SymAndSig); } } } @@ -148,7 +150,11 @@ } while (MatchedSymbols.empty() && !Names.empty() && IsNestedSearch); rank(MatchedSymbols, FileName); - return MatchedSymbols; + // Strip signals, they are no longer needed. + std::vector Res; + for (const auto &SymAndSig : MatchedSymbols) + Res.push_back(SymAndSig.Symbol); + return Res; } } // namespace include_fixer Index: include-fixer/YamlSymbolIndex.h =================================================================== --- include-fixer/YamlSymbolIndex.h +++ include-fixer/YamlSymbolIndex.h @@ -29,15 +29,15 @@ static llvm::ErrorOr> createFromDirectory(llvm::StringRef Directory, llvm::StringRef Name); - std::vector + std::vector search(llvm::StringRef Identifier) override; private: explicit YamlSymbolIndex( - std::vector Symbols) + std::vector Symbols) : Symbols(std::move(Symbols)) {} - std::vector Symbols; + std::vector Symbols; }; } // namespace include_fixer Index: include-fixer/YamlSymbolIndex.cpp =================================================================== --- include-fixer/YamlSymbolIndex.cpp +++ include-fixer/YamlSymbolIndex.cpp @@ -17,6 +17,7 @@ #include using clang::find_all_symbols::SymbolInfo; +using clang::find_all_symbols::SymbolAndSignals; namespace clang { namespace include_fixer { @@ -27,9 +28,8 @@ if (!Buffer) return Buffer.getError(); - return std::unique_ptr( - new YamlSymbolIndex(clang::find_all_symbols::ReadSymbolInfosFromYAML( - Buffer.get()->getBuffer()))); + return std::unique_ptr(new YamlSymbolIndex( + find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer()))); } llvm::ErrorOr> @@ -47,10 +47,11 @@ return llvm::make_error_code(llvm::errc::no_such_file_or_directory); } -std::vector YamlSymbolIndex::search(llvm::StringRef Identifier) { - std::vector Results; +std::vector +YamlSymbolIndex::search(llvm::StringRef Identifier) { + std::vector Results; for (const auto &Symbol : Symbols) { - if (Symbol.getName() == Identifier) + if (Symbol.Symbol.getName() == Identifier) Results.push_back(Symbol); } return Results; Index: include-fixer/find-all-symbols/FindAllMacros.h =================================================================== --- include-fixer/find-all-symbols/FindAllMacros.h +++ include-fixer/find-all-symbols/FindAllMacros.h @@ -16,6 +16,7 @@ #include "clang/Lex/PPCallbacks.h" namespace clang { +class MacroInfo; namespace find_all_symbols { class HeaderMapCollector; @@ -32,7 +33,24 @@ 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 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; Index: include-fixer/find-all-symbols/FindAllMacros.cpp =================================================================== --- include-fixer/find-all-symbols/FindAllMacros.cpp +++ include-fixer/find-all-symbols/FindAllMacros.cpp @@ -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 +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; +} + +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); +} - Reporter->reportSymbol(SM->getFileEntryForID(SM->getMainFileID())->getName(), - Symbol); +void FindAllMacros::EndOfMainFile() { + Reporter->reportSymbols(SM->getFileEntryForID(SM->getMainFileID())->getName(), + FileSymbols); + FileSymbols = {}; } } // namespace find_all_symbols Index: include-fixer/find-all-symbols/FindAllSymbols.h =================================================================== --- include-fixer/find-all-symbols/FindAllSymbols.h +++ include-fixer/find-all-symbols/FindAllSymbols.h @@ -32,18 +32,24 @@ /// 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 Index: include-fixer/find-all-symbols/FindAllSymbols.cpp =================================================================== --- include-fixer/find-all-symbols/FindAllSymbols.cpp +++ include-fixer/find-all-symbols/FindAllSymbols.cpp @@ -154,64 +154,84 @@ // 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( + 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( - enumConstantDecl( - CommonFilter, - unless(isInScopedEnum()), - anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher)) - .bind("decl"), + typeLoc(isExpansionInMainFile(), + loc(templateSpecializationType(hasDeclaration( + classTemplateDecl(has(CXXRecords.bind("use"))))))), this); } @@ -221,15 +241,28 @@ return; } - const auto *ND = Result.Nodes.getNodeAs("decl"); - assert(ND && "Matched declaration must be a NamedDecl!"); + SymbolInfo::Signals Signals; + const NamedDecl *ND; + if ((ND = Result.Nodes.getNodeAs("use"))) + Signals.Used = 1; + else if ((ND = Result.Nodes.getNodeAs("decl"))) + Signals.Seen = 1; + else + assert(false && "Must match a NamedDecl!"); + const SourceManager *SM = Result.SourceManager; + if (auto Symbol = CreateSymbolInfo(ND, *SM, Collector)) { + Filename = SM->getFileEntryForID(SM->getMainFileID())->getName(); + FileSymbols[*Symbol] += Signals; + } +} - llvm::Optional Symbol = - CreateSymbolInfo(ND, *SM, Collector); - if (Symbol) - Reporter->reportSymbol( - SM->getFileEntryForID(SM->getMainFileID())->getName(), *Symbol); +void FindAllSymbols::onEndOfTranslationUnit() { + if (Filename != "") { + Reporter->reportSymbols(Filename, FileSymbols); + FileSymbols = {}; + Filename = ""; + } } } // namespace find_all_symbols Index: include-fixer/find-all-symbols/FindAllSymbolsAction.cpp =================================================================== --- include-fixer/find-all-symbols/FindAllSymbolsAction.cpp +++ include-fixer/find-all-symbols/FindAllSymbolsAction.cpp @@ -24,8 +24,8 @@ Matcher.registerMatchers(&MatchFinder); } -std::unique_ptr -FindAllSymbolsAction::CreateASTConsumer(clang::CompilerInstance &Compiler, +std::unique_ptr +FindAllSymbolsAction::CreateASTConsumer(CompilerInstance &Compiler, StringRef InFile) { Compiler.getPreprocessor().addCommentHandler(&Handler); Compiler.getPreprocessor().addPPCallbacks(llvm::make_unique( Index: include-fixer/find-all-symbols/SymbolInfo.h =================================================================== --- include-fixer/find-all-symbols/SymbolInfo.h +++ include-fixer/find-all-symbols/SymbolInfo.h @@ -20,7 +20,6 @@ namespace clang { namespace find_all_symbols { - /// \brief Contains all information for a Symbol. class SymbolInfo { public: @@ -46,13 +45,30 @@ /// \brief A pair of . typedef std::pair 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; + // 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 &Contexts, - unsigned NumOccurrences = 0); + int LineNumber, const std::vector &Contexts); void SetFilePath(llvm::StringRef Path) { FilePath = Path; } @@ -76,15 +92,12 @@ /// \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; + friend struct llvm::yaml::MappingTraits; /// \brief Identifier name. std::string Name; @@ -110,18 +123,19 @@ /// \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 &Symbols); + const SymbolInfo::SignalMap &Symbols); /// \brief Read SymbolInfos from a YAML document. -std::vector ReadSymbolInfosFromYAML(llvm::StringRef Yaml); +std::vector ReadSymbolInfosFromYAML(llvm::StringRef Yaml); } // namespace find_all_symbols } // namespace clang Index: include-fixer/find-all-symbols/SymbolInfo.cpp =================================================================== --- include-fixer/find-all-symbols/SymbolInfo.cpp +++ include-fixer/find-all-symbols/SymbolInfo.cpp @@ -18,22 +18,24 @@ 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 { - 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 { + 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 @@ SymbolInfo::SymbolInfo(llvm::StringRef Name, SymbolKind Type, llvm::StringRef FilePath, int LineNumber, - const std::vector &Contexts, - unsigned NumOccurrences) + const std::vector &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 @@ 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 &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 ReadSymbolInfosFromYAML(llvm::StringRef Yaml) { - std::vector Symbols; +std::vector ReadSymbolInfosFromYAML(llvm::StringRef Yaml) { + std::vector Symbols; llvm::yaml::Input yin(Yaml); yin >> Symbols; return Symbols; Index: include-fixer/find-all-symbols/SymbolReporter.h =================================================================== --- include-fixer/find-all-symbols/SymbolReporter.h +++ include-fixer/find-all-symbols/SymbolReporter.h @@ -20,8 +20,8 @@ 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 Index: include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp =================================================================== --- include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp +++ include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp @@ -62,38 +62,30 @@ 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 reportSymbol(StringRef FileName, const SymbolInfo &Symbol) override { - Symbols[FileName].insert(Symbol); + 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); } - -private: - std::map> Symbols; }; bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) { std::error_code EC; - std::map SymbolToNumOccurrences; + SymbolInfo::SignalMap Symbols; std::mutex SymbolMutex; - auto AddSymbols = [&](ArrayRef Symbols) { + auto AddSymbols = [&](ArrayRef NewSymbols) { // Synchronize set accesses. std::unique_lock 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 @@ llvm::errs() << "Can't open " << Path << "\n"; return; } - std::vector Symbols = + std::vector 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 @@ << '\n'; return false; } - std::set 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; } Index: include-fixer/tool/ClangIncludeFixer.cpp =================================================================== --- include-fixer/tool/ClangIncludeFixer.cpp +++ include-fixer/tool/ClangIncludeFixer.cpp @@ -158,6 +158,8 @@ std::unique_ptr createSymbolIndexManager(StringRef FilePath) { + using find_all_symbols::SymbolInfo; + auto SymbolIndexMgr = llvm::make_unique(); switch (DatabaseFormat) { case fixed: { @@ -167,17 +169,19 @@ std::map> SymbolsMap; SmallVector SemicolonSplits; StringRef(Input).split(SemicolonSplits, ";"); - std::vector Symbols; + std::vector Symbols; for (StringRef Pair : SemicolonSplits) { auto Split = Pair.split('='); std::vector Headers; SmallVector 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(E - I), + /*Used=*/0)}); } SymbolIndexMgr->addSymbolIndex([=]() { return llvm::make_unique(Symbols); Index: test/include-fixer/Inputs/fake_yaml_db.yaml =================================================================== --- test/include-fixer/Inputs/fake_yaml_db.yaml +++ test/include-fixer/Inputs/fake_yaml_db.yaml @@ -8,7 +8,8 @@ FilePath: foo.h LineNumber: 1 Type: Class -NumOccurrences: 1 +Seen: 1 +Used: 0 --- Name: bar Contexts: @@ -19,7 +20,8 @@ FilePath: ../include/bar.h LineNumber: 1 Type: Class -NumOccurrences: 1 +Seen: 1 +Used: 0 --- Name: bar Contexts: @@ -30,7 +32,8 @@ FilePath: ../include/bar.h LineNumber: 2 Type: Class -NumOccurrences: 3 +Seen: 3 +Used: 0 --- Name: bar Contexts: @@ -41,14 +44,16 @@ 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 @@ FilePath: test/include-fixer/baz.h LineNumber: 1 Type: Class -NumOccurrences: 1 +Seen: 1 +Used: 0 Index: test/include-fixer/Inputs/merge/a.yaml =================================================================== --- test/include-fixer/Inputs/merge/a.yaml +++ test/include-fixer/Inputs/merge/a.yaml @@ -6,7 +6,8 @@ FilePath: foo.h LineNumber: 1 Type: Class -NumOccurrences: 1 +Seen: 1 +Used: 1 ... --- Name: bar @@ -16,5 +17,6 @@ FilePath: ../include/bar.h LineNumber: 1 Type: Class -NumOccurrences: 1 +Seen: 1 +Used: 2 ... Index: test/include-fixer/Inputs/merge/b.yaml =================================================================== --- test/include-fixer/Inputs/merge/b.yaml +++ test/include-fixer/Inputs/merge/b.yaml @@ -6,7 +6,8 @@ FilePath: foo.h LineNumber: 1 Type: Class -NumOccurrences: 1 +Seen: 1 +Used: 2 ... --- Name: bar @@ -16,5 +17,6 @@ FilePath: ../include/barbar.h LineNumber: 1 Type: Class -NumOccurrences: 1 +Seen: 1 +Used: 0 ... Index: test/include-fixer/merge.test =================================================================== --- test/include-fixer/merge.test +++ test/include-fixer/merge.test @@ -9,7 +9,8 @@ FilePath: ../include/bar.h LineNumber: 1 Type: Class -NumOccurrences: 1 +Seen: 1 +Used: 1 ... --- Name: bar @@ -19,7 +20,8 @@ FilePath: ../include/barbar.h LineNumber: 1 Type: Class -NumOccurrences: 1 +Seen: 1 +Used: 0 ... --- Name: foo @@ -29,5 +31,6 @@ FilePath: foo.h LineNumber: 1 Type: Class -NumOccurrences: 2 +Seen: 2 +Used: 2 ... Index: unittests/include-fixer/IncludeFixerTest.cpp =================================================================== --- unittests/include-fixer/IncludeFixerTest.cpp +++ unittests/include-fixer/IncludeFixerTest.cpp @@ -19,6 +19,7 @@ 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 std::string runIncludeFixer( StringRef Code, const std::vector &ExtraArgs = std::vector()) { - std::vector Symbols = { - SymbolInfo("string", SymbolInfo::SymbolKind::Class, "", 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 Symbols = { + {SymbolInfo("string", SymbolInfo::SymbolKind::Class, "", 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(); - SymbolIndexMgr->addSymbolIndex([=]() { - return llvm::make_unique(Symbols); - }); + auto SymbolIndexMgr = llvm::make_unique(); + SymbolIndexMgr->addSymbolIndex( + [=]() { return llvm::make_unique(Symbols); }); std::vector FixerContexts; IncludeFixerActionFactory Factory(*SymbolIndexMgr, FixerContexts, "llvm"); @@ -96,15 +104,14 @@ 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(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); } Index: unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp =================================================================== --- unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp +++ unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp @@ -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 @@ 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 Symbols; + SymbolInfo::SignalMap Symbols; }; class FindAllSymbolsTest : public ::testing::Test { @@ -58,7 +62,9 @@ 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 InMemoryFileSystem( new vfs::InMemoryFileSystem); llvm::IntrusiveRefCntPtr Files( @@ -88,7 +94,7 @@ InMemoryFileSystem->addFile(InternalHeader, 0, llvm::MemoryBuffer::getMemBuffer(InternalCode)); - std::unique_ptr Factory( + std::unique_ptr Factory( new FindAllSymbolsActionFactory(&Reporter, &RegexMap)); tooling::ToolInvocation Invocation( @@ -98,7 +104,7 @@ std::make_shared()); InMemoryFileSystem->addFile(HeaderName, 0, - llvm::MemoryBuffer::getMemBuffer(Code)); + llvm::MemoryBuffer::getMemBuffer(HeaderCode)); std::string Content = "#include\"" + std::string(HeaderName) + "\"\n" @@ -118,6 +124,7 @@ 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 @@ }; 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 @@ }; }; // )"; - 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 - class T_TEMP { + struct T_TEMP { template struct rebind { typedef T_TEMP<_Tp1> other; }; }; @@ -222,11 +251,15 @@ // Ignore specialization. template <> class Observer {}; )"; - runFindAllSymbols(Code); + static const char Main[] = R"( + extern T_TEMP::rebind 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 @@ template void f() {}; template<> void f() {}; )"; - runFindAllSymbols(Code); + runFindAllSymbols(Code, ""); SymbolInfo Symbol = SymbolInfo("Class", SymbolInfo::SymbolKind::Class, HeaderName, 4, {}); EXPECT_TRUE(hasSymbol(Symbol)); @@ -252,7 +285,7 @@ } 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 @@ } // 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 struct vector{}; + vector 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 @@ }; 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, 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));