Index: LICENSE.TXT =================================================================== --- LICENSE.TXT +++ LICENSE.TXT @@ -60,4 +60,3 @@ Program Directory ------- --------- clang-tidy clang-tidy/cert -clang-tidy clang-tidy/hicpp Index: change-namespace/ChangeNamespace.h =================================================================== --- change-namespace/ChangeNamespace.h +++ change-namespace/ChangeNamespace.h @@ -50,7 +50,6 @@ // files matching `FilePattern`. ChangeNamespaceTool( llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, - llvm::ArrayRef WhiteListedSymbolPatterns, std::map *FileToReplacements, llvm::StringRef FallbackStyle = "LLVM"); @@ -165,9 +164,6 @@ // CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have // been processed so that we don't handle them twice. llvm::SmallPtrSet ProcessedFuncRefs; - // Patterns of symbol names whose references are not expected to be updated - // when changing namespaces around them. - std::vector WhiteListedSymbolRegexes; }; } // namespace change_namespace Index: change-namespace/ChangeNamespace.cpp =================================================================== --- change-namespace/ChangeNamespace.cpp +++ change-namespace/ChangeNamespace.cpp @@ -28,14 +28,6 @@ return Result; } -// Given "a::b::c", returns {"a", "b", "c"}. -llvm::SmallVector splitSymbolName(llvm::StringRef Name) { - llvm::SmallVector Splitted; - Name.split(Splitted, "::", /*MaxSplit=*/-1, - /*KeepEmpty=*/false); - return Splitted; -} - SourceLocation startLocationForType(TypeLoc TLoc) { // For elaborated types (e.g. `struct a::A`) we want the portion after the // `struct` but including the namespace qualifier, `a::`. @@ -76,7 +68,9 @@ return nullptr; const auto *CurrentContext = llvm::cast(InnerNs); const auto *CurrentNs = InnerNs; - auto PartialNsNameSplitted = splitSymbolName(PartialNsName); + llvm::SmallVector PartialNsNameSplitted; + PartialNsName.split(PartialNsNameSplitted, "::", /*MaxSplit=*/-1, + /*KeepEmpty=*/false); while (!PartialNsNameSplitted.empty()) { // Get the inner-most namespace in CurrentContext. while (CurrentContext && !llvm::isa(CurrentContext)) @@ -214,8 +208,12 @@ if (DeclName.find(':') == llvm::StringRef::npos) return DeclName; - auto NsNameSplitted = splitSymbolName(NsName); - auto DeclNsSplitted = splitSymbolName(DeclName); + llvm::SmallVector NsNameSplitted; + NsName.split(NsNameSplitted, "::", /*MaxSplit=*/-1, + /*KeepEmpty=*/false); + llvm::SmallVector DeclNsSplitted; + DeclName.split(DeclNsSplitted, "::", /*MaxSplit=*/-1, + /*KeepEmpty=*/false); llvm::StringRef UnqualifiedDeclName = DeclNsSplitted.pop_back_val(); // If the Decl is in global namespace, there is no need to shorten it. if (DeclNsSplitted.empty()) @@ -251,7 +249,9 @@ std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) { if (Code.back() != '\n') Code += "\n"; - auto NsSplitted = splitSymbolName(NestedNs); + llvm::SmallVector NsSplitted; + NestedNs.split(NsSplitted, "::", /*MaxSplit=*/-1, + /*KeepEmpty=*/false); while (!NsSplitted.empty()) { // FIXME: consider code style for comments. Code = ("namespace " + NsSplitted.back() + " {\n" + Code + @@ -282,54 +282,24 @@ isNestedDeclContext(DeclCtx, D->getDeclContext())); } -// Given a qualified symbol name, returns true if the symbol will be -// incorrectly qualified without leading "::". -bool conflictInNamespace(llvm::StringRef QualifiedSymbol, - llvm::StringRef Namespace) { - auto SymbolSplitted = splitSymbolName(QualifiedSymbol.trim(":")); - assert(!SymbolSplitted.empty()); - SymbolSplitted.pop_back(); // We are only interested in namespaces. - - if (SymbolSplitted.size() > 1 && !Namespace.empty()) { - auto NsSplitted = splitSymbolName(Namespace.trim(":")); - assert(!NsSplitted.empty()); - // We do not check the outermost namespace since it would not be a conflict - // if it equals to the symbol's outermost namespace and the symbol name - // would have been shortened. - for (auto I = NsSplitted.begin() + 1, E = NsSplitted.end(); I != E; ++I) { - if (*I == SymbolSplitted.front()) - return true; - } - } - return false; -} - AST_MATCHER(EnumDecl, isScoped) { return Node.isScoped(); } -bool isTemplateParameter(TypeLoc Type) { - while (!Type.isNull()) { - if (Type.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm) - return true; - Type = Type.getNextTypeLoc(); - } - return false; -} - } // anonymous namespace ChangeNamespaceTool::ChangeNamespaceTool( llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, - llvm::ArrayRef WhiteListedSymbolPatterns, std::map *FileToReplacements, llvm::StringRef FallbackStyle) : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements), OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')), FilePattern(FilePattern), FilePatternRE(FilePattern) { FileToReplacements->clear(); - auto OldNsSplitted = splitSymbolName(OldNamespace); - auto NewNsSplitted = splitSymbolName(NewNamespace); + llvm::SmallVector OldNsSplitted; + llvm::SmallVector NewNsSplitted; + llvm::StringRef(OldNamespace).split(OldNsSplitted, "::"); + llvm::StringRef(NewNamespace).split(NewNsSplitted, "::"); // Calculates `DiffOldNamespace` and `DiffNewNamespace`. while (!OldNsSplitted.empty() && !NewNsSplitted.empty() && OldNsSplitted.front() == NewNsSplitted.front()) { @@ -338,9 +308,6 @@ } DiffOldNamespace = joinNamespaces(OldNsSplitted); DiffNewNamespace = joinNamespaces(NewNsSplitted); - - for (const auto &Pattern : WhiteListedSymbolPatterns) - WhiteListedSymbolRegexes.emplace_back(Pattern); } void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) { @@ -705,18 +672,19 @@ const NamedDecl *FwdDecl) { SourceLocation Start = FwdDecl->getLocStart(); SourceLocation End = FwdDecl->getLocEnd(); - const SourceManager &SM = *Result.SourceManager; SourceLocation AfterSemi = Lexer::findLocationAfterToken( - End, tok::semi, SM, Result.Context->getLangOpts(), + End, tok::semi, *Result.SourceManager, Result.Context->getLangOpts(), /*SkipTrailingWhitespaceAndNewLine=*/true); if (AfterSemi.isValid()) End = AfterSemi.getLocWithOffset(-1); // Delete the forward declaration from the code to be moved. - addReplacementOrDie(Start, End, "", SM, &FileToReplacements); + addReplacementOrDie(Start, End, "", *Result.SourceManager, + &FileToReplacements); llvm::StringRef Code = Lexer::getSourceText( - CharSourceRange::getTokenRange(SM.getSpellingLoc(Start), - SM.getSpellingLoc(End)), - SM, Result.Context->getLangOpts()); + CharSourceRange::getTokenRange( + Result.SourceManager->getSpellingLoc(Start), + Result.SourceManager->getSpellingLoc(End)), + *Result.SourceManager, Result.Context->getLangOpts()); // Insert the forward declaration back into the old namespace after moving the // code from old namespace to new namespace. // Insertion information is stored in `InsertFwdDecls` and actual @@ -725,9 +693,8 @@ const auto *NsDecl = Result.Nodes.getNodeAs("ns_decl"); // The namespace contains the forward declaration, so it must not be empty. assert(!NsDecl->decls_empty()); - const auto Insertion = createInsertion( - getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()), - Code, SM); + const auto Insertion = createInsertion(NsDecl->decls_begin()->getLocStart(), + Code, *Result.SourceManager); InsertForwardDeclaration InsertFwd; InsertFwd.InsertionOffset = Insertion.getOffset(); InsertFwd.ForwardDeclText = Insertion.getReplacementText().str(); @@ -769,9 +736,6 @@ Result.SourceManager->getSpellingLoc(End)), *Result.SourceManager, Result.Context->getLangOpts()); std::string FromDeclName = FromDecl->getQualifiedNameAsString(); - for (llvm::Regex &RE : WhiteListedSymbolRegexes) - if (RE.match(FromDeclName)) - return; std::string ReplaceName = getShortestQualifiedNameInNamespace(FromDeclName, NewNs); // Checks if there is any using namespace declarations that can shorten the @@ -845,8 +809,7 @@ return; // If the reference need to be fully-qualified, add a leading "::" unless // NewNamespace is the global namespace. - if (ReplaceName == FromDeclName && !NewNamespace.empty() && - conflictInNamespace(ReplaceName, NewNamespace)) + if (ReplaceName == FromDeclName && !NewNamespace.empty()) ReplaceName = "::" + ReplaceName; addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager, &FileToReplacements); @@ -863,8 +826,6 @@ // Types of CXXCtorInitializers do not need to be fixed. if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type)) return; - if (isTemplateParameter(Type)) - return; // The declaration which this TypeLoc refers to. const auto *FromDecl = Result.Nodes.getNodeAs("from_decl"); // `hasDeclaration` gives underlying declaration, but if the type is Index: change-namespace/tool/ClangChangeNamespace.cpp =================================================================== --- change-namespace/tool/ClangChangeNamespace.cpp +++ change-namespace/tool/ClangChangeNamespace.cpp @@ -73,29 +73,6 @@ cl::desc("The style name used for reformatting."), cl::init("LLVM"), cl::cat(ChangeNamespaceCategory)); -cl::opt WhiteListFile( - "whitelist_file", - cl::desc("A file containing regexes of symbol names that are not expected " - "to be updated when changing namespaces around them."), - cl::init(""), cl::cat(ChangeNamespaceCategory)); - -llvm::ErrorOr> GetWhiteListedSymbolPatterns() { - std::vector Patterns; - if (WhiteListFile.empty()) - return Patterns; - - llvm::SmallVector Lines; - llvm::ErrorOr> File = - llvm::MemoryBuffer::getFile(WhiteListFile); - if (!File) - return File.getError(); - llvm::StringRef Content = File.get()->getBuffer(); - Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - for (auto Line : Lines) - Patterns.push_back(Line.trim()); - return Patterns; -} - } // anonymous namespace int main(int argc, const char **argv) { @@ -104,16 +81,8 @@ ChangeNamespaceCategory); const auto &Files = OptionsParser.getSourcePathList(); tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files); - llvm::ErrorOr> WhiteListPatterns = - GetWhiteListedSymbolPatterns(); - if (!WhiteListPatterns) { - llvm::errs() << "Failed to open whitelist file " << WhiteListFile << ". " - << WhiteListPatterns.getError().message() << "\n"; - return 1; - } change_namespace::ChangeNamespaceTool NamespaceTool( - OldNamespace, NewNamespace, FilePattern, *WhiteListPatterns, - &Tool.getReplacements(), Style); + OldNamespace, NewNamespace, FilePattern, &Tool.getReplacements(), Style); ast_matchers::MatchFinder Finder; NamespaceTool.registerMatchers(&Finder); std::unique_ptr Factory = Index: clang-move/ClangMove.cpp =================================================================== --- clang-move/ClangMove.cpp +++ clang-move/ClangMove.cpp @@ -168,21 +168,6 @@ ClangMoveTool *MoveTool; }; -class VarDeclarationMatch : public MatchFinder::MatchCallback { -public: - explicit VarDeclarationMatch(ClangMoveTool *MoveTool) - : MoveTool(MoveTool) {} - - void run(const MatchFinder::MatchResult &Result) override { - const auto *VD = Result.Nodes.getNodeAs("var"); - assert(VD); - MoveDeclFromOldFileToNewFile(MoveTool, VD); - } - -private: - ClangMoveTool *MoveTool; -}; - class TypeAliasMatch : public MatchFinder::MatchCallback { public: explicit TypeAliasMatch(ClangMoveTool *MoveTool) @@ -633,11 +618,6 @@ .bind("function"), MatchCallbacks.back().get()); - MatchCallbacks.push_back(llvm::make_unique(this)); - Finder->addMatcher( - varDecl(InOldFiles, *HasAnySymbolNames, TopLevelDecl).bind("var"), - MatchCallbacks.back().get()); - // Match enum definition in old.h. Enum helpers (which are defined in old.cc) // will not be moved for now no matter whether they are used or not. MatchCallbacks.push_back(llvm::make_unique(this)); @@ -872,10 +852,7 @@ for (const auto *Decl : UnremovedDeclsInOldHeader) { auto Kind = Decl->getKind(); const std::string QualifiedName = Decl->getQualifiedNameAsString(); - if (Kind == Decl::Kind::Var) - Reporter->reportDeclaration(QualifiedName, "Variable"); - else if (Kind == Decl::Kind::Function || - Kind == Decl::Kind::FunctionTemplate) + if (Kind == Decl::Kind::Function || Kind == Decl::Kind::FunctionTemplate) Reporter->reportDeclaration(QualifiedName, "Function"); else if (Kind == Decl::Kind::ClassTemplate || Kind == Decl::Kind::CXXRecord) @@ -906,7 +883,6 @@ case Decl::Kind::Typedef: case Decl::Kind::TypeAlias: case Decl::Kind::TypeAliasTemplate: - case Decl::Kind::Var: return true; default: return false; Index: clang-tidy/CMakeLists.txt =================================================================== --- clang-tidy/CMakeLists.txt +++ clang-tidy/CMakeLists.txt @@ -26,17 +26,17 @@ clangToolingCore ) +add_subdirectory(tool) +add_subdirectory(plugin) add_subdirectory(boost) add_subdirectory(cert) +add_subdirectory(llvm) add_subdirectory(cppcoreguidelines) add_subdirectory(google) -add_subdirectory(hicpp) -add_subdirectory(llvm) add_subdirectory(misc) add_subdirectory(modernize) add_subdirectory(mpi) add_subdirectory(performance) -add_subdirectory(plugin) add_subdirectory(readability) -add_subdirectory(tool) +add_subdirectory(safety) add_subdirectory(utils) Index: clang-tidy/ClangTidy.cpp =================================================================== --- clang-tidy/ClangTidy.cpp +++ clang-tidy/ClangTidy.cpp @@ -183,6 +183,7 @@ } void Finish() { + // FIXME: Run clang-format on changes. if (ApplyFixes && TotalFixes > 0) { Rewriter Rewrite(SourceMgr, LangOpts); for (const auto &FileAndReplacements : FileReplacements) { @@ -196,28 +197,19 @@ continue; } StringRef Code = Buffer.get()->getBuffer(); - auto Style = format::getStyle(FormatStyle, File, "none"); + auto Style = format::getStyle("file", File, FormatStyle); if (!Style) { llvm::errs() << llvm::toString(Style.takeError()) << "\n"; continue; } - llvm::Expected Replacements = + llvm::Expected CleanReplacements = format::cleanupAroundReplacements(Code, FileAndReplacements.second, *Style); - if (!Replacements) { - llvm::errs() << llvm::toString(Replacements.takeError()) << "\n"; + if (!CleanReplacements) { + llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n"; continue; } - if (llvm::Expected FormattedReplacements = - format::formatReplacements(Code, *Replacements, *Style)) { - Replacements = std::move(FormattedReplacements); - if (!Replacements) - llvm_unreachable("!Replacements"); - } else { - llvm::errs() << llvm::toString(FormattedReplacements.takeError()) - << ". Skipping formatting.\n"; - } - if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) { + if (!tooling::applyAllReplacements(CleanReplacements.get(), Rewrite)) { llvm::errs() << "Can't apply replacements for file " << File << "\n"; } } Index: clang-tidy/cppcoreguidelines/NoMallocCheck.h =================================================================== --- clang-tidy/cppcoreguidelines/NoMallocCheck.h +++ clang-tidy/cppcoreguidelines/NoMallocCheck.h @@ -27,32 +27,14 @@ /// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-no-malloc.html class NoMallocCheck : public ClangTidyCheck { public: - /// Construct Checker and read in configuration for function names. NoMallocCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - AllocList(Options.get("Allocations", "::malloc;::calloc")), - ReallocList(Options.get("Reallocations", "::realloc")), - DeallocList(Options.get("Deallocations", "::free")) {} - - /// Make configuration of checker discoverable. - void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + : ClangTidyCheck(Name, Context) {} /// Registering for malloc, calloc, realloc and free calls. void registerMatchers(ast_matchers::MatchFinder *Finder) override; /// Checks matched function calls and gives suggestion to modernize the code. void check(const ast_matchers::MatchFinder::MatchResult &Result) override; - -private: - /// Semicolon-seperated list of fully qualified names of memory allocation - /// functions the check warns about. Defaults to `::malloc;::calloc`. - const std::string AllocList; - /// Semicolon-seperated list of fully qualified names of memory reallocation - /// functions the check warns about. Defaults to `::realloc`. - const std::string ReallocList; - /// Semicolon-seperated list of fully qualified names of memory deallocation - /// functions the check warns about. Defaults to `::free`. - const std::string DeallocList; }; } // namespace cppcoreguidelines Index: clang-tidy/cppcoreguidelines/NoMallocCheck.cpp =================================================================== --- clang-tidy/cppcoreguidelines/NoMallocCheck.cpp +++ clang-tidy/cppcoreguidelines/NoMallocCheck.cpp @@ -8,63 +8,43 @@ //===----------------------------------------------------------------------===// #include "NoMallocCheck.h" -#include "../utils/Matchers.h" -#include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" -#include +#include #include -#include using namespace clang::ast_matchers; -using namespace clang::ast_matchers::internal; namespace clang { namespace tidy { namespace cppcoreguidelines { -namespace { -Matcher hasAnyListedName(const std::string &FunctionNames) { - const std::vector NameList = - utils::options::parseStringList(FunctionNames); - return hasAnyName(std::vector(NameList.begin(), NameList.end())); -} -} - -void NoMallocCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { - Options.store(Opts, "Allocations", AllocList); - Options.store(Opts, "Reallocations", ReallocList); - Options.store(Opts, "Deallocations", DeallocList); -} - void NoMallocCheck::registerMatchers(MatchFinder *Finder) { // C-style memory management is only problematic in C++. if (!getLangOpts().CPlusPlus) return; // Registering malloc, will suggest RAII. - Finder->addMatcher(callExpr(callee(functionDecl(hasAnyListedName(AllocList)))) - .bind("allocation"), - this); + Finder->addMatcher( + callExpr(callee(functionDecl(hasAnyName("::malloc", "::calloc")))) + .bind("aquisition"), + this); // Registering realloc calls, suggest std::vector or std::string. Finder->addMatcher( - callExpr(callee(functionDecl(hasAnyListedName(ReallocList)))) - .bind("realloc"), + callExpr(callee(functionDecl(hasName("::realloc")))).bind("realloc"), this); // Registering free calls, will suggest RAII instead. Finder->addMatcher( - callExpr(callee(functionDecl(hasAnyListedName(DeallocList)))) - .bind("free"), - this); + callExpr(callee(functionDecl(hasName("::free")))).bind("free"), this); } void NoMallocCheck::check(const MatchFinder::MatchResult &Result) { const CallExpr *Call = nullptr; StringRef Recommendation; - if ((Call = Result.Nodes.getNodeAs("allocation"))) + if ((Call = Result.Nodes.getNodeAs("aquisition"))) Recommendation = "consider a container or a smart pointer"; else if ((Call = Result.Nodes.getNodeAs("realloc"))) Recommendation = "consider std::vector or std::string"; Index: clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h =================================================================== --- clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h +++ clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h @@ -25,16 +25,14 @@ /// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-special-member-functions.html class SpecialMemberFunctionsCheck : public ClangTidyCheck { public: - SpecialMemberFunctionsCheck(StringRef Name, ClangTidyContext *Context); - void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + SpecialMemberFunctionsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void onEndOfTranslationUnit() override; enum class SpecialMemberFunctionKind : uint8_t { Destructor, - DefaultDestructor, - NonDefaultDestructor, CopyConstructor, CopyAssignment, MoveConstructor, @@ -48,12 +46,6 @@ llvm::SmallVector>; private: - void checkForMissingMembers( - const ClassDefId &ID, - llvm::ArrayRef DefinedSpecialMembers); - - const bool AllowMissingMoveFunctions; - const bool AllowSoleDefaultDtor; ClassDefiningSpecialMembersMap ClassWithSpecialMembers; }; @@ -91,7 +83,7 @@ return Val.first.getRawEncoding() + SecondHash(Val.second); } - static bool isEqual(const ClassDefId &LHS, const ClassDefId &RHS) { + static bool isEqual(ClassDefId LHS, ClassDefId RHS) { if (RHS == getEmptyKey()) return LHS == getEmptyKey(); if (RHS == getTombstoneKey()) Index: clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp =================================================================== --- clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp +++ clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp @@ -22,18 +22,6 @@ namespace tidy { namespace cppcoreguidelines { -SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck( - StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - AllowMissingMoveFunctions(Options.get("AllowMissingMoveFunctions", 0)), - AllowSoleDefaultDtor(Options.get("AllowSoleDefaultDtor", 0)) {} - -void SpecialMemberFunctionsCheck::storeOptions( - ClangTidyOptions::OptionMap &Opts) { - Options.store(Opts, "AllowMissingMoveFunctions", AllowMissingMoveFunctions); - Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor); -} - void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; @@ -60,12 +48,6 @@ switch (K) { case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::Destructor: return "a destructor"; - case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind:: - DefaultDestructor: - return "a default destructor"; - case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind:: - NonDefaultDestructor: - return "a non-default destructor"; case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor: return "a copy constructor"; case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment: @@ -106,86 +88,51 @@ ClassDefId ID(MatchedDecl->getLocation(), MatchedDecl->getName()); - auto StoreMember = [this, &ID](SpecialMemberFunctionKind Kind) { - llvm::SmallVectorImpl &Members = - ClassWithSpecialMembers[ID]; - if (!llvm::is_contained(Members, Kind)) - Members.push_back(Kind); - }; - - if (const auto *Dtor = Result.Nodes.getNodeAs("dtor")) { - StoreMember(Dtor->isDefaulted() - ? SpecialMemberFunctionKind::DefaultDestructor - : SpecialMemberFunctionKind::NonDefaultDestructor); - } - std::initializer_list> - Matchers = {{"copy-ctor", SpecialMemberFunctionKind::CopyConstructor}, + Matchers = {{"dtor", SpecialMemberFunctionKind::Destructor}, + {"copy-ctor", SpecialMemberFunctionKind::CopyConstructor}, {"copy-assign", SpecialMemberFunctionKind::CopyAssignment}, {"move-ctor", SpecialMemberFunctionKind::MoveConstructor}, {"move-assign", SpecialMemberFunctionKind::MoveAssignment}}; for (const auto &KV : Matchers) if (Result.Nodes.getNodeAs(KV.first)) { - StoreMember(KV.second); + SpecialMemberFunctionKind Kind = KV.second; + llvm::SmallVectorImpl &Members = + ClassWithSpecialMembers[ID]; + if (find(Members, Kind) == Members.end()) + Members.push_back(Kind); } } void SpecialMemberFunctionsCheck::onEndOfTranslationUnit() { - for (const auto &C : ClassWithSpecialMembers) { - checkForMissingMembers(C.first, C.second); + llvm::SmallVector AllSpecialMembers = { + SpecialMemberFunctionKind::Destructor, + SpecialMemberFunctionKind::CopyConstructor, + SpecialMemberFunctionKind::CopyAssignment}; + + if (getLangOpts().CPlusPlus11) { + AllSpecialMembers.push_back(SpecialMemberFunctionKind::MoveConstructor); + AllSpecialMembers.push_back(SpecialMemberFunctionKind::MoveAssignment); } -} -void SpecialMemberFunctionsCheck::checkForMissingMembers( - const ClassDefId &ID, - llvm::ArrayRef DefinedMembers) { - llvm::SmallVector MissingMembers; - - auto HasMember = [&](SpecialMemberFunctionKind Kind) { - return llvm::is_contained(DefinedMembers, Kind); - }; - - auto RequireMember = [&](SpecialMemberFunctionKind Kind) { - if (!HasMember(Kind)) - MissingMembers.push_back(Kind); - }; - - bool RequireThree = - HasMember(SpecialMemberFunctionKind::NonDefaultDestructor) || - (!AllowSoleDefaultDtor && - HasMember(SpecialMemberFunctionKind::DefaultDestructor)) || - HasMember(SpecialMemberFunctionKind::CopyConstructor) || - HasMember(SpecialMemberFunctionKind::CopyAssignment) || - HasMember(SpecialMemberFunctionKind::MoveConstructor) || - HasMember(SpecialMemberFunctionKind::MoveAssignment); - - bool RequireFive = (!AllowMissingMoveFunctions && RequireThree && - getLangOpts().CPlusPlus11) || - HasMember(SpecialMemberFunctionKind::MoveConstructor) || - HasMember(SpecialMemberFunctionKind::MoveAssignment); - - if (RequireThree) { - if (!HasMember(SpecialMemberFunctionKind::DefaultDestructor) && - !HasMember(SpecialMemberFunctionKind::NonDefaultDestructor)) - MissingMembers.push_back(SpecialMemberFunctionKind::Destructor); - - RequireMember(SpecialMemberFunctionKind::CopyConstructor); - RequireMember(SpecialMemberFunctionKind::CopyAssignment); - } + for (const auto &C : ClassWithSpecialMembers) { + const auto &DefinedSpecialMembers = C.second; - if (RequireFive) { - assert(RequireThree); - RequireMember(SpecialMemberFunctionKind::MoveConstructor); - RequireMember(SpecialMemberFunctionKind::MoveAssignment); - } + if (DefinedSpecialMembers.size() == AllSpecialMembers.size()) + continue; - if (!MissingMembers.empty()) - diag(ID.first, "class '%0' defines %1 but does not define %2") - << ID.second << cppcoreguidelines::join(DefinedMembers, " and ") - << cppcoreguidelines::join(MissingMembers, " or "); -} + llvm::SmallVector UndefinedSpecialMembers; + std::set_difference(AllSpecialMembers.begin(), AllSpecialMembers.end(), + DefinedSpecialMembers.begin(), + DefinedSpecialMembers.end(), + std::back_inserter(UndefinedSpecialMembers)); + diag(C.first.first, "class '%0' defines %1 but does not define %2") + << C.first.second << join(DefinedSpecialMembers, " and ") + << join(UndefinedSpecialMembers, " or "); + } +} } // namespace cppcoreguidelines } // namespace tidy } // namespace clang Index: clang-tidy/google/AvoidCStyleCastsCheck.cpp =================================================================== --- clang-tidy/google/AvoidCStyleCastsCheck.cpp +++ clang-tidy/google/AvoidCStyleCastsCheck.cpp @@ -35,16 +35,19 @@ } static bool needsConstCast(QualType SourceType, QualType DestType) { - while ((SourceType->isPointerType() && DestType->isPointerType()) || - (SourceType->isReferenceType() && DestType->isReferenceType())) { - SourceType = SourceType->getPointeeType(); - DestType = DestType->getPointeeType(); + for (;;) { if (SourceType.isConstQualified() && !DestType.isConstQualified()) { return (SourceType->isPointerType() == DestType->isPointerType()) && (SourceType->isReferenceType() == DestType->isReferenceType()); } + if ((SourceType->isPointerType() && DestType->isPointerType()) || + (SourceType->isReferenceType() && DestType->isReferenceType())) { + SourceType = SourceType->getPointeeType(); + DestType = DestType->getPointeeType(); + } else { + return false; + } } - return false; } static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2) { @@ -58,6 +61,7 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { const auto *CastExpr = Result.Nodes.getNodeAs("cast"); + auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(), CastExpr->getRParenLoc()); // Ignore casts in macros. @@ -75,10 +79,8 @@ T->isMemberFunctionPointerType(); }; - const QualType DestTypeAsWritten = - CastExpr->getTypeAsWritten().getUnqualifiedType(); - const QualType SourceTypeAsWritten = - CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType(); + const QualType DestTypeAsWritten = CastExpr->getTypeAsWritten(); + const QualType SourceTypeAsWritten = CastExpr->getSubExprAsWritten()->getType(); const QualType SourceType = SourceTypeAsWritten.getCanonicalType(); const QualType DestType = DestTypeAsWritten.getCanonicalType(); @@ -95,6 +97,11 @@ << FixItHint::CreateRemoval(ParenRange); return; } + if (SourceType == DestType) { + diag(CastExpr->getLocStart(), + "possibly redundant cast between typedefs of the same type"); + return; + } } // The rest of this check is only relevant to C++. @@ -108,11 +115,9 @@ // compiled as C++. if (getCurrentMainFile().endswith(".c")) return; - - SourceManager &SM = *Result.SourceManager; - // Ignore code in .c files #included in other files (which shouldn't be done, // but people still do this for test and other purposes). + SourceManager &SM = *Result.SourceManager; if (SM.getFilename(SM.getSpellingLoc(CastExpr->getLocStart())).endswith(".c")) return; @@ -140,7 +145,21 @@ }; auto ReplaceWithNamedCast = [&](StringRef CastType) { Diag << CastType; - ReplaceWithCast((CastType + "<" + DestTypeString + ">").str()); + std::string CastText = (CastType + "<" + DestTypeString + ">").str(); + ReplaceWithCast(std::move(CastText)); + }; + auto ReplaceWithCtorCall = [&]() { + std::string CastText; + if (!DestTypeAsWritten.hasQualifiers() && + DestTypeAsWritten->isRecordType() && + !DestTypeAsWritten->isElaboratedTypeSpecifier()) { + Diag << "constructor call syntax"; + CastText = DestTypeString.str(); // FIXME: Validate DestTypeString, maybe. + } else { + Diag << "static_cast"; + CastText = ("static_cast<" + DestTypeString + ">").str(); + } + ReplaceWithCast(std::move(CastText)); }; // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics. @@ -149,26 +168,13 @@ ReplaceWithNamedCast("static_cast"); return; case CK_ConstructorConversion: - if (!CastExpr->getTypeAsWritten().hasQualifiers() && - DestTypeAsWritten->isRecordType() && - !DestTypeAsWritten->isElaboratedTypeSpecifier()) { - Diag << "constructor call syntax"; - // FIXME: Validate DestTypeString, maybe. - ReplaceWithCast(DestTypeString.str()); - } else { - ReplaceWithNamedCast("static_cast"); - } + ReplaceWithCtorCall(); return; case CK_NoOp: if (FnToFnCast) { ReplaceWithNamedCast("static_cast"); return; } - if (SourceType == DestType) { - Diag << "static_cast (if needed, the cast may be redundant)"; - ReplaceWithCast(("static_cast<" + DestTypeString + ">").str()); - return; - } if (needsConstCast(SourceType, DestType) && pointedUnqualifiedTypesAreEqual(SourceType, DestType)) { ReplaceWithNamedCast("const_cast"); @@ -198,10 +204,7 @@ case CK_BitCast: // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement. if (!needsConstCast(SourceType, DestType)) { - if (SourceType->isVoidPointerType()) - ReplaceWithNamedCast("static_cast"); - else - ReplaceWithNamedCast("reinterpret_cast"); + ReplaceWithNamedCast("reinterpret_cast"); return; } break; Index: clang-tidy/google/ExplicitConstructorCheck.cpp =================================================================== --- clang-tidy/google/ExplicitConstructorCheck.cpp +++ clang-tidy/google/ExplicitConstructorCheck.cpp @@ -24,15 +24,12 @@ // provide any benefit to other languages, despite being benign. if (!getLangOpts().CPlusPlus) return; - Finder->addMatcher( - cxxConstructorDecl(unless(anyOf(isImplicit(), // Compiler-generated. - isDeleted(), isInstantiated()))) - .bind("ctor"), - this); + Finder->addMatcher(cxxConstructorDecl(unless(isInstantiated())).bind("ctor"), + this); Finder->addMatcher( cxxConversionDecl(unless(anyOf(isExplicit(), // Already marked explicit. isImplicit(), // Compiler-generated. - isDeleted(), isInstantiated()))) + isInstantiated()))) .bind("conversion"), this); @@ -102,8 +99,10 @@ } const auto *Ctor = Result.Nodes.getNodeAs("ctor"); - if (Ctor->isOutOfLine() || Ctor->getNumParams() == 0 || - Ctor->getMinRequiredArguments() > 1) + // Do not be confused: isExplicit means 'explicit' keyword is present, + // isImplicit means that it's a compiler-generated constructor. + if (Ctor->isOutOfLine() || Ctor->isImplicit() || Ctor->isDeleted() || + Ctor->getNumParams() == 0 || Ctor->getMinRequiredArguments() > 1) return; bool takesInitializerList = isStdInitializerList( Index: clang-tidy/hicpp/CMakeLists.txt =================================================================== --- clang-tidy/hicpp/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -set(LLVM_LINK_COMPONENTS support) - -add_clang_library(clangTidyHICPPModule - NoAssemblerCheck.cpp - HICPPTidyModule.cpp - - LINK_LIBS - clangAST - clangASTMatchers - clangBasic - clangLex - clangTidy - clangTidyUtils - ) Index: clang-tidy/hicpp/HICPPTidyModule.cpp =================================================================== --- clang-tidy/hicpp/HICPPTidyModule.cpp +++ /dev/null @@ -1,38 +0,0 @@ -//===------- HICPPTidyModule.cpp - clang-tidy -----------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "../ClangTidy.h" -#include "../ClangTidyModule.h" -#include "../ClangTidyModuleRegistry.h" -#include "NoAssemblerCheck.h" - -namespace clang { -namespace tidy { -namespace hicpp { - -class HICPPModule : public ClangTidyModule { -public: - void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { - CheckFactories.registerCheck( - "hicpp-no-assembler"); - } -}; - -// Register the HICPPModule using this statically initialized variable. -static ClangTidyModuleRegistry::Add - X("hicpp-module", "Adds High-Integrity C++ checks."); - -} // namespace hicpp - -// This anchor is used to force the linker to link in the generated object file -// and thus register the HICPPModule. -volatile int HICPPModuleAnchorSource = 0; - -} // namespace tidy -} // namespace clang Index: clang-tidy/hicpp/LICENSE.TXT =================================================================== --- clang-tidy/hicpp/LICENSE.TXT +++ /dev/null @@ -1,12 +0,0 @@ ------------------------------------------------------------------------------- -clang-tidy High-Integrity C++ Files ------------------------------------------------------------------------------- -All clang-tidy files are licensed under the LLVM license with the following -additions: - -Any file referencing a High-Integrity C++ Coding guideline: - -HIC++ Coding Standard as created by PRQA. - -Please see http://www.codingstandard.com/section/conditions-of-use/ for more -information. Index: clang-tidy/hicpp/NoAssemblerCheck.h =================================================================== --- clang-tidy/hicpp/NoAssemblerCheck.h +++ /dev/null @@ -1,35 +0,0 @@ -//===--- NoAssemblerCheck.h - clang-tidy-------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace hicpp { - -/// Find assembler statements. No fix is offered. -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/hicpp-no-assembler.html -class NoAssemblerCheck : public ClangTidyCheck { -public: - NoAssemblerCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; -}; - -} // namespace hicpp -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H Index: clang-tidy/hicpp/NoAssemblerCheck.cpp =================================================================== --- clang-tidy/hicpp/NoAssemblerCheck.cpp +++ /dev/null @@ -1,51 +0,0 @@ -//===--- NoAssemblerCheck.cpp - clang-tidy---------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "NoAssemblerCheck.h" -#include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" - -using namespace clang::ast_matchers; - -namespace clang { -namespace ast_matchers { -AST_MATCHER(VarDecl, isAsm) { return Node.hasAttr(); } -const internal::VariadicDynCastAllOfMatcher - fileScopeAsmDecl; -} -} - -namespace clang { -namespace tidy { -namespace hicpp { - -void NoAssemblerCheck::registerMatchers(MatchFinder *Finder) { - Finder->addMatcher(asmStmt().bind("asm-stmt"), this); - Finder->addMatcher(fileScopeAsmDecl().bind("asm-file-scope"), this); - Finder->addMatcher(varDecl(isAsm()).bind("asm-var"), this); -} - -void NoAssemblerCheck::check(const MatchFinder::MatchResult &Result) { - SourceLocation ASMLocation; - if (const auto *ASM = Result.Nodes.getNodeAs("asm-stmt")) - ASMLocation = ASM->getAsmLoc(); - else if (const auto *ASM = - Result.Nodes.getNodeAs("asm-file-scope")) - ASMLocation = ASM->getAsmLoc(); - else if (const auto *ASM = Result.Nodes.getNodeAs("asm-var")) - ASMLocation = ASM->getLocation(); - else - llvm_unreachable("Unhandled case in matcher."); - - diag(ASMLocation, "do not use inline assembler in safety-critical code"); -} - -} // namespace hicpp -} // namespace tidy -} // namespace clang Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangTidyMiscModule ArgumentCommentCheck.cpp AssertSideEffectCheck.cpp + ForwardingReferenceOverloadCheck.cpp MisplacedConstCheck.cpp UnconventionalAssignOperatorCheck.cpp BoolPointerImplicitConversionCheck.cpp Index: clang-tidy/misc/ForwardingReferenceOverloadCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/ForwardingReferenceOverloadCheck.h @@ -0,0 +1,42 @@ +//===--- ForwardingReferenceOverloadCheck.h - clang-tidy---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// The checker looks for constructors that can act as copy or move constructors +/// through their forwarding reference parameters. If a non const lvalue +/// reference is passed to the constructor, the forwarding reference parameter +/// can be a perfect match while the const reference parameter of the copy +/// constructor can't. The forwarding reference constructor will be called, +/// which can lead to confusion. +/// For detailed description of this problem see: Scott Meyers, Effective Modern +/// C++ Design, item 26. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-forwarding-reference-overload.html +class ForwardingReferenceOverloadCheck : public ClangTidyCheck { +public: + ForwardingReferenceOverloadCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H Index: clang-tidy/misc/ForwardingReferenceOverloadCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/ForwardingReferenceOverloadCheck.cpp @@ -0,0 +1,155 @@ +//===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ForwardingReferenceOverloadCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +namespace { +// Check if the given type is related to std::enable_if. +AST_MATCHER(QualType, isEnableIf) { + auto CheckTemplate = [](const TemplateSpecializationType *Spec) { + if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) { + return false; + } + const NamedDecl *TypeDecl = + Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl(); + return TypeDecl->isInStdNamespace() && + (TypeDecl->getName().equals("enable_if") || + TypeDecl->getName().equals("enable_if_t")); + }; + const Type *BaseType = Node.getTypePtr(); + // Case: pointer or reference to enable_if. + while (BaseType->isPointerType() || BaseType->isReferenceType()) { + BaseType = BaseType->getPointeeType().getTypePtr(); + } + // Case: type parameter dependent (enable_if>). + if (const auto *Dependent = BaseType->getAs()) { + BaseType = Dependent->getQualifier()->getAsType(); + } + if (CheckTemplate(BaseType->getAs())) { + return true; // Case: enable_if_t< >. + } else if (const auto *Elaborated = BaseType->getAs()) { + if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) { + if (CheckTemplate(Qualifier->getAs())) { + return true; // Case: enable_if< >::type. + } + } + } + return false; +} +AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument, + clang::ast_matchers::internal::Matcher, TypeMatcher) { + return Node.hasDefaultArgument() && + TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder); +} +} + +void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) { + // Forwarding references require C++11 or later. + if (!getLangOpts().CPlusPlus11) + return; + + auto ForwardingRefParm = + parmVarDecl( + hasType(qualType(rValueReferenceType(), + references(templateTypeParmType(hasDeclaration( + templateTypeParmDecl().bind("type-parm-decl")))), + unless(references(isConstQualified()))))) + .bind("parm-var"); + + DeclarationMatcher findOverload = + cxxConstructorDecl( + hasParameter(0, ForwardingRefParm), + unless(hasAnyParameter( + // No warning: enable_if as constructor parameter. + parmVarDecl(hasType(isEnableIf())))), + unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl( + // No warning: enable_if as type parameter. + hasDefaultArgument(isEnableIf()))))))) + .bind("ctor"); + Finder->addMatcher(findOverload, this); +} + +void ForwardingReferenceOverloadCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *ParmVar = Result.Nodes.getNodeAs("parm-var"); + const auto *TypeParmDecl = + Result.Nodes.getNodeAs("type-parm-decl"); + + // Get the FunctionDecl and FunctionTemplateDecl containing the function + // parameter. + const auto *FuncForParam = dyn_cast(ParmVar->getDeclContext()); + if (!FuncForParam) + return; + const FunctionTemplateDecl *FuncTemplate = + FuncForParam->getDescribedFunctionTemplate(); + if (!FuncTemplate) + return; + + // Check that the template type parameter belongs to the same function + // template as the function parameter of that type. (This implies that type + // deduction will happen on the type.) + const TemplateParameterList *Params = FuncTemplate->getTemplateParameters(); + if (std::find(Params->begin(), Params->end(), TypeParmDecl) == Params->end()) + return; + + // Every parameter after the first must have a default value. + const auto *Ctor = Result.Nodes.getNodeAs("ctor"); + for (auto Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end(); ++Iter) { + if (!(*Iter)->hasDefaultArg()) { + return; + } + } + bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false, + DisabledMove = false; + for (const auto *OtherCtor : Ctor->getParent()->ctors()) { + if (OtherCtor->isCopyConstructor()) { + if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private) { + DisabledCopy = true; + } else { + EnabledCopy = true; + } + } else if (OtherCtor->isMoveConstructor()) { + if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private) { + DisabledMove = true; + } else { + EnabledMove = true; + } + } + } + bool NoCopy = DisabledCopy && !EnabledCopy, + NoMove = DisabledMove && !EnabledMove; + if (NoCopy && NoMove) { + return; + } + diag(Ctor->getLocation(), "constructor accepting a forwarding reference can " + "hide the %select{copy|move|copy and the " + "move}0 constructor%s1") + << 2 - NoCopy - 2 * NoMove << NoCopy + NoMove; + for (const auto *OtherCtor : Ctor->getParent()->ctors()) { + if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() && + OtherCtor->getAccess() != AS_private) { + diag(OtherCtor->getLocation(), + "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note) + << OtherCtor->isMoveConstructor(); + } + } +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -17,6 +17,7 @@ #include "DefinitionsInHeadersCheck.h" #include "FoldInitTypeCheck.h" #include "ForwardDeclarationNamespaceCheck.h" +#include "ForwardingReferenceOverloadCheck.h" #include "InaccurateEraseCheck.h" #include "IncorrectRoundings.h" #include "InefficientAlgorithmCheck.h" @@ -65,6 +66,8 @@ CheckFactories.registerCheck("misc-argument-comment"); CheckFactories.registerCheck( "misc-assert-side-effect"); + CheckFactories.registerCheck( + "misc-forwarding-reference-overload"); CheckFactories.registerCheck("misc-misplaced-const"); CheckFactories.registerCheck( "misc-unconventional-assign-operator"); Index: clang-tidy/misc/NoexceptMoveConstructorCheck.cpp =================================================================== --- clang-tidy/misc/NoexceptMoveConstructorCheck.cpp +++ clang-tidy/misc/NoexceptMoveConstructorCheck.cpp @@ -43,10 +43,6 @@ } const auto *ProtoType = Decl->getType()->getAs(); - - if (isUnresolvedExceptionSpec(ProtoType->getExceptionSpecType())) - return; - switch (ProtoType->getNoexceptSpec(*Result.Context)) { case FunctionProtoType::NR_NoNoexcept: diag(Decl->getLocation(), "move %0s should be marked noexcept") Index: clang-tidy/misc/UseAfterMoveCheck.cpp =================================================================== --- clang-tidy/misc/UseAfterMoveCheck.cpp +++ clang-tidy/misc/UseAfterMoveCheck.cpp @@ -384,13 +384,6 @@ // the direct ancestor of the std::move() that isn't one of the node // types ignored by ignoringParenImpCasts(). stmt(forEach(expr(ignoringParenImpCasts(CallMoveMatcher))), - // Don't allow an InitListExpr to be the moving call. An InitListExpr - // has both a syntactic and a semantic form, and the parent-child - // relationships are different between the two. This could cause an - // InitListExpr to be analyzed as the moving call in addition to the - // Expr that we actually want, resulting in two diagnostics with - // different code locations for the same move. - unless(initListExpr()), unless(expr(ignoringParenImpCasts(equalsBoundNode("call-move"))))) .bind("moving-call"), this); @@ -405,7 +398,7 @@ const auto *MovingCall = Result.Nodes.getNodeAs("moving-call"); const auto *Arg = Result.Nodes.getNodeAs("arg"); - if (!MovingCall || !MovingCall->getExprLoc().isValid()) + if (!MovingCall) MovingCall = CallMove; Stmt *FunctionBody = nullptr; Index: clang-tidy/modernize/UseAutoCheck.h =================================================================== --- clang-tidy/modernize/UseAutoCheck.h +++ clang-tidy/modernize/UseAutoCheck.h @@ -26,7 +26,7 @@ private: void replaceIterators(const DeclStmt *D, ASTContext *Context); void replaceExpr(const DeclStmt *D, ASTContext *Context, - llvm::function_ref GetType, + std::function GetType, StringRef Message); const bool RemoveStars; Index: clang-tidy/modernize/UseAutoCheck.cpp =================================================================== --- clang-tidy/modernize/UseAutoCheck.cpp +++ clang-tidy/modernize/UseAutoCheck.cpp @@ -352,9 +352,9 @@ << FixItHint::CreateReplacement(Range, "auto"); } -void UseAutoCheck::replaceExpr( - const DeclStmt *D, ASTContext *Context, - llvm::function_ref GetType, StringRef Message) { +void UseAutoCheck::replaceExpr(const DeclStmt *D, ASTContext *Context, + std::function GetType, + StringRef Message) { const auto *FirstDecl = dyn_cast(*D->decl_begin()); // Ensure that there is at least one VarDecl within the DeclStmt. if (!FirstDecl) Index: clang-tidy/modernize/UseNullptrCheck.cpp =================================================================== --- clang-tidy/modernize/UseNullptrCheck.cpp +++ clang-tidy/modernize/UseNullptrCheck.cpp @@ -39,7 +39,6 @@ StatementMatcher makeCastSequenceMatcher() { StatementMatcher ImplicitCastToNull = implicitCastExpr( anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)), - unless(hasImplicitDestinationType(qualType(substTemplateTypeParmType()))), unless(hasSourceExpression(hasType(sugaredNullptrType())))); return castExpr(anyOf(ImplicitCastToNull, @@ -191,10 +190,8 @@ bool VisitStmt(Stmt *S) { auto *C = dyn_cast(S); // Catch the castExpr inside cxxDefaultArgExpr. - if (auto *E = dyn_cast(S)) { + if (auto *E = dyn_cast(S)) C = dyn_cast(E->getExpr()); - FirstSubExpr = nullptr; - } if (!C) { FirstSubExpr = nullptr; return true; Index: clang-tidy/modernize/UseOverrideCheck.cpp =================================================================== --- clang-tidy/modernize/UseOverrideCheck.cpp +++ clang-tidy/modernize/UseOverrideCheck.cpp @@ -147,13 +147,14 @@ // end of the declaration of the function, but prefer to put it on the // same line as the declaration if the beginning brace for the start of // the body falls on the next line. + Token LastNonCommentToken; + for (Token T : Tokens) { + if (!T.is(tok::comment)) { + LastNonCommentToken = T; + } + } + InsertLoc = LastNonCommentToken.getEndLoc(); ReplacementText = " override"; - auto LastTokenIter = std::prev(Tokens.end()); - // When try statement is used instead of compound statement as - // method body - insert override keyword before it. - if (LastTokenIter->is(tok::kw_try)) - LastTokenIter = std::prev(LastTokenIter); - InsertLoc = LastTokenIter->getEndLoc(); } if (!InsertLoc.isValid()) { Index: clang-tidy/readability/ContainerSizeEmptyCheck.cpp =================================================================== --- clang-tidy/readability/ContainerSizeEmptyCheck.cpp +++ clang-tidy/readability/ContainerSizeEmptyCheck.cpp @@ -56,7 +56,8 @@ Finder->addMatcher( cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer), hasType(pointsTo(ValidContainer)), - hasType(references(ValidContainer))))), + hasType(references(ValidContainer)))) + .bind("STLObject")), callee(cxxMethodDecl(hasName("size"))), WrongUse, unless(hasAncestor(cxxMethodDecl( ofClass(equalsBoundNode("container")))))) @@ -68,7 +69,7 @@ const auto *MemberCall = Result.Nodes.getNodeAs("SizeCallExpr"); const auto *BinaryOp = Result.Nodes.getNodeAs("SizeBinaryOp"); - const auto *E = MemberCall->getImplicitObjectArgument(); + const auto *E = Result.Nodes.getNodeAs("STLObject"); FixItHint Hint; std::string ReplacementText = Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()), Index: clang-tidy/readability/DeleteNullPointerCheck.cpp =================================================================== --- clang-tidy/readability/DeleteNullPointerCheck.cpp +++ clang-tidy/readability/DeleteNullPointerCheck.cpp @@ -67,10 +67,16 @@ *Result.SourceManager, Result.Context->getLangOpts()))); if (Compound) { - Diag << FixItHint::CreateRemoval( - CharSourceRange::getTokenRange(Compound->getLBracLoc())); - Diag << FixItHint::CreateRemoval( - CharSourceRange::getTokenRange(Compound->getRBracLoc())); + Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( + Compound->getLBracLoc(), + Lexer::getLocForEndOfToken(Compound->getLBracLoc(), 0, + *Result.SourceManager, + Result.Context->getLangOpts()))); + Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( + Compound->getRBracLoc(), + Lexer::getLocForEndOfToken(Compound->getRBracLoc(), 0, + *Result.SourceManager, + Result.Context->getLangOpts()))); } } Index: clang-tidy/readability/FunctionSizeCheck.h =================================================================== --- clang-tidy/readability/FunctionSizeCheck.h +++ clang-tidy/readability/FunctionSizeCheck.h @@ -27,8 +27,6 @@ /// macro-heavy code. The default is `800`. /// * `BranchThreshold` - flag functions exceeding this number of control /// statements. The default is `-1` (ignore the number of branches). -/// * `ParameterThreshold` - flag functions having a high number of parameters. -/// The default is `6`. class FunctionSizeCheck : public ClangTidyCheck { public: FunctionSizeCheck(StringRef Name, ClangTidyContext *Context); @@ -41,7 +39,6 @@ const unsigned LineThreshold; const unsigned StatementThreshold; const unsigned BranchThreshold; - const unsigned ParameterThreshold; }; } // namespace readability Index: clang-tidy/readability/FunctionSizeCheck.cpp =================================================================== --- clang-tidy/readability/FunctionSizeCheck.cpp +++ clang-tidy/readability/FunctionSizeCheck.cpp @@ -72,14 +72,12 @@ : ClangTidyCheck(Name, Context), LineThreshold(Options.get("LineThreshold", -1U)), StatementThreshold(Options.get("StatementThreshold", 800U)), - BranchThreshold(Options.get("BranchThreshold", -1U)), - ParameterThreshold(Options.get("ParameterThreshold", -1U)) {} + BranchThreshold(Options.get("BranchThreshold", -1U)) {} void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "LineThreshold", LineThreshold); Options.store(Opts, "StatementThreshold", StatementThreshold); Options.store(Opts, "BranchThreshold", BranchThreshold); - Options.store(Opts, "ParameterThreshold", ParameterThreshold); } void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) { @@ -105,11 +103,8 @@ } } - unsigned ActualNumberParameters = Func->getNumParams(); - if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold || - FI.Branches > BranchThreshold || - ActualNumberParameters > ParameterThreshold) { + FI.Branches > BranchThreshold) { diag(Func->getLocation(), "function %0 exceeds recommended size/complexity thresholds") << Func; @@ -132,12 +127,6 @@ diag(Func->getLocation(), "%0 branches (threshold %1)", DiagnosticIDs::Note) << FI.Branches << BranchThreshold; } - - if (ActualNumberParameters > ParameterThreshold) { - diag(Func->getLocation(), "%0 parameters (threshold %1)", - DiagnosticIDs::Note) - << ActualNumberParameters << ParameterThreshold; - } } } // namespace readability Index: clang-tidy/readability/IdentifierNamingCheck.h =================================================================== --- clang-tidy/readability/IdentifierNamingCheck.h +++ clang-tidy/readability/IdentifierNamingCheck.h @@ -53,15 +53,19 @@ }; struct NamingStyle { - NamingStyle() = default; + NamingStyle() : Case(CT_AnyCase) {} - NamingStyle(llvm::Optional Case, const std::string &Prefix, + NamingStyle(CaseType Case, const std::string &Prefix, const std::string &Suffix) : Case(Case), Prefix(Prefix), Suffix(Suffix) {} - llvm::Optional Case; + CaseType Case; std::string Prefix; std::string Suffix; + + bool isSet() const { + return !(Case == CT_AnyCase && Prefix.empty() && Suffix.empty()); + } }; /// \brief Holds an identifier name check failure, tracking the kind of the @@ -97,7 +101,7 @@ void expandMacro(const Token &MacroNameTok, const MacroInfo *MI); private: - std::vector> NamingStyles; + std::vector NamingStyles; bool IgnoreFailedSplit; NamingCheckFailureMap NamingCheckFailures; }; Index: clang-tidy/readability/IdentifierNamingCheck.cpp =================================================================== --- clang-tidy/readability/IdentifierNamingCheck.cpp +++ clang-tidy/readability/IdentifierNamingCheck.cpp @@ -49,7 +49,7 @@ return Val.first.getRawEncoding() + SecondHash(Val.second); } - static bool isEqual(const NamingCheckId &LHS, const NamingCheckId &RHS) { + static bool isEqual(NamingCheckId LHS, NamingCheckId RHS) { if (RHS == getEmptyKey()) return LHS == getEmptyKey(); if (RHS == getTombstoneKey()) @@ -158,28 +158,21 @@ ClangTidyContext *Context) : ClangTidyCheck(Name, Context) { auto const fromString = [](StringRef Str) { - return llvm::StringSwitch>(Str) - .Case("aNy_CasE", CT_AnyCase) + return llvm::StringSwitch(Str) .Case("lower_case", CT_LowerCase) .Case("UPPER_CASE", CT_UpperCase) .Case("camelBack", CT_CamelBack) .Case("CamelCase", CT_CamelCase) .Case("Camel_Snake_Case", CT_CamelSnakeCase) .Case("camel_Snake_Back", CT_CamelSnakeBack) - .Default(llvm::None); + .Default(CT_AnyCase); }; for (auto const &Name : StyleNames) { - auto const caseOptional = - fromString(Options.get((Name + "Case").str(), "")); - auto prefix = Options.get((Name + "Prefix").str(), ""); - auto postfix = Options.get((Name + "Suffix").str(), ""); - - if (caseOptional || !prefix.empty() || !postfix.empty()) { - NamingStyles.push_back(NamingStyle(caseOptional, prefix, postfix)); - } else { - NamingStyles.push_back(llvm::None); - } + NamingStyles.push_back( + NamingStyle(fromString(Options.get((Name + "Case").str(), "")), + Options.get((Name + "Prefix").str(), ""), + Options.get((Name + "Suffix").str(), ""))); } IgnoreFailedSplit = Options.get("IgnoreFailedSplit", 0); @@ -208,16 +201,12 @@ }; for (size_t i = 0; i < SK_Count; ++i) { - if (NamingStyles[i]) { - if (NamingStyles[i]->Case) { - Options.store(Opts, (StyleNames[i] + "Case").str(), - toString(*NamingStyles[i]->Case)); - } - Options.store(Opts, (StyleNames[i] + "Prefix").str(), - NamingStyles[i]->Prefix); - Options.store(Opts, (StyleNames[i] + "Suffix").str(), - NamingStyles[i]->Suffix); - } + Options.store(Opts, (StyleNames[i] + "Case").str(), + toString(NamingStyles[i].Case)); + Options.store(Opts, (StyleNames[i] + "Prefix").str(), + NamingStyles[i].Prefix); + Options.store(Opts, (StyleNames[i] + "Suffix").str(), + NamingStyles[i].Suffix); } Options.store(Opts, "IgnoreFailedSplit", IgnoreFailedSplit); @@ -262,7 +251,7 @@ else Matches = false; - if (Style.Case && !Matchers[static_cast(*Style.Case)].match(Name)) + if (!Matchers[static_cast(Style.Case)].match(Name)) Matches = false; return Matches; @@ -364,44 +353,39 @@ return Fixup; } -static std::string -fixupWithStyle(StringRef Name, - const IdentifierNamingCheck::NamingStyle &Style) { - return Style.Prefix + - fixupWithCase(Name, Style.Case.getValueOr( - IdentifierNamingCheck::CaseType::CT_AnyCase)) + - Style.Suffix; +static std::string fixupWithStyle(StringRef Name, + IdentifierNamingCheck::NamingStyle Style) { + return Style.Prefix + fixupWithCase(Name, Style.Case) + Style.Suffix; } static StyleKind findStyleKind( const NamedDecl *D, - const std::vector> - &NamingStyles) { - if (isa(D) && NamingStyles[SK_Typedef]) + const std::vector &NamingStyles) { + if (isa(D) && NamingStyles[SK_Typedef].isSet()) return SK_Typedef; - if (isa(D) && NamingStyles[SK_TypeAlias]) + if (isa(D) && NamingStyles[SK_TypeAlias].isSet()) return SK_TypeAlias; if (const auto *Decl = dyn_cast(D)) { if (Decl->isAnonymousNamespace()) return SK_Invalid; - if (Decl->isInline() && NamingStyles[SK_InlineNamespace]) + if (Decl->isInline() && NamingStyles[SK_InlineNamespace].isSet()) return SK_InlineNamespace; - if (NamingStyles[SK_Namespace]) + if (NamingStyles[SK_Namespace].isSet()) return SK_Namespace; } - if (isa(D) && NamingStyles[SK_Enum]) + if (isa(D) && NamingStyles[SK_Enum].isSet()) return SK_Enum; if (isa(D)) { - if (NamingStyles[SK_EnumConstant]) + if (NamingStyles[SK_EnumConstant].isSet()) return SK_EnumConstant; - if (NamingStyles[SK_Constant]) + if (NamingStyles[SK_Constant].isSet()) return SK_Constant; return SK_Invalid; @@ -415,25 +399,25 @@ return SK_Invalid; if (Decl->hasDefinition() && Decl->isAbstract() && - NamingStyles[SK_AbstractClass]) + NamingStyles[SK_AbstractClass].isSet()) return SK_AbstractClass; - if (Decl->isStruct() && NamingStyles[SK_Struct]) + if (Decl->isStruct() && NamingStyles[SK_Struct].isSet()) return SK_Struct; - if (Decl->isStruct() && NamingStyles[SK_Class]) + if (Decl->isStruct() && NamingStyles[SK_Class].isSet()) return SK_Class; - if (Decl->isClass() && NamingStyles[SK_Class]) + if (Decl->isClass() && NamingStyles[SK_Class].isSet()) return SK_Class; - if (Decl->isClass() && NamingStyles[SK_Struct]) + if (Decl->isClass() && NamingStyles[SK_Struct].isSet()) return SK_Struct; - if (Decl->isUnion() && NamingStyles[SK_Union]) + if (Decl->isUnion() && NamingStyles[SK_Union].isSet()) return SK_Union; - if (Decl->isEnum() && NamingStyles[SK_Enum]) + if (Decl->isEnum() && NamingStyles[SK_Enum].isSet()) return SK_Enum; return SK_Invalid; @@ -443,23 +427,25 @@ QualType Type = Decl->getType(); if (!Type.isNull() && Type.isLocalConstQualified() && - NamingStyles[SK_ConstantMember]) + NamingStyles[SK_ConstantMember].isSet()) return SK_ConstantMember; if (!Type.isNull() && Type.isLocalConstQualified() && - NamingStyles[SK_Constant]) + NamingStyles[SK_Constant].isSet()) return SK_Constant; - if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMember]) + if (Decl->getAccess() == AS_private && + NamingStyles[SK_PrivateMember].isSet()) return SK_PrivateMember; - if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMember]) + if (Decl->getAccess() == AS_protected && + NamingStyles[SK_ProtectedMember].isSet()) return SK_ProtectedMember; - if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember]) + if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember].isSet()) return SK_PublicMember; - if (NamingStyles[SK_Member]) + if (NamingStyles[SK_Member].isSet()) return SK_Member; return SK_Invalid; @@ -468,21 +454,21 @@ if (const auto *Decl = dyn_cast(D)) { QualType Type = Decl->getType(); - if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable]) + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable].isSet()) return SK_ConstexprVariable; if (!Type.isNull() && Type.isLocalConstQualified() && - NamingStyles[SK_ConstantParameter]) + NamingStyles[SK_ConstantParameter].isSet()) return SK_ConstantParameter; if (!Type.isNull() && Type.isLocalConstQualified() && - NamingStyles[SK_Constant]) + NamingStyles[SK_Constant].isSet()) return SK_Constant; - if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack]) + if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack].isSet()) return SK_ParameterPack; - if (NamingStyles[SK_Parameter]) + if (NamingStyles[SK_Parameter].isSet()) return SK_Parameter; return SK_Invalid; @@ -491,49 +477,51 @@ if (const auto *Decl = dyn_cast(D)) { QualType Type = Decl->getType(); - if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable]) + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable].isSet()) return SK_ConstexprVariable; if (!Type.isNull() && Type.isLocalConstQualified() && - Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant]) + Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant].isSet()) return SK_ClassConstant; if (!Type.isNull() && Type.isLocalConstQualified() && - Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant]) + Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant].isSet()) return SK_GlobalConstant; if (!Type.isNull() && Type.isLocalConstQualified() && - Decl->isStaticLocal() && NamingStyles[SK_StaticConstant]) + Decl->isStaticLocal() && NamingStyles[SK_StaticConstant].isSet()) return SK_StaticConstant; if (!Type.isNull() && Type.isLocalConstQualified() && - Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant]) + Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant].isSet()) return SK_LocalConstant; if (!Type.isNull() && Type.isLocalConstQualified() && - Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalConstant]) + Decl->isFunctionOrMethodVarDecl() && + NamingStyles[SK_LocalConstant].isSet()) return SK_LocalConstant; if (!Type.isNull() && Type.isLocalConstQualified() && - NamingStyles[SK_Constant]) + NamingStyles[SK_Constant].isSet()) return SK_Constant; - if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember]) + if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember].isSet()) return SK_ClassMember; - if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable]) + if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable].isSet()) return SK_GlobalVariable; - if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable]) + if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable].isSet()) return SK_StaticVariable; - if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable]) + if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable].isSet()) return SK_LocalVariable; - if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalVariable]) + if (Decl->isFunctionOrMethodVarDecl() && + NamingStyles[SK_LocalVariable].isSet()) return SK_LocalVariable; - if (NamingStyles[SK_Variable]) + if (NamingStyles[SK_Variable].isSet()) return SK_Variable; return SK_Invalid; @@ -546,31 +534,33 @@ Decl->size_overridden_methods() > 0) return SK_Invalid; - if (Decl->isConstexpr() && NamingStyles[SK_ConstexprMethod]) + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprMethod].isSet()) return SK_ConstexprMethod; - if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction]) + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction].isSet()) return SK_ConstexprFunction; - if (Decl->isStatic() && NamingStyles[SK_ClassMethod]) + if (Decl->isStatic() && NamingStyles[SK_ClassMethod].isSet()) return SK_ClassMethod; - if (Decl->isVirtual() && NamingStyles[SK_VirtualMethod]) + if (Decl->isVirtual() && NamingStyles[SK_VirtualMethod].isSet()) return SK_VirtualMethod; - if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMethod]) + if (Decl->getAccess() == AS_private && + NamingStyles[SK_PrivateMethod].isSet()) return SK_PrivateMethod; - if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMethod]) + if (Decl->getAccess() == AS_protected && + NamingStyles[SK_ProtectedMethod].isSet()) return SK_ProtectedMethod; - if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMethod]) + if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMethod].isSet()) return SK_PublicMethod; - if (NamingStyles[SK_Method]) + if (NamingStyles[SK_Method].isSet()) return SK_Method; - if (NamingStyles[SK_Function]) + if (NamingStyles[SK_Function].isSet()) return SK_Function; return SK_Invalid; @@ -580,41 +570,41 @@ if (Decl->isMain()) return SK_Invalid; - if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction]) + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction].isSet()) return SK_ConstexprFunction; - if (Decl->isGlobal() && NamingStyles[SK_GlobalFunction]) + if (Decl->isGlobal() && NamingStyles[SK_GlobalFunction].isSet()) return SK_GlobalFunction; - if (NamingStyles[SK_Function]) + if (NamingStyles[SK_Function].isSet()) return SK_Function; } if (isa(D)) { - if (NamingStyles[SK_TypeTemplateParameter]) + if (NamingStyles[SK_TypeTemplateParameter].isSet()) return SK_TypeTemplateParameter; - if (NamingStyles[SK_TemplateParameter]) + if (NamingStyles[SK_TemplateParameter].isSet()) return SK_TemplateParameter; return SK_Invalid; } if (isa(D)) { - if (NamingStyles[SK_ValueTemplateParameter]) + if (NamingStyles[SK_ValueTemplateParameter].isSet()) return SK_ValueTemplateParameter; - if (NamingStyles[SK_TemplateParameter]) + if (NamingStyles[SK_TemplateParameter].isSet()) return SK_TemplateParameter; return SK_Invalid; } if (isa(D)) { - if (NamingStyles[SK_TemplateTemplateParameter]) + if (NamingStyles[SK_TemplateTemplateParameter].isSet()) return SK_TemplateTemplateParameter; - if (NamingStyles[SK_TemplateParameter]) + if (NamingStyles[SK_TemplateParameter].isSet()) return SK_TemplateParameter; return SK_Invalid; @@ -817,10 +807,7 @@ if (SK == SK_Invalid) return; - if (!NamingStyles[SK]) - return; - - const NamingStyle &Style = *NamingStyles[SK]; + NamingStyle Style = NamingStyles[SK]; StringRef Name = Decl->getName(); if (matchesStyle(Name, Style)) return; @@ -853,11 +840,8 @@ void IdentifierNamingCheck::checkMacro(SourceManager &SourceMgr, const Token &MacroNameTok, const MacroInfo *MI) { - if (!NamingStyles[SK_MacroDefinition]) - return; - StringRef Name = MacroNameTok.getIdentifierInfo()->getName(); - const NamingStyle &Style = *NamingStyles[SK_MacroDefinition]; + NamingStyle Style = NamingStyles[SK_MacroDefinition]; if (matchesStyle(Name, Style)) return; Index: clang-tidy/readability/MisleadingIndentationCheck.h =================================================================== --- clang-tidy/readability/MisleadingIndentationCheck.h +++ clang-tidy/readability/MisleadingIndentationCheck.h @@ -30,8 +30,7 @@ void check(const ast_matchers::MatchFinder::MatchResult &Result) override; private: - void danglingElseCheck(const SourceManager &SM, ASTContext *Context, - const IfStmt *If); + void danglingElseCheck(const SourceManager &SM, const IfStmt *If); void missingBracesCheck(const SourceManager &SM, const CompoundStmt *CStmt); }; Index: clang-tidy/readability/MisleadingIndentationCheck.cpp =================================================================== --- clang-tidy/readability/MisleadingIndentationCheck.cpp +++ clang-tidy/readability/MisleadingIndentationCheck.cpp @@ -17,22 +17,7 @@ namespace tidy { namespace readability { -static const IfStmt *getPrecedingIf(const SourceManager &SM, - ASTContext *Context, const IfStmt *If) { - auto parents = Context->getParents(*If); - if (parents.size() != 1) - return nullptr; - if (const auto *PrecedingIf = parents[0].get()) { - SourceLocation PreviousElseLoc = PrecedingIf->getElseLoc(); - if (SM.getExpansionLineNumber(PreviousElseLoc) == - SM.getExpansionLineNumber(If->getIfLoc())) - return PrecedingIf; - } - return nullptr; -} - void MisleadingIndentationCheck::danglingElseCheck(const SourceManager &SM, - ASTContext *Context, const IfStmt *If) { SourceLocation IfLoc = If->getIfLoc(); SourceLocation ElseLoc = If->getElseLoc(); @@ -44,11 +29,6 @@ SM.getExpansionLineNumber(ElseLoc)) return; - // Find location of first 'if' in a 'if else if' chain. - for (auto PrecedingIf = getPrecedingIf(SM, Context, If); PrecedingIf; - PrecedingIf = getPrecedingIf(SM, Context, PrecedingIf)) - IfLoc = PrecedingIf->getIfLoc(); - if (SM.getExpansionColumnNumber(IfLoc) != SM.getExpansionColumnNumber(ElseLoc)) diag(ElseLoc, "different indentation for 'if' and corresponding 'else'"); @@ -112,7 +92,7 @@ void MisleadingIndentationCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *If = Result.Nodes.getNodeAs("if")) - danglingElseCheck(*Result.SourceManager, Result.Context, If); + danglingElseCheck(*Result.SourceManager, If); if (const auto *CStmt = Result.Nodes.getNodeAs("compound")) missingBracesCheck(*Result.SourceManager, CStmt); Index: clang-tidy/readability/RedundantDeclarationCheck.cpp =================================================================== --- clang-tidy/readability/RedundantDeclarationCheck.cpp +++ clang-tidy/readability/RedundantDeclarationCheck.cpp @@ -19,10 +19,7 @@ namespace readability { void RedundantDeclarationCheck::registerMatchers(MatchFinder *Finder) { - auto UnlessDefinition = unless(isDefinition()); - Finder->addMatcher(namedDecl(anyOf(varDecl(UnlessDefinition), - functionDecl(UnlessDefinition))) - .bind("Decl"), + Finder->addMatcher(namedDecl(anyOf(varDecl(), functionDecl())).bind("Decl"), this); } @@ -44,6 +41,9 @@ bool MultiVar = false; if (const auto *VD = dyn_cast(D)) { + if (VD->getPreviousDecl()->getStorageClass() == SC_Extern && + VD->getStorageClass() != SC_Extern) + return; // Is this a multivariable declaration? for (const auto Other : VD->getDeclContext()->decls()) { if (Other != D && Other->getLocStart() == VD->getLocStart()) { @@ -51,6 +51,10 @@ break; } } + } else { + const auto *FD = cast(D); + if (FD->isThisDeclarationADefinition()) + return; } SourceLocation EndLoc = Lexer::getLocForEndOfToken( Index: clang-tidy/safety/CMakeLists.txt =================================================================== --- /dev/null +++ clang-tidy/safety/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangTidySafetyModule + NoAssemblerCheck.cpp + SafetyTidyModule.cpp + + LINK_LIBS + clangAST + clangASTMatchers + clangBasic + clangLex + clangTidy + clangTidyUtils + ) Index: clang-tidy/safety/NoAssemblerCheck.h =================================================================== --- /dev/null +++ clang-tidy/safety/NoAssemblerCheck.h @@ -0,0 +1,35 @@ +//===--- NoAssemblerCheck.h - clang-tidy-------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SAFETY_NO_ASSEMBLER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SAFETY_NO_ASSEMBLER_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace safety { + +/// Find assembler statements. No fix is offered. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/safety-no-assembler.html +class NoAssemblerCheck : public ClangTidyCheck { +public: + NoAssemblerCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace safety +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SAFETY_NO_ASSEMBLER_H Index: clang-tidy/safety/NoAssemblerCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/safety/NoAssemblerCheck.cpp @@ -0,0 +1,51 @@ +//===--- NoAssemblerCheck.cpp - clang-tidy---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NoAssemblerCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace ast_matchers { +AST_MATCHER(VarDecl, isAsm) { return Node.hasAttr(); } +const internal::VariadicDynCastAllOfMatcher + fileScopeAsmDecl; +} +} + +namespace clang { +namespace tidy { +namespace safety { + +void NoAssemblerCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(asmStmt().bind("asm-stmt"), this); + Finder->addMatcher(fileScopeAsmDecl().bind("asm-file-scope"), this); + Finder->addMatcher(varDecl(isAsm()).bind("asm-var"), this); +} + +void NoAssemblerCheck::check(const MatchFinder::MatchResult &Result) { + SourceLocation ASMLocation; + if (const auto *ASM = Result.Nodes.getNodeAs("asm-stmt")) + ASMLocation = ASM->getAsmLoc(); + else if (const auto *ASM = + Result.Nodes.getNodeAs("asm-file-scope")) + ASMLocation = ASM->getAsmLoc(); + else if (const auto *ASM = Result.Nodes.getNodeAs("asm-var")) + ASMLocation = ASM->getLocation(); + else + llvm_unreachable("Unhandled case in matcher."); + + diag(ASMLocation, "do not use inline assembler in safety-critical code"); +} + +} // namespace safety +} // namespace tidy +} // namespace clang Index: clang-tidy/safety/SafetyTidyModule.cpp =================================================================== --- /dev/null +++ clang-tidy/safety/SafetyTidyModule.cpp @@ -0,0 +1,38 @@ +//===------- SafetyTidyModule.cpp - clang-tidy ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../ClangTidy.h" +#include "../ClangTidyModule.h" +#include "../ClangTidyModuleRegistry.h" +#include "NoAssemblerCheck.h" + +namespace clang { +namespace tidy { +namespace safety { + +class SafetyModule : public ClangTidyModule { +public: + void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "safety-no-assembler"); + } +}; + +// Register the SafetyModule using this statically initialized variable. +static ClangTidyModuleRegistry::Add + X("safety-module", "Adds safety-critical checks."); + +} // namespace safety + +// This anchor is used to force the linker to link in the generated object file +// and thus register the SafetyModule. +volatile int SafetyModuleAnchorSource = 0; + +} // namespace tidy +} // namespace clang Index: clang-tidy/tool/CMakeLists.txt =================================================================== --- clang-tidy/tool/CMakeLists.txt +++ clang-tidy/tool/CMakeLists.txt @@ -17,13 +17,13 @@ clangTidyCERTModule clangTidyCppCoreGuidelinesModule clangTidyGoogleModule - clangTidyHICPPModule clangTidyLLVMModule clangTidyMiscModule clangTidyModernizeModule clangTidyMPIModule clangTidyPerformanceModule clangTidyReadabilityModule + clangTidySafetyModule clangTooling ) Index: clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tidy/tool/ClangTidyMain.cpp +++ clang-tidy/tool/ClangTidyMain.cpp @@ -60,7 +60,7 @@ prefix add checks with matching names to the set, globs with the '-' prefix remove checks with matching names from the set of enabled -checks. This option's value is appended to the +checks. This option's value is appended to the value of the 'Checks' option in .clang-tidy file, if any. )"), @@ -120,20 +120,12 @@ )"), cl::init(false), cl::cat(ClangTidyCategory)); -static cl::opt FormatStyle("format-style", cl::desc(R"( -Style for formatting code around applied fixes: - - 'none' (default) turns off formatting - - 'file' (literally 'file', not a placeholder) - uses .clang-format file in the closest parent - directory - - '{ }' specifies options inline, e.g. - -format-style='{BasedOnStyle: llvm, IndentWidth: 8}' - - 'llvm', 'google', 'webkit', 'mozilla' -See clang-format documentation for the up-to-date -information about formatting styles and options. +static cl::opt FormatStyle("style", cl::desc(R"( +Fallback style for reformatting after inserting fixes +if there is no clang-format config file found. )"), - cl::init("none"), - cl::cat(ClangTidyCategory)); + cl::init("llvm"), + cl::cat(ClangTidyCategory)); static cl::opt ListChecks("list-checks", cl::desc(R"( List all enabled checks and exit. Use with @@ -197,7 +189,7 @@ cl::cat(ClangTidyCategory)); static cl::opt Quiet("quiet", cl::desc(R"( -Run clang-tidy in quiet mode. This suppresses +Run clang-tidy in quiet mode. This suppresses printing statistics about ignored warnings and warnings treated as errors if the respective options are specified. @@ -496,10 +488,10 @@ static int LLVM_ATTRIBUTE_UNUSED ReadabilityModuleAnchorDestination = ReadabilityModuleAnchorSource; -// This anchor is used to force the linker to link the HICPPModule. -extern volatile int HICPPModuleAnchorSource; -static int LLVM_ATTRIBUTE_UNUSED HICPPModuleAnchorDestination = - HICPPModuleAnchorSource; +// This anchor is used to force the linker to link the SafetyModule. +extern volatile int SafetyModuleAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED SafetyModuleAnchorDestination = + SafetyModuleAnchorSource; } // namespace tidy } // namespace clang Index: clangd/ASTManager.h =================================================================== --- clangd/ASTManager.h +++ clangd/ASTManager.h @@ -12,8 +12,6 @@ #include "DocumentStore.h" #include "JSONRPCDispatcher.h" -#include "Protocol.h" -#include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include #include @@ -31,30 +29,17 @@ class ASTManager : public DocumentStoreListener { public: - ASTManager(JSONOutput &Output, DocumentStore &Store, bool RunSynchronously); + ASTManager(JSONOutput &Output, DocumentStore &Store); ~ASTManager() override; void onDocumentAdd(StringRef Uri) override; // FIXME: Implement onDocumentRemove // FIXME: Implement codeComplete - /// Get the fixes associated with a certain diagnostic as replacements. - /// - /// This function is thread-safe. It returns a copy to avoid handing out - /// references to unguarded data. - std::vector - getFixIts(const clangd::Diagnostic &D); - - DocumentStore &getStore() const { return Store; } - private: JSONOutput &Output; DocumentStore &Store; - // Set to true if requests should block instead of being processed - // asynchronously. - bool RunSynchronously; - /// Loads a compilation database for URI. May return nullptr if it fails. The /// database is cached for subsequent accesses. clang::tooling::CompilationDatabase * @@ -65,7 +50,6 @@ createASTUnitForFile(StringRef Uri, const DocumentStore &Docs); void runWorker(); - void parseFileAndPublishDiagnostics(StringRef File); /// Clang objects. llvm::StringMap> ASTs; @@ -73,11 +57,6 @@ CompilationDatabases; std::shared_ptr PCHs; - typedef std::map> - DiagnosticToReplacementMap; - DiagnosticToReplacementMap FixIts; - std::mutex FixItLock; - /// Queue of requests. std::deque RequestQueue; /// Setting Done to true will make the worker thread terminate. Index: clangd/ASTManager.cpp =================================================================== --- clangd/ASTManager.cpp +++ clangd/ASTManager.cpp @@ -54,9 +54,8 @@ llvm_unreachable("Unknown diagnostic level!"); } -ASTManager::ASTManager(JSONOutput &Output, DocumentStore &Store, - bool RunSynchronously) - : Output(Output), Store(Store), RunSynchronously(RunSynchronously), +ASTManager::ASTManager(JSONOutput &Output, DocumentStore &Store) + : Output(Output), Store(Store), PCHs(std::make_shared()), ClangWorker([this]() { runWorker(); }) {} @@ -68,8 +67,9 @@ std::unique_lock Lock(RequestLock); // Check if there's another request pending. We keep parsing until // our one-element queue is empty. - ClangRequestCV.wait(Lock, - [this] { return !RequestQueue.empty() || Done; }); + ClangRequestCV.wait(Lock, [this] { + return !RequestQueue.empty() || Done; + }); if (RequestQueue.empty() && Done) return; @@ -78,67 +78,49 @@ RequestQueue.pop_back(); } // unlock. - parseFileAndPublishDiagnostics(File); - } -} - -void ASTManager::parseFileAndPublishDiagnostics(StringRef File) { - auto &Unit = ASTs[File]; // Only one thread can access this at a time. + auto &Unit = ASTs[File]; // Only one thread can access this at a time. - if (!Unit) { - Unit = createASTUnitForFile(File, this->Store); - } else { - // Do a reparse if this wasn't the first parse. - // FIXME: This might have the wrong working directory if it changed in the - // meantime. - Unit->Reparse(PCHs, getRemappedFiles(this->Store)); - } + if (!Unit) { + Unit = createASTUnitForFile(File, this->Store); + } else { + // Do a reparse if this wasn't the first parse. + // FIXME: This might have the wrong working directory if it changed in the + // meantime. + Unit->Reparse(PCHs, getRemappedFiles(this->Store)); + } - if (!Unit) - return; - - // Send the diagnotics to the editor. - // FIXME: If the diagnostic comes from a different file, do we want to - // show them all? Right now we drop everything not coming from the - // main file. - std::string Diagnostics; - DiagnosticToReplacementMap LocalFixIts; // Temporary storage - for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(), - DEnd = Unit->stored_diag_end(); - D != DEnd; ++D) { - if (!D->getLocation().isValid() || - !D->getLocation().getManager().isInMainFile(D->getLocation())) + if (!Unit) continue; - Position P; - P.line = D->getLocation().getSpellingLineNumber() - 1; - P.character = D->getLocation().getSpellingColumnNumber(); - Range R = {P, P}; - Diagnostics += - R"({"range":)" + Range::unparse(R) + - R"(,"severity":)" + std::to_string(getSeverity(D->getLevel())) + - R"(,"message":")" + llvm::yaml::escape(D->getMessage()) + - R"("},)"; - - // We convert to Replacements to become independent of the SourceManager. - clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()}; - auto &FixItsForDiagnostic = LocalFixIts[Diag]; - for (const FixItHint &Fix : D->getFixIts()) { - FixItsForDiagnostic.push_back(clang::tooling::Replacement( - Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert)); + + // Send the diagnotics to the editor. + // FIXME: If the diagnostic comes from a different file, do we want to + // show them all? Right now we drop everything not coming from the + // main file. + // FIXME: Send FixIts to the editor. + std::string Diagnostics; + for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(), + DEnd = Unit->stored_diag_end(); + D != DEnd; ++D) { + if (!D->getLocation().isValid() || + !D->getLocation().getManager().isInMainFile(D->getLocation())) + continue; + Position P; + P.line = D->getLocation().getSpellingLineNumber() - 1; + P.character = D->getLocation().getSpellingColumnNumber(); + Range R = {P, P}; + Diagnostics += + R"({"range":)" + Range::unparse(R) + + R"(,"severity":)" + std::to_string(getSeverity(D->getLevel())) + + R"(,"message":")" + llvm::yaml::escape(D->getMessage()) + + R"("},)"; } - } - // Put FixIts into place. - { - std::lock_guard Guard(FixItLock); - FixIts = std::move(LocalFixIts); + if (!Diagnostics.empty()) + Diagnostics.pop_back(); // Drop trailing comma. + Output.writeMessage( + R"({"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":")" + + File + R"(","diagnostics":[)" + Diagnostics + R"(]}})"); } - - if (!Diagnostics.empty()) - Diagnostics.pop_back(); // Drop trailing comma. - Output.writeMessage( - R"({"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":")" + - File + R"(","diagnostics":[)" + Diagnostics + R"(]}})"); } ASTManager::~ASTManager() { @@ -152,10 +134,6 @@ } void ASTManager::onDocumentAdd(StringRef Uri) { - if (RunSynchronously) { - parseFileAndPublishDiagnostics(Uri); - return; - } std::lock_guard Guard(RequestLock); // Currently we discard all pending requests and just enqueue the latest one. RequestQueue.clear(); @@ -223,12 +201,3 @@ /*IncludeBriefCommentsInCodeCompletion=*/true, /*AllowPCHWithCompilerErrors=*/true)); } - -std::vector -ASTManager::getFixIts(const clangd::Diagnostic &D) { - std::lock_guard Guard(FixItLock); - auto I = FixIts.find(D); - if (I != FixIts.end()) - return I->second; - return {}; -} Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -6,13 +6,10 @@ ProtocolHandlers.cpp ) -install(TARGETS clangd RUNTIME DESTINATION bin) - target_link_libraries(clangd clangBasic clangFormat clangFrontend clangTooling - clangToolingCore LLVMSupport ) Index: clangd/ClangDMain.cpp =================================================================== --- clangd/ClangDMain.cpp +++ clangd/ClangDMain.cpp @@ -11,20 +11,13 @@ #include "DocumentStore.h" #include "JSONRPCDispatcher.h" #include "ProtocolHandlers.h" -#include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Program.h" #include #include using namespace clang::clangd; -static llvm::cl::opt - RunSynchronously("run-synchronously", - llvm::cl::desc("parse on main thread"), - llvm::cl::init(false), llvm::cl::Hidden); - int main(int argc, char *argv[]) { - llvm::cl::ParseCommandLineOptions(argc, argv, "clangd"); llvm::raw_ostream &Outs = llvm::outs(); llvm::raw_ostream &Logs = llvm::errs(); JSONOutput Out(Outs, Logs); @@ -35,14 +28,14 @@ // Set up a document store and intialize all the method handlers for JSONRPC // dispatching. DocumentStore Store; - ASTManager AST(Out, Store, RunSynchronously); + ASTManager AST(Out, Store); Store.addListener(&AST); JSONRPCDispatcher Dispatcher(llvm::make_unique(Out)); Dispatcher.registerHandler("initialize", llvm::make_unique(Out)); auto ShutdownPtr = llvm::make_unique(Out); auto *ShutdownHandler = ShutdownPtr.get(); - Dispatcher.registerHandler("shutdown", std::move(ShutdownPtr)); + Dispatcher.registerHandler("shutdown",std::move(ShutdownPtr)); Dispatcher.registerHandler( "textDocument/didOpen", llvm::make_unique(Out, Store)); @@ -59,18 +52,12 @@ Dispatcher.registerHandler( "textDocument/formatting", llvm::make_unique(Out, Store)); - Dispatcher.registerHandler("textDocument/codeAction", - llvm::make_unique(Out, AST)); while (std::cin.good()) { // A Language Server Protocol message starts with a HTTP header, delimited // by \r\n. std::string Line; std::getline(std::cin, Line); - if (!std::cin.good() && errno == EINTR) { - std::cin.clear(); - continue; - } // Skip empty lines. llvm::StringRef LineRef(Line); Index: clangd/DocumentStore.h =================================================================== --- clangd/DocumentStore.h +++ clangd/DocumentStore.h @@ -49,9 +49,6 @@ Listener->onDocumentRemove(Uri); } /// Retrieve a document from the store. Empty string if it's unknown. - /// - /// This function is thread-safe. It returns a copy to avoid handing out - /// references to unguarded data. std::string getDocument(StringRef Uri) const { // FIXME: This could be a reader lock. std::lock_guard Guard(DocsMutex); @@ -62,9 +59,6 @@ void addListener(DocumentStoreListener *DSL) { Listeners.push_back(DSL); } /// Get name and constents of all documents in this store. - /// - /// This function is thread-safe. It returns a copies to avoid handing out - /// references to unguarded data. std::vector> getAllDocuments() const { std::vector> AllDocs; std::lock_guard Guard(DocsMutex); Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -44,15 +44,6 @@ /// Character offset on a line in a document (zero-based). int character; - friend bool operator==(const Position &LHS, const Position &RHS) { - return std::tie(LHS.line, LHS.character) == - std::tie(RHS.line, RHS.character); - } - friend bool operator<(const Position &LHS, const Position &RHS) { - return std::tie(LHS.line, LHS.character) < - std::tie(RHS.line, RHS.character); - } - static llvm::Optional parse(llvm::yaml::MappingNode *Params); static std::string unparse(const Position &P); }; @@ -64,13 +55,6 @@ /// The range's end position. Position end; - friend bool operator==(const Range &LHS, const Range &RHS) { - return std::tie(LHS.start, LHS.end) == std::tie(RHS.start, RHS.end); - } - friend bool operator<(const Range &LHS, const Range &RHS) { - return std::tie(LHS.start, LHS.end) < std::tie(RHS.start, RHS.end); - } - static llvm::Optional parse(llvm::yaml::MappingNode *Params); static std::string unparse(const Range &P); }; @@ -188,51 +172,6 @@ parse(llvm::yaml::MappingNode *Params); }; -struct Diagnostic { - /// The range at which the message applies. - Range range; - - /// The diagnostic's severity. Can be omitted. If omitted it is up to the - /// client to interpret diagnostics as error, warning, info or hint. - int severity; - - /// The diagnostic's message. - std::string message; - - friend bool operator==(const Diagnostic &LHS, const Diagnostic &RHS) { - return std::tie(LHS.range, LHS.severity, LHS.message) == - std::tie(RHS.range, RHS.severity, RHS.message); - } - friend bool operator<(const Diagnostic &LHS, const Diagnostic &RHS) { - return std::tie(LHS.range, LHS.severity, LHS.message) < - std::tie(RHS.range, RHS.severity, RHS.message); - } - - static llvm::Optional parse(llvm::yaml::MappingNode *Params); -}; - -struct CodeActionContext { - /// An array of diagnostics. - std::vector diagnostics; - - static llvm::Optional - parse(llvm::yaml::MappingNode *Params); -}; - -struct CodeActionParams { - /// The document in which the command was invoked. - TextDocumentIdentifier textDocument; - - /// The range for which the command was invoked. - Range range; - - /// Context carrying additional information. - CodeActionContext context; - - static llvm::Optional - parse(llvm::yaml::MappingNode *Params); -}; - } // namespace clangd } // namespace clang Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -457,116 +457,3 @@ } return Result; } - -llvm::Optional Diagnostic::parse(llvm::yaml::MappingNode *Params) { - Diagnostic Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - - llvm::SmallString<10> Storage; - if (KeyValue == "range") { - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - auto Parsed = Range::parse(Value); - if (!Parsed) - return llvm::None; - Result.range = std::move(*Parsed); - } else if (KeyValue == "severity") { - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - long long Val; - if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) - return llvm::None; - Result.severity = Val; - } else if (KeyValue == "message") { - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - Result.message = Value->getValue(Storage); - } else { - return llvm::None; - } - } - return Result; -} - -llvm::Optional -CodeActionContext::parse(llvm::yaml::MappingNode *Params) { - CodeActionContext Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = NextKeyValue.getValue(); - - llvm::SmallString<10> Storage; - if (KeyValue == "diagnostics") { - auto *Seq = dyn_cast(Value); - if (!Seq) - return llvm::None; - for (auto &Item : *Seq) { - auto *I = dyn_cast(&Item); - if (!I) - return llvm::None; - auto Parsed = Diagnostic::parse(I); - if (!Parsed) - return llvm::None; - Result.diagnostics.push_back(std::move(*Parsed)); - } - } else { - return llvm::None; - } - } - return Result; -} - -llvm::Optional -CodeActionParams::parse(llvm::yaml::MappingNode *Params) { - CodeActionParams Result; - for (auto &NextKeyValue : *Params) { - auto *KeyString = dyn_cast(NextKeyValue.getKey()); - if (!KeyString) - return llvm::None; - - llvm::SmallString<10> KeyStorage; - StringRef KeyValue = KeyString->getValue(KeyStorage); - auto *Value = - dyn_cast_or_null(NextKeyValue.getValue()); - if (!Value) - return llvm::None; - - llvm::SmallString<10> Storage; - if (KeyValue == "textDocument") { - auto Parsed = TextDocumentIdentifier::parse(Value); - if (!Parsed) - return llvm::None; - Result.textDocument = std::move(*Parsed); - } else if (KeyValue == "range") { - auto Parsed = Range::parse(Value); - if (!Parsed) - return llvm::None; - Result.range = std::move(*Parsed); - } else if (KeyValue == "context") { - auto Parsed = CodeActionContext::parse(Value); - if (!Parsed) - return llvm::None; - Result.context = std::move(*Parsed); - } else { - return llvm::None; - } - } - return Result; -} Index: clangd/ProtocolHandlers.h =================================================================== --- clangd/ProtocolHandlers.h +++ clangd/ProtocolHandlers.h @@ -22,7 +22,6 @@ namespace clang { namespace clangd { -class ASTManager; class DocumentStore; struct InitializeHandler : Handler { @@ -35,8 +34,7 @@ "textDocumentSync": 1, "documentFormattingProvider": true, "documentRangeFormattingProvider": true, - "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]}, - "codeActionProvider": true + "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]} }}})"); } }; @@ -104,16 +102,6 @@ DocumentStore &Store; }; -struct CodeActionHandler : Handler { - CodeActionHandler(JSONOutput &Output, ASTManager &AST) - : Handler(Output), AST(AST) {} - - void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override; - -private: - ASTManager &AST; -}; - } // namespace clangd } // namespace clang Index: clangd/ProtocolHandlers.cpp =================================================================== --- clangd/ProtocolHandlers.cpp +++ clangd/ProtocolHandlers.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "ProtocolHandlers.h" -#include "ASTManager.h" #include "DocumentStore.h" #include "clang/Format/Format.h" using namespace clang; @@ -59,9 +58,18 @@ return {Lines, Cols}; } -template -static std::string replacementsToEdits(StringRef Code, const T &Replacements) { - // Turn the replacements into the format specified by the Language Server +static std::string formatCode(StringRef Code, StringRef Filename, + ArrayRef Ranges, StringRef ID) { + // Call clang-format. + // FIXME: Don't ignore style. + format::FormatStyle Style = format::getLLVMStyle(); + // On windows FileManager doesn't like file://. Just strip it, clang-format + // doesn't need it. + Filename.consume_front("file://"); + tooling::Replacements Replacements = + format::reformat(Style, Code, Ranges, Filename); + + // Now turn the replacements into the format specified by the Language Server // Protocol. Fuse them into one big JSON array. std::string Edits; for (auto &R : Replacements) { @@ -75,21 +83,6 @@ if (!Edits.empty()) Edits.pop_back(); - return Edits; -} - -static std::string formatCode(StringRef Code, StringRef Filename, - ArrayRef Ranges, StringRef ID) { - // Call clang-format. - // FIXME: Don't ignore style. - format::FormatStyle Style = format::getLLVMStyle(); - // On windows FileManager doesn't like file://. Just strip it, clang-format - // doesn't need it. - Filename.consume_front("file://"); - tooling::Replacements Replacements = - format::reformat(Style, Code, Ranges, Filename); - - std::string Edits = replacementsToEdits(Code, Replacements); return R"({"jsonrpc":"2.0","id":)" + ID.str() + R"(,"result":[)" + Edits + R"(]})"; } @@ -145,36 +138,3 @@ writeMessage(formatCode(Code, DFP->textDocument.uri, {clang::tooling::Range(0, Code.size())}, ID)); } - -void CodeActionHandler::handleMethod(llvm::yaml::MappingNode *Params, - StringRef ID) { - auto CAP = CodeActionParams::parse(Params); - if (!CAP) { - Output.log("Failed to decode CodeActionParams!\n"); - return; - } - - // We provide a code action for each diagnostic at the requested location - // which has FixIts available. - std::string Code = AST.getStore().getDocument(CAP->textDocument.uri); - std::string Commands; - for (Diagnostic &D : CAP->context.diagnostics) { - std::vector Fixes = AST.getFixIts(D); - std::string Edits = replacementsToEdits(Code, Fixes); - - if (!Edits.empty()) - Commands += - R"({"title":"Apply FixIt ')" + llvm::yaml::escape(D.message) + - R"('", "command": "clangd.applyFix", "arguments": [")" + - llvm::yaml::escape(CAP->textDocument.uri) + - R"(", [)" + Edits + - R"(]]},)"; - } - if (!Commands.empty()) - Commands.pop_back(); - - writeMessage( - R"({"jsonrpc":"2.0","id":)" + ID.str() + - R"(, "result": [)" + Commands + - R"(]})"); -} Index: clangd/clients/clangd-vscode/.gitignore =================================================================== --- clangd/clients/clangd-vscode/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -out -node_modules \ No newline at end of file Index: clangd/clients/clangd-vscode/.vscode/launch.json =================================================================== --- clangd/clients/clangd-vscode/.vscode/launch.json +++ /dev/null @@ -1,28 +0,0 @@ -// A launch configuration that compiles the extension and then opens it inside a new window -{ - "version": "0.1.0", - "configurations": [ - { - "name": "Launch Extension", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], - "stopOnEntry": false, - "sourceMaps": true, - "outFiles": [ "${workspaceRoot}/out/src/**/*.js" ], - "preLaunchTask": "npm" - }, - { - "name": "Launch Tests", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ], - "stopOnEntry": false, - "sourceMaps": true, - "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ], - "preLaunchTask": "npm" - } - ] -} Index: clangd/clients/clangd-vscode/.vscode/settings.json =================================================================== --- clangd/clients/clangd-vscode/.vscode/settings.json +++ /dev/null @@ -1,9 +0,0 @@ -// Place your settings in this file to overwrite default and user settings. -{ - "files.exclude": { - "out": false // set this to true to hide the "out" folder with the compiled JS files - }, - "search.exclude": { - "out": true // set this to false to include "out" folder in search results - } -} \ No newline at end of file Index: clangd/clients/clangd-vscode/.vscode/tasks.json =================================================================== --- clangd/clients/clangd-vscode/.vscode/tasks.json +++ /dev/null @@ -1,30 +0,0 @@ -// Available variables which can be used inside of strings. -// ${workspaceRoot}: the root folder of the team -// ${file}: the current opened file -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process - -// A task runner that calls a custom npm script that compiles the extension. -{ - "version": "0.1.0", - - // we want to run npm - "command": "npm", - - // the command is a shell script - "isShellCommand": true, - - // show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // we run the custom script "compile" as defined in package.json - "args": ["run", "compile", "--loglevel", "silent"], - - // The tsc compiler is started in watching mode - "isWatching": true, - - // use the standard tsc in watch mode problem matcher to find compile problems in the output. - "problemMatcher": "$tsc-watch" -} \ No newline at end of file Index: clangd/clients/clangd-vscode/.vscodeignore =================================================================== --- clangd/clients/clangd-vscode/.vscodeignore +++ /dev/null @@ -1,9 +0,0 @@ -.vscode/** -.vscode-test/** -out/test/** -test/** -src/** -**/*.map -.gitignore -tsconfig.json -vsc-extension-quickstart.md Index: clangd/clients/clangd-vscode/README.txt =================================================================== --- clangd/clients/clangd-vscode/README.txt +++ /dev/null @@ -1,11 +0,0 @@ -A *toy* VS Code integration for development purposes. - -Steps: -1. Make sure you have clangd in /usr/bin/clangd or edit src/extension.ts to -point to the binary. -2. Make sure you have nodejs and npm installed. -3. Make sure you have VS Code installed. -4. In order to start a development instance of VS code extended with this, run: - $ npm install - $ code . - When VS Code starts, press . Index: clangd/clients/clangd-vscode/package.json =================================================================== --- clangd/clients/clangd-vscode/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "clangd-vscode", - "displayName": "clangd-vscode", - "description": "Clang Language Server", - "version": "0.0.1", - "publisher": "Unpublished", - "engines": { - "vscode": "^1.8.0" - }, - "categories": [ - "Languages", - "Linters", - "Snippets" - ], - "activationEvents": [ - "onLanguage:cpp", - "onLanguage:c" - ], - "main": "./out/src/extension", - "scripts": { - "vscode:prepublish": "tsc -p ./", - "compile": "tsc -watch -p ./", - "postinstall": "node ./node_modules/vscode/bin/install", - "test": "node ./node_modules/vscode/bin/test" - }, - "dependencies": { - "vscode-languageclient": "^2.6.3", - "vscode-languageserver": "^2.6.2" - }, - "devDependencies": { - "typescript": "^2.0.3", - "vscode": "^1.0.3", - "mocha": "^2.3.3", - "@types/node": "^6.0.40", - "@types/mocha": "^2.2.32" - } -} \ No newline at end of file Index: clangd/clients/clangd-vscode/src/extension.ts =================================================================== --- clangd/clients/clangd-vscode/src/extension.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as vscode from 'vscode'; -import * as vscodelc from 'vscode-languageclient'; - -/** - * this method is called when your extension is activate - * your extension is activated the very first time the command is executed - */ -export function activate(context: vscode.ExtensionContext) { - // TODO: make this configurable - const clangdPath = '/usr/bin/clangd'; - - const serverOptions: vscodelc.ServerOptions = { command: clangdPath }; - - const clientOptions: vscodelc.LanguageClientOptions = { - // Register the server for C/C++ files - documentSelector: ['c', 'cc', 'cpp', 'h', 'hh', 'hpp'] - }; - - const clangdClient = new vscodelc.LanguageClient('Clang Language Server', serverOptions, clientOptions); - - function applyTextEdits(uri: string, edits: vscodelc.TextEdit[]) { - let textEditor = vscode.window.activeTextEditor; - - if (textEditor && textEditor.document.uri.toString() === uri) { - textEditor.edit(mutator => { - for (const edit of edits) { - mutator.replace(vscodelc.Protocol2Code.asRange(edit.range), edit.newText); - } - }).then((success) => { - if (!success) { - vscode.window.showErrorMessage('Failed to apply fixes to the document.'); - } - }); - } - } - - console.log('Clang Language Server is now active!'); - - const disposable = clangdClient.start(); - - context.subscriptions.push(disposable, vscode.commands.registerCommand('clangd.applyFix', applyTextEdits)); -} \ No newline at end of file Index: clangd/clients/clangd-vscode/test/extension.test.ts =================================================================== --- clangd/clients/clangd-vscode/test/extension.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** The module 'assert' provides assertion methods from node */ -import * as assert from 'assert'; - -import * as vscode from 'vscode'; -import * as myExtension from '../src/extension'; - -// TODO: add tests -suite("Extension Tests", () => { - - // Defines a Mocha unit test - test("Something 1", () => { - assert.equal(-1, [1, 2, 3].indexOf(5)); - assert.equal(-1, [1, 2, 3].indexOf(0)); - }); -}); \ No newline at end of file Index: clangd/clients/clangd-vscode/test/index.ts =================================================================== --- clangd/clients/clangd-vscode/test/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -// -// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING -// -// This file is providing the test runner to use when running extension tests. -// By default the test runner in use is Mocha based. -// -// You can provide your own test runner if you want to override it by exporting -// a function run(testRoot: string, clb: (error:Error) => void) that the extension -// host can call to run the tests. The test runner is expected to use console.log -// to report the results back to the caller. When the tests are finished, return -// a possible error to the callback or null if none. - -var testRunner = require('vscode/lib/testrunner'); - -// You can directly control Mocha options by uncommenting the following lines -// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info -testRunner.configure({ - ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) - useColors: true // colored output from test results -}); - -module.exports = testRunner; \ No newline at end of file Index: clangd/clients/clangd-vscode/tsconfig.json =================================================================== --- clangd/clients/clangd-vscode/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "outDir": "out", - "lib": [ - "es6", - "es2015.core", - "es2015.collection", - "es2015.generator", - "es2015.iterable", - "es2015.promise", - "es2015.symbol", - "es2016.array.include" - ], - "sourceMap": true, - "rootDir": ".", - "alwaysStrict": true, - "noEmitOnError": true, - "noFallthroughCasesInSwitch": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "noImplicitThis": true - }, - "exclude": [ - "node_modules", - ".vscode-test" - ] -} \ No newline at end of file Index: clangd/clients/clangd-vscode/vsc-extension-quickstart.md =================================================================== --- clangd/clients/clangd-vscode/vsc-extension-quickstart.md +++ /dev/null @@ -1,33 +0,0 @@ -# Toy VS Code Extension for clangd - -## What's in the folder -* This folder contains all of the files necessary for your extension -* `package.json` - this is the manifest file in which you declare your extension and command. -The sample plugin registers a command and defines its title and command name. With this information -VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. -* `src/extension.ts` - this is the main file where you will provide the implementation of your command. -The file exports one function, `activate`, which is called the very first time your extension is -activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. -We pass the function containing the implementation of the command as the second parameter to -`registerCommand`. - -## Get up and running straight away -* press `F5` to open a new window with your extension loaded -* run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World` -* set breakpoints in your code inside `src/extension.ts` to debug your extension -* find output from your extension in the debug console - -## Make changes -* you can relaunch the extension from the debug toolbar after changing code in `src/extension.ts` -* you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes - -## Explore the API -* you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts` - -## Run tests -* open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests` -* press `F5` to run the tests in a new window with your extension loaded -* see the output of the test result in the debug console -* make changes to `test/extension.test.ts` or create new test files inside the `test` folder - * by convention, the test runner will only consider files matching the name pattern `**.test.ts` - * you can create folders inside the `test` folder to structure your tests any way you want \ No newline at end of file Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -62,20 +62,11 @@ Finds modification of the ``std`` or ``posix`` namespace. -- Improved `cppcoreguidelines-no-malloc - `_ check - - Allow custom memory management functions to be considered as well. - - New `readability-misleading-indentation `_ check Finds misleading indentation where braces should be introduced or the code should be reformatted. -- Added `ParameterThreshold` to `readability-function-size`. - - Finds functions that have more then `ParameterThreshold` parameters and emits a warning. - - New `safety-no-assembler `_ check @@ -86,9 +77,11 @@ Finds and replaces explicit calls to the constructor in a return statement by a braced initializer list so that the return type is not needlessly repeated. + +- New `misc-forwarding-reference-overload + `_ check -- Support clang-formatting of the code around applied fixes (``-format-style`` - command-line option). + Finds perfect forwarding constructors that can unintentionally hide copy or move constructors. Improvements to include-fixer ----------------------------- Index: docs/clang-tidy/checks/cppcoreguidelines-no-malloc.rst =================================================================== --- docs/clang-tidy/checks/cppcoreguidelines-no-malloc.rst +++ docs/clang-tidy/checks/cppcoreguidelines-no-malloc.rst @@ -6,9 +6,8 @@ This check handles C-Style memory management using ``malloc()``, ``realloc()``, ``calloc()`` and ``free()``. It warns about its use and tries to suggest the use of an appropriate RAII object. -Furthermore, it can be configured to check against a user-specified list of functions -that are used for memory management (e.g. ``posix_memalign()``). -See `C++ Core Guidelines `_. +See `C++ Core Guidelines +`. There is no attempt made to provide fix-it hints, since manual resource management isn't easily transformed automatically into RAII. @@ -26,21 +25,3 @@ // Rather use a smartpointer or stack variable. struct some_struct* s = (struct some_struct*) malloc(sizeof(struct some_struct)); -Options -------- - -.. option:: Allocations - - Semicolon-separated list of fully qualified names of memory allocation functions. - Defaults to ``::malloc;::calloc``. - -.. option:: Deallocations - - Semicolon-separated list of fully qualified names of memory allocation functions. - Defaults to ``::free``. - -.. option:: Reallocations - - Semicolon-separated list of fully qualified names of memory allocation functions. - Defaults to ``::realloc``. - Index: docs/clang-tidy/checks/cppcoreguidelines-special-member-functions.rst =================================================================== --- docs/clang-tidy/checks/cppcoreguidelines-special-member-functions.rst +++ docs/clang-tidy/checks/cppcoreguidelines-special-member-functions.rst @@ -19,31 +19,3 @@ Guidelines, corresponding to rule C.21. See https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c21-if-you-define-or-delete-any-default-operation-define-or-delete-them-all. - -Options -------- - -.. option:: AllowSoleDefaultDtor - - When set to `1` (default is `0`), this check doesn't flag classes with a sole, explicitly - defaulted destructor. An example for such a class is: - - .. code-block:: c++ - - struct A { - virtual ~A() = default; - }; - -.. option:: AllowMissingMoveFunctions - - When set to `1` (default is `0`), this check doesn't flag classes which define no move - operations at all. It still flags classes which define only one of either - move constructor or move assignment operator. With this option enabled, the following class won't be flagged: - - .. code-block:: c++ - - struct A { - A(const A&); - A& operator=(const A&); - ~A(); - } Index: docs/clang-tidy/checks/hicpp-no-assembler.rst =================================================================== --- docs/clang-tidy/checks/hicpp-no-assembler.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. title:: clang-tidy - hicpp-no-assembler - -hicpp-no-assembler -=================== - -Check for assembler statements. No fix is offered. - -Inline assembler is forbidden by the `High Intergrity C++ Coding Standard -`_ -as it restricts the portability of code. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -53,7 +53,6 @@ google-runtime-memset google-runtime-operator google-runtime-references - hicpp-no-assembler llvm-header-guard llvm-include-order llvm-namespace-comment @@ -65,6 +64,7 @@ misc-definitions-in-headers misc-fold-init-type misc-forward-declaration-namespace + misc-forwarding-reference-overload misc-inaccurate-erase misc-incorrect-roundings misc-inefficient-algorithm @@ -156,3 +156,4 @@ readability-simplify-boolean-expr readability-static-definition-in-anonymous-namespace readability-uniqueptr-delete-release + safety-no-assembler Index: docs/clang-tidy/checks/misc-argument-comment.rst =================================================================== --- docs/clang-tidy/checks/misc-argument-comment.rst +++ docs/clang-tidy/checks/misc-argument-comment.rst @@ -24,6 +24,5 @@ .. option:: StrictMode - When zero (default value), the check will ignore leading and trailing - underscores and case when comparing names -- otherwise they are taken into - account. + When non-zero, the check will ignore leading and trailing underscores and + case when comparing parameter names. Index: docs/clang-tidy/checks/misc-forwarding-reference-overload.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-forwarding-reference-overload.rst @@ -0,0 +1,52 @@ +.. title:: clang-tidy - misc-forwarding-reference-overload + +misc-forwarding-reference-overload +================================== + +The check looks for perfect forwarding constructors that can hide copy or move +constructors. If a non const lvalue reference is passed to the constructor, the +forwarding reference parameter will be a better match than the const reference +parameter of the copy constructor, so the perfect forwarding constructor will be +called, which can be confusing. +For detailed description of this issue see: Scott Meyers, Effective Modern C++, +Item 26. + +Consider the following example: + + .. code-block:: c++ + + class Person { + public: + // C1: perfect forwarding ctor + template + explicit Person(T&& n) {} + + // C2: perfect forwarding ctor with parameter default value + template + explicit Person(T&& n, int x = 1) {} + + // C3: perfect forwarding ctor guarded with enable_if + template,void>> + explicit Person(T&& n) {} + + // (possibly compiler generated) copy ctor + Person(const Person& rhs); + }; + +The check warns for constructors C1 and C2, because those can hide copy and move +constructors. We suppress warnings if the copy and the move constructors are both +disabled (deleted or private), because there is nothing the perfect forwarding +constructor could hide in this case. We also suppress warnings for constructors +like C3 that are guarded with an enable_if, assuming the programmer was aware of +the possible hiding. + +Background +---------- + +For deciding whether a constructor is guarded with enable_if, we consider the +default values of the type parameters and the types of the contructor +parameters. If any part of these types is std::enable_if or std::enable_if_t, we +assume the constructor is guarded. + + + Index: docs/clang-tidy/checks/modernize-use-emplace.rst =================================================================== --- docs/clang-tidy/checks/modernize-use-emplace.rst +++ docs/clang-tidy/checks/modernize-use-emplace.rst @@ -36,7 +36,7 @@ std::vector> w; w.emplace_back(21, 37); - // This will be fixed to w.emplace_back(21L, 37L); in next version + // This will be fixed to w.push_back(21, 37); in next version w.emplace_back(std::make_pair(21L, 37L); The other situation is when we pass arguments that will be converted to a type @@ -69,7 +69,7 @@ This is because replacing it with ``emplace_back`` could cause a leak of this pointer if ``emplace_back`` would throw exception before emplacement (e.g. not -enough memory to add a new element). +enough memory to add new element). For more info read item 42 - "Consider emplacement instead of insertion." of Scott Meyers "Effective Modern C++". @@ -79,15 +79,14 @@ other classes use the :option:`SmartPointers` option. -Check also doesn't fire if any argument of the constructor call would be: +Check also fires if any argument of constructor call would be: - - a bit-field (bit-fields can't bind to rvalue/universal reference) + - bitfield (bitfields can't bind to rvalue/universal reference) - - a ``new`` expression (to avoid leak) + - ``new`` expression (to avoid leak) or if the argument would be converted via + derived-to-base cast. - - if the argument would be converted via derived-to-base cast. - -This check requires C++11 or higher to run. +This check requires C++11 of higher to run. Options ------- Index: docs/clang-tidy/checks/modernize-use-nullptr.rst =================================================================== --- docs/clang-tidy/checks/modernize-use-nullptr.rst +++ docs/clang-tidy/checks/modernize-use-nullptr.rst @@ -39,7 +39,7 @@ Options ------- -.. option:: NullMacros +.. option:: UserNullMacros Comma-separated list of macro names that will be transformed along with ``NULL``. By default this check will only replace the ``NULL`` macro and will @@ -64,4 +64,4 @@ int *p = nullptr; } -if the :option:`NullMacros` option is set to ``MY_NULL``. +if the :option:`UserNullMacros` option is set to ``MY_NULL``. Index: docs/clang-tidy/checks/readability-function-size.rst =================================================================== --- docs/clang-tidy/checks/readability-function-size.rst +++ docs/clang-tidy/checks/readability-function-size.rst @@ -25,8 +25,3 @@ Flag functions exceeding this number of control statements. The default is `-1` (ignore the number of branches). - -.. option:: ParameterThreshold - - Flag functions that exceed a specified number of parameters. The default - is `-1` (ignore the number of parameters). Index: docs/clang-tidy/checks/safety-no-assembler.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/safety-no-assembler.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - safety-no-assembler + +safety-no-assembler +=================== + +Check for assembler statements. No fix is offered. + +Inline assembler is forbidden by safety-critical C++ standards like `High +Intergrity C++ `_ as it restricts the +portability of code. Index: docs/clang-tidy/index.rst =================================================================== --- docs/clang-tidy/index.rst +++ docs/clang-tidy/index.rst @@ -158,17 +158,6 @@ errors were found. If compiler errors have attached fix-its, clang-tidy will apply them as well. - -format-style= - - Style for formatting code around applied fixes: - - 'none' (default) turns off formatting - - 'file' (literally 'file', not a placeholder) - uses .clang-format file in the closest parent - directory - - '{ }' specifies options inline, e.g. - -format-style='{BasedOnStyle: llvm, IndentWidth: 8}' - - 'llvm', 'google', 'webkit', 'mozilla' - See clang-format documentation for the up-to-date - information about formatting styles and options. -header-filter= - Regular expression matching the names of the headers to output diagnostics from. Diagnostics @@ -190,11 +179,6 @@ List all enabled checks and exit. Use with -checks=* to list all available checks. -p= - Build path - -quiet - - Run clang-tidy in quiet mode. This suppresses - printing statistics about ignored warnings and - warnings treated as errors if the respective - options are specified. -style= - Fallback style for reformatting after inserting fixes if there is no clang-format config file found. @@ -574,10 +558,10 @@ against the fixed code (i.e., the code after generated fix-its are applied). In particular, ``CHECK-FIXES:`` can be used to check that code was not modified by fix-its, by checking that it is present -unchanged in the fixed code. The full set of `FileCheck`_ directives +unchanged in the fixed code. The full set of `FileCheck`_ directives is available (e.g., ``CHECK-MESSAGES-SAME:``, ``CHECK-MESSAGES-NOT:``), though typically the basic ``CHECK`` forms (``CHECK-MESSAGES`` and ``CHECK-FIXES``) -are sufficient for clang-tidy tests. Note that the `FileCheck`_ +are sufficient for clang-tidy tests. Note that the `FileCheck`_ documentation mostly assumes the default prefix (``CHECK``), and hence describes the directive as ``CHECK:``, ``CHECK-SAME:``, ``CHECK-NOT:``, etc. Replace ``CHECK`` by either ``CHECK-FIXES`` or ``CHECK-MESSAGES`` for Index: include-fixer/CMakeLists.txt =================================================================== --- include-fixer/CMakeLists.txt +++ include-fixer/CMakeLists.txt @@ -6,7 +6,6 @@ IncludeFixer.cpp IncludeFixerContext.cpp InMemorySymbolIndex.cpp - FuzzySymbolIndex.cpp SymbolIndexManager.cpp YamlSymbolIndex.cpp Index: include-fixer/FuzzySymbolIndex.h =================================================================== --- include-fixer/FuzzySymbolIndex.h +++ /dev/null @@ -1,55 +0,0 @@ -//===--- FuzzySymbolIndex.h - Lookup symbols for autocomplete ---*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H -#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H - -#include "SymbolIndex.h" -#include "find-all-symbols/SymbolInfo.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Error.h" -#include -#include - -namespace clang { -namespace include_fixer { - -// A FuzzySymbolIndex retrieves top-level symbols matching a query string. -// -// It refines the contract of SymbolIndex::search to do fuzzy matching: -// - symbol names are tokenized: "unique ptr", "string ref". -// - query must match prefixes of symbol tokens: [upt] -// - if the query has multiple tokens, splits must match: [StR], not [STr]. -// Helpers for tokenization and regex matching are provided. -// -// Implementations may choose to truncate results, refuse short queries, etc. -class FuzzySymbolIndex : public SymbolIndex { -public: - // Loads the specified include-fixer database and returns an index serving it. - static llvm::Expected> - createFromYAML(llvm::StringRef File); - - // Helpers for implementing indexes: - - // Transforms a symbol name or query into a sequence of tokens. - // - URLHandlerCallback --> [url, handler, callback] - // - snake_case11 --> [snake, case, 11] - // - _WTF$ --> [wtf] - static std::vector tokenize(llvm::StringRef Text); - - // Transforms query tokens into an unanchored regexp to match symbol tokens. - // - [fe f] --> /f(\w* )?e\w* f/, matches [fee fie foe]. - static std::string queryRegexp(const std::vector &Tokens); -}; - -} // namespace include_fixer -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H Index: include-fixer/FuzzySymbolIndex.cpp =================================================================== --- include-fixer/FuzzySymbolIndex.cpp +++ /dev/null @@ -1,143 +0,0 @@ -//===--- FuzzySymbolIndex.cpp - Lookup symbols for autocomplete -*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#include "FuzzySymbolIndex.h" -#include "llvm/Support/Regex.h" - -using clang::find_all_symbols::SymbolAndSignals; -using llvm::StringRef; - -namespace clang { -namespace include_fixer { -namespace { - -class MemSymbolIndex : public FuzzySymbolIndex { -public: - MemSymbolIndex(std::vector Symbols) { - for (auto &Symbol : Symbols) { - auto Tokens = tokenize(Symbol.Symbol.getName()); - this->Symbols.emplace_back( - StringRef(llvm::join(Tokens.begin(), Tokens.end(), " ")), - std::move(Symbol)); - } - } - - std::vector search(StringRef Query) override { - auto Tokens = tokenize(Query); - llvm::Regex Pattern("^" + queryRegexp(Tokens)); - std::vector Results; - for (const Entry &E : Symbols) - if (Pattern.match(E.first)) - Results.push_back(E.second); - return Results; - } - -private: - using Entry = std::pair, SymbolAndSignals>; - std::vector Symbols; -}; - -// Helpers for tokenize state machine. -enum TokenizeState { - EMPTY, // No pending characters. - ONE_BIG, // Read one uppercase letter, could be WORD or Word. - BIG_WORD, // Reading an uppercase WORD. - SMALL_WORD, // Reading a lowercase word. - NUMBER // Reading a number. -}; - -enum CharType { UPPER, LOWER, DIGIT, MISC }; -CharType classify(char c) { - if (isupper(c)) - return UPPER; - if (islower(c)) - return LOWER; - if (isdigit(c)) - return DIGIT; - return MISC; -} - -} // namespace - -std::vector FuzzySymbolIndex::tokenize(StringRef Text) { - std::vector Result; - // State describes the treatment of text from Start to I. - // Once text is Flush()ed into Result, we're done with it and advance Start. - TokenizeState State = EMPTY; - size_t Start = 0; - auto Flush = [&](size_t End) { - if (State != EMPTY) { - Result.push_back(Text.substr(Start, End - Start).lower()); - State = EMPTY; - } - Start = End; - }; - for (size_t I = 0; I < Text.size(); ++I) { - CharType Type = classify(Text[I]); - if (Type == MISC) - Flush(I); - else if (Type == LOWER) - switch (State) { - case BIG_WORD: - Flush(I - 1); // FOOBar: first token is FOO, not FOOB. - LLVM_FALLTHROUGH; - case ONE_BIG: - State = SMALL_WORD; - LLVM_FALLTHROUGH; - case SMALL_WORD: - break; - default: - Flush(I); - State = SMALL_WORD; - } - else if (Type == UPPER) - switch (State) { - case ONE_BIG: - State = BIG_WORD; - LLVM_FALLTHROUGH; - case BIG_WORD: - break; - default: - Flush(I); - State = ONE_BIG; - } - else if (Type == DIGIT && State != NUMBER) { - Flush(I); - State = NUMBER; - } - } - Flush(Text.size()); - return Result; -} - -std::string -FuzzySymbolIndex::queryRegexp(const std::vector &Tokens) { - std::string Result; - for (size_t I = 0; I < Tokens.size(); ++I) { - if (I) - Result.append("[[:alnum:]]* "); - for (size_t J = 0; J < Tokens[I].size(); ++J) { - if (J) - Result.append("([[:alnum:]]* )?"); - Result.push_back(Tokens[I][J]); - } - } - return Result; -} - -llvm::Expected> -FuzzySymbolIndex::createFromYAML(StringRef FilePath) { - auto Buffer = llvm::MemoryBuffer::getFile(FilePath); - if (!Buffer) - return llvm::errorCodeToError(Buffer.getError()); - return llvm::make_unique( - find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer())); -} - -} // namespace include_fixer -} // namespace clang Index: include-fixer/InMemorySymbolIndex.h =================================================================== --- include-fixer/InMemorySymbolIndex.h +++ include-fixer/InMemorySymbolIndex.h @@ -21,14 +21,13 @@ /// 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::SymbolAndSignals; +using clang::find_all_symbols::SymbolInfo; namespace clang { namespace include_fixer { InMemorySymbolIndex::InMemorySymbolIndex( - const std::vector &Symbols) { + const std::vector &Symbols) { for (const auto &Symbol : Symbols) - LookupTable[Symbol.Symbol.getName()].push_back(Symbol); + LookupTable[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 @@ -333,7 +333,9 @@ : "\"" + FilePath + "\""), SourceManager, HeaderSearch); SymbolCandidates.emplace_back(Symbol.getName(), Symbol.getSymbolKind(), - MinimizedFilePath, Symbol.getContexts()); + MinimizedFilePath, Symbol.getLineNumber(), + Symbol.getContexts(), + Symbol.getNumOccurrences()); } 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,8 +19,7 @@ namespace clang { namespace include_fixer { -using find_all_symbols::SymbolInfo; -using find_all_symbols::SymbolAndSignals; +using clang::find_all_symbols::SymbolInfo; // Calculate a score based on whether we think the given header is closely // related to the given source file. @@ -46,26 +45,26 @@ return MaxSegments; } -static void rank(std::vector &Symbols, +static void rank(std::vector &Symbols, llvm::StringRef FileName) { llvm::DenseMap Score; - for (const auto &Symbol : Symbols) { + for (const SymbolInfo &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.Symbol.getFilePath()) * - (1.0 + std::log2(1 + Symbol.Signals.Seen)); - double &S = Score[Symbol.Symbol.getFilePath()]; + double NewScore = similarityScore(FileName, Symbol.getFilePath()) * + (1.0 + std::log2(1 + Symbol.getNumOccurrences())); + double &S = Score[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 SymbolAndSignals &A, const SymbolAndSignals &B) { - auto AS = Score[A.Symbol.getFilePath()]; - auto BS = Score[B.Symbol.getFilePath()]; + [&](const SymbolInfo &A, const SymbolInfo &B) { + auto AS = Score[A.getFilePath()]; + auto BS = Score[B.getFilePath()]; if (AS != BS) return AS > BS; - return A.Symbol.getFilePath() < B.Symbol.getFilePath(); + return A.getFilePath() < B.getFilePath(); }); } @@ -89,9 +88,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()); @@ -100,47 +99,48 @@ DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got " << Symbols.size() << " results...\n"); - for (auto &SymAndSig : Symbols) { - const SymbolInfo &Symbol = SymAndSig.Symbol; + for (const auto &Symbol : Symbols) { // Match the identifier name without qualifier. - bool IsMatched = true; - auto SymbolContext = Symbol.getContexts().begin(); - auto IdentiferContext = Names.rbegin() + 1; // Skip identifier name. - // Match the remaining context names. - while (IdentiferContext != Names.rend() && - SymbolContext != Symbol.getContexts().end()) { - if (SymbolContext->second == *IdentiferContext) { - ++IdentiferContext; - ++SymbolContext; - } else if (SymbolContext->first == - find_all_symbols::SymbolInfo::ContextType::EnumDecl) { - // Skip non-scoped enum context. - ++SymbolContext; - } else { - IsMatched = false; - break; + if (Symbol.getName() == Names.back()) { + bool IsMatched = true; + auto SymbolContext = Symbol.getContexts().begin(); + auto IdentiferContext = Names.rbegin() + 1; // Skip identifier name. + // Match the remaining context names. + while (IdentiferContext != Names.rend() && + SymbolContext != Symbol.getContexts().end()) { + if (SymbolContext->second == *IdentiferContext) { + ++IdentiferContext; + ++SymbolContext; + } else if (SymbolContext->first == + find_all_symbols::SymbolInfo::ContextType::EnumDecl) { + // Skip non-scoped enum context. + ++SymbolContext; + } else { + IsMatched = false; + break; + } } - } - // If the name was qualified we only want to add results if we evaluated - // all contexts. - if (IsFullyQualified) - IsMatched &= (SymbolContext == Symbol.getContexts().end()); - - // FIXME: Support full match. At this point, we only find symbols in - // database which end with the same contexts with the identifier. - if (IsMatched && IdentiferContext == Names.rend()) { - // If we're in a situation where we took a prefix but the thing we - // found couldn't possibly have a nested member ignore it. - if (TookPrefix && - (Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Function || - Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Variable || - Symbol.getSymbolKind() == - SymbolInfo::SymbolKind::EnumConstantDecl || - Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Macro)) - continue; - - MatchedSymbols.push_back(std::move(SymAndSig)); + // If the name was qualified we only want to add results if we evaluated + // all contexts. + if (IsFullyQualified) + IsMatched &= (SymbolContext == Symbol.getContexts().end()); + + // FIXME: Support full match. At this point, we only find symbols in + // database which end with the same contexts with the identifier. + if (IsMatched && IdentiferContext == Names.rend()) { + // If we're in a situation where we took a prefix but the thing we + // found couldn't possibly have a nested member ignore it. + if (TookPrefix && + (Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Function || + Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Variable || + Symbol.getSymbolKind() == + SymbolInfo::SymbolKind::EnumConstantDecl || + Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Macro)) + continue; + + MatchedSymbols.push_back(Symbol); + } } } Names.pop_back(); @@ -148,11 +148,7 @@ } while (MatchedSymbols.empty() && !Names.empty() && IsNestedSearch); rank(MatchedSymbols, FileName); - // Strip signals, they are no longer needed. - std::vector Res; - for (auto &SymAndSig : MatchedSymbols) - Res.push_back(std::move(SymAndSig.Symbol)); - return Res; + return MatchedSymbols; } } // 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,7 +17,6 @@ #include using clang::find_all_symbols::SymbolInfo; -using clang::find_all_symbols::SymbolAndSignals; namespace clang { namespace include_fixer { @@ -28,8 +27,9 @@ if (!Buffer) return Buffer.getError(); - return std::unique_ptr(new YamlSymbolIndex( - find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer()))); + return std::unique_ptr( + new YamlSymbolIndex(clang::find_all_symbols::ReadSymbolInfosFromYAML( + Buffer.get()->getBuffer()))); } llvm::ErrorOr> @@ -47,11 +47,10 @@ 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.Symbol.getName() == Identifier) + if (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,7 +16,6 @@ #include "clang/Lex/PPCallbacks.h" namespace clang { -class MacroInfo; namespace find_all_symbols { class HeaderMapCollector; @@ -33,24 +32,7 @@ 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,57 +13,24 @@ #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, {}); -} - void FindAllMacros::MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) { - if (auto Symbol = CreateMacroSymbol(MacroNameTok, MD->getMacroInfo())) - ++FileSymbols[*Symbol].Seen; -} - -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; -} + SourceLocation Loc = SM->getExpansionLoc(MacroNameTok.getLocation()); + std::string FilePath = getIncludePath(*SM, Loc, Collector); + if (FilePath.empty()) return; -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); -} + SymbolInfo Symbol(MacroNameTok.getIdentifierInfo()->getName(), + SymbolInfo::SymbolKind::Macro, FilePath, + SM->getSpellingLineNumber(Loc), {}); -void FindAllMacros::EndOfMainFile() { - Reporter->reportSymbols(SM->getFileEntryForID(SM->getMainFileID())->getName(), - FileSymbols); - FileSymbols.clear(); + Reporter->reportSymbol(SM->getFileEntryForID(SM->getMainFileID())->getName(), + Symbol); } } // 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,24 +32,18 @@ /// through the class. #include fixer only needs the class name to find /// headers. /// -class FindAllSymbols : public ast_matchers::MatchFinder::MatchCallback { +class FindAllSymbols : public clang::ast_matchers::MatchFinder::MatchCallback { public: explicit FindAllSymbols(SymbolReporter *Reporter, HeaderMapCollector *Collector = nullptr) : Reporter(Reporter), Collector(Collector) {} - void registerMatchers(ast_matchers::MatchFinder *MatchFinder); + void registerMatchers(clang::ast_matchers::MatchFinder *MatchFinder); - void run(const ast_matchers::MatchFinder::MatchResult &result) override; - -protected: - void onEndOfTranslationUnit() override; + void + run(const clang::ast_matchers::MatchFinder::MatchResult &result) 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 @@ -109,7 +109,8 @@ std::string FilePath = getIncludePath(SM, Loc, Collector); if (FilePath.empty()) return llvm::None; - return SymbolInfo(ND->getNameAsString(), Type, FilePath, GetContexts(ND)); + return SymbolInfo(ND->getNameAsString(), Type, FilePath, + SM.getExpansionLineNumber(Loc), GetContexts(ND)); } } // namespace @@ -153,84 +154,64 @@ // 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. - auto Vars = varDecl(CommonFilter, anyOf(ExternCMatcher, CCMatcher), - unless(parmVarDecl())); + MatchFinder->addMatcher(varDecl(CommonFilter, + anyOf(ExternCMatcher, CCMatcher), + unless(parmVarDecl())) + .bind("decl"), + this); // Matchers for C-style record declarations in extern "C" {...}. - auto CRecords = recordDecl(CommonFilter, ExternCMatcher, isDefinition()); + MatchFinder->addMatcher( + recordDecl(CommonFilter, ExternCMatcher, isDefinition()).bind("decl"), + this); + // Matchers for C++ record declarations. - auto CXXRecords = cxxRecordDecl(CommonFilter, CCMatcher, isDefinition()); + auto CxxRecordDecl = + cxxRecordDecl(CommonFilter, CCMatcher, isDefinition()); + MatchFinder->addMatcher(CxxRecordDecl.bind("decl"), this); // 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`. - auto Functions = functionDecl(CommonFilter, unless(hasParent(friendDecl())), - anyOf(ExternCMatcher, CCMatcher)); + MatchFinder->addMatcher(functionDecl(CommonFilter, + unless(hasParent(friendDecl())), + anyOf(ExternCMatcher, CCMatcher)) + .bind("decl"), + this); // Matcher for typedef and type alias declarations. // - // typedef and type alias can come from C-style headers and C++ headers. - // For C-style headers, `DeclContxet` can be either `TranslationUnitDecl` + // typedef and type alias can come from C-style headers and C++ heaeders. + // For C-style header, `DeclContxet` can be either `TranslationUnitDecl` // or `LinkageSpecDecl`. - // For C++ headers, `DeclContext ` can be either `TranslationUnitDecl` - // or `NamespaceDecl`. + // For C++ header, `DeclContext ` can be one of `TranslationUnitDecl`, + // `NamespaceDecl`. // With the following context matcher, we can match `typedefNameDecl` from - // both C-style headers and C++ headers (except for those in classes). + // both C-style header and C++ header (except for those in classes). // "cc_matchers" are not included since template-related matchers are not // applicable on `TypedefNameDecl`. - auto Typedefs = + MatchFinder->addMatcher( typedefNameDecl(CommonFilter, anyOf(HasNSOrTUCtxMatcher, - hasDeclContext(linkageSpecDecl()))); + hasDeclContext(linkageSpecDecl()))) + .bind("decl"), + this); // Matchers for enum declarations. - auto Enums = enumDecl(CommonFilter, isDefinition(), - anyOf(HasNSOrTUCtxMatcher, ExternCMatcher)); + MatchFinder->addMatcher(enumDecl(CommonFilter, isDefinition(), + anyOf(HasNSOrTUCtxMatcher, ExternCMatcher)) + .bind("decl"), + this); // 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)); - 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 often 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"))))))), + enumConstantDecl( + CommonFilter, + unless(isInScopedEnum()), + anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher)) + .bind("decl"), this); } @@ -240,28 +221,15 @@ return; } - 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 auto *ND = Result.Nodes.getNodeAs("decl"); + assert(ND && "Matched declaration must be a NamedDecl!"); 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 = ""; - } + llvm::Optional Symbol = + CreateSymbolInfo(ND, *SM, Collector); + if (Symbol) + Reporter->reportSymbol( + SM->getFileEntryForID(SM->getMainFileID())->getName(), *Symbol); } } // 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(CompilerInstance &Compiler, +std::unique_ptr +FindAllSymbolsAction::CreateASTConsumer(clang::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,11 +20,8 @@ namespace clang { namespace find_all_symbols { -/// \brief Describes a named symbol from a header. -/// Symbols with the same qualified name and type (e.g. function overloads) -/// that appear in the same header are represented by a single SymbolInfo. -/// -/// TODO: keep track of instances, e.g. overload locations and signatures. + +/// \brief Contains all information for a Symbol. class SymbolInfo { public: /// \brief The SymbolInfo Type. @@ -49,31 +46,13 @@ /// \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; - bool 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) {} + SymbolInfo() : Type(SymbolKind::Unknown), LineNumber(-1) {} SymbolInfo(llvm::StringRef Name, SymbolKind Type, llvm::StringRef FilePath, - const std::vector &Contexts); + int LineNumber, const std::vector &Contexts, + unsigned NumOccurrences = 0); void SetFilePath(llvm::StringRef Path) { FilePath = Path; } @@ -94,12 +73,18 @@ return Contexts; } + /// \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; @@ -122,20 +107,21 @@ /// /// If the symbol is declared in `TranslationUnitDecl`, it has no context. std::vector Contexts; -}; -struct SymbolAndSignals { - SymbolInfo Symbol; - SymbolInfo::Signals Signals; - bool operator==(const SymbolAndSignals& RHS) const; + /// \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; }; /// \brief Write SymbolInfos to a stream (YAML format). bool WriteSymbolInfosToStream(llvm::raw_ostream &OS, - const SymbolInfo::SignalMap &Symbols); + const std::set &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,23 +18,22 @@ 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(SymbolAndSignals) +LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(SymbolInfo) 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, SymbolAndSignals &Symbol) { - io.mapRequired("Name", Symbol.Symbol.Name); - io.mapRequired("Contexts", Symbol.Symbol.Contexts); - io.mapRequired("FilePath", Symbol.Symbol.FilePath); - io.mapRequired("Type", Symbol.Symbol.Type); - io.mapRequired("Seen", Symbol.Signals.Seen); - io.mapRequired("Used", Symbol.Signals.Used); +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); } }; @@ -73,18 +72,22 @@ namespace find_all_symbols { SymbolInfo::SymbolInfo(llvm::StringRef Name, SymbolKind Type, - llvm::StringRef FilePath, - const std::vector &Contexts) - : Name(Name), Type(Type), FilePath(FilePath), Contexts(Contexts) {} + llvm::StringRef FilePath, int LineNumber, + const std::vector &Contexts, + unsigned NumOccurrences) + : Name(Name), Type(Type), FilePath(FilePath), Contexts(Contexts), + LineNumber(LineNumber), NumOccurrences(NumOccurrences) {} bool SymbolInfo::operator==(const SymbolInfo &Symbol) const { - return std::tie(Name, Type, FilePath, Contexts) == - std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.Contexts); + return std::tie(Name, Type, FilePath, LineNumber, Contexts) == + std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.LineNumber, + Symbol.Contexts); } bool SymbolInfo::operator<(const SymbolInfo &Symbol) const { - return std::tie(Name, Type, FilePath, Contexts) < - std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.Contexts); + return std::tie(Name, Type, FilePath, LineNumber, Contexts) < + std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.LineNumber, + Symbol.Contexts); } std::string SymbolInfo::getQualifiedName() const { @@ -97,38 +100,16 @@ 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 SymbolInfo::Signals::operator==(const Signals &RHS) const { - return std::tie(Seen, Used) == std::tie(RHS.Seen, RHS.Used); -} - -bool SymbolAndSignals::operator==(const SymbolAndSignals& RHS) const { - return std::tie(Symbol, Signals) == std::tie(RHS.Symbol, RHS.Signals); -} - bool WriteSymbolInfosToStream(llvm::raw_ostream &OS, - const SymbolInfo::SignalMap &Symbols) { + const std::set &Symbols) { llvm::yaml::Output yout(OS); - for (const auto &Symbol : Symbols) { - SymbolAndSignals S{Symbol.first, Symbol.second}; - yout << S; - } + for (auto Symbol : Symbols) + yout << Symbol; 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 reportSymbols(llvm::StringRef FileName, - const SymbolInfo::SignalMap &Symbols) = 0; + virtual void reportSymbol(llvm::StringRef FileName, + const SymbolInfo &Symbol) = 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,30 +62,38 @@ namespace clang { namespace find_all_symbols { -class YamlReporter : public SymbolReporter { +class YamlReporter : public clang::find_all_symbols::SymbolReporter { public: - 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); + ~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); } + +private: + std::map> Symbols; }; bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) { std::error_code EC; - SymbolInfo::SignalMap Symbols; + std::map SymbolToNumOccurrences; std::mutex SymbolMutex; - auto AddSymbols = [&](ArrayRef NewSymbols) { + auto AddSymbols = [&](ArrayRef Symbols) { // Synchronize set accesses. std::unique_lock LockGuard(SymbolMutex); - for (const auto &Symbol : NewSymbols) { - Symbols[Symbol.Symbol] += Symbol.Signals; - } + for (const auto &Symbol : Symbols) + ++SymbolToNumOccurrences[Symbol]; }; // Load all symbol files in MergeDir. @@ -101,13 +109,8 @@ 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); }, @@ -121,7 +124,14 @@ << '\n'; return false; } - WriteSymbolInfosToStream(OS, Symbols); + 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); return true; } Index: include-fixer/tool/ClangIncludeFixer.cpp =================================================================== --- include-fixer/tool/ClangIncludeFixer.cpp +++ include-fixer/tool/ClangIncludeFixer.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "FuzzySymbolIndex.h" #include "InMemorySymbolIndex.h" #include "IncludeFixer.h" #include "IncludeFixerContext.h" @@ -84,16 +83,14 @@ cl::OptionCategory IncludeFixerCategory("Tool options"); enum DatabaseFormatTy { - fixed, ///< Hard-coded mapping. - yaml, ///< Yaml database created by find-all-symbols. - fuzzyYaml, ///< Yaml database with fuzzy-matched identifiers. + fixed, ///< Hard-coded mapping. + yaml, ///< Yaml database created by find-all-symbols. }; cl::opt DatabaseFormat( "db", cl::desc("Specify input format"), cl::values(clEnumVal(fixed, "Hard-coded mapping"), - clEnumVal(yaml, "Yaml database created by find-all-symbols"), - clEnumVal(fuzzyYaml, "Yaml database, with fuzzy-matched names")), + clEnumVal(yaml, "Yaml database created by find-all-symbols")), cl::init(yaml), cl::cat(IncludeFixerCategory)); cl::opt Input("input", @@ -161,8 +158,6 @@ std::unique_ptr createSymbolIndexManager(StringRef FilePath) { - using find_all_symbols::SymbolInfo; - auto SymbolIndexMgr = llvm::make_unique(); switch (DatabaseFormat) { case fixed: { @@ -172,19 +167,17 @@ 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( - {SymbolInfo(Split.first.trim(), SymbolInfo::SymbolKind::Unknown, - CommaSplits[I].trim(), {}), - // Use fake "seen" signal for tests, so first header wins. - SymbolInfo::Signals(/*Seen=*/static_cast(E - I), - /*Used=*/0)}); + Symbols.push_back(find_all_symbols::SymbolInfo( + Split.first.trim(), + find_all_symbols::SymbolInfo::SymbolKind::Unknown, + CommaSplits[I].trim(), 1, {}, /*NumOccurrences=*/E - I)); } SymbolIndexMgr->addSymbolIndex([=]() { return llvm::make_unique(Symbols); @@ -218,21 +211,6 @@ SymbolIndexMgr->addSymbolIndex(std::move(CreateYamlIdx)); break; } - case fuzzyYaml: { - // This mode is not very useful, because we don't correct the identifier. - // It's main purpose is to expose FuzzySymbolIndex to tests. - SymbolIndexMgr->addSymbolIndex( - []() -> std::unique_ptr { - auto DB = include_fixer::FuzzySymbolIndex::createFromYAML(Input); - if (!DB) { - llvm::errs() << "Couldn't load fuzzy YAML db: " - << llvm::toString(DB.takeError()) << '\n'; - return nullptr; - } - return std::move(*DB); - }); - break; - } } return SymbolIndexMgr; } Index: include-fixer/tool/clang-include-fixer-test.el =================================================================== --- include-fixer/tool/clang-include-fixer-test.el +++ include-fixer/tool/clang-include-fixer-test.el @@ -23,18 +23,6 @@ (should (equal (buffer-string) "aa\nab\nac\nad\n"))))) (should (equal (buffer-string) "aa\nab\nac\nad\n")))) -(ert-deftest clang-include-fixer--insert-line-diff-on-empty-line () - "Unit test for `clang-include-fixer--insert-line'." - (with-temp-buffer - (insert "aa\nab\n\nac\nad\n") - (let ((from (current-buffer))) - (with-temp-buffer - (insert "aa\n\nac\nad\n") - (let ((to (current-buffer))) - (should (clang-include-fixer--insert-line from to)) - (should (equal (buffer-string) "aa\nab\n\nac\nad\n"))))) - (should (equal (buffer-string) "aa\nab\n\nac\nad\n")))) - (ert-deftest clang-include-fixer--symbol-at-point () "Unit test for `clang-include-fixer--symbol-at-point'." (with-temp-buffer @@ -51,15 +39,4 @@ (goto-char (point-max)) (should (equal (clang-include-fixer--symbol-at-point) "bbb::cc")))) -(ert-deftest clang-include-fixer--highlight () - (with-temp-buffer - (insert "util::Status foo;\n") - (setq buffer-file-coding-system 'utf-8-unix) - (should (equal nil (clang-include-fixer--highlight - '((Range . ((Offset . 0) (Length . 0))))))) - (let ((overlay (clang-include-fixer--highlight - '((Range . ((Offset . 1) (Length . 12))))))) - (should (equal 2 (overlay-start overlay))) - (should (equal 14 (overlay-end overlay)))))) - ;;; clang-include-fixer-test.el ends here Index: include-fixer/tool/clang-include-fixer.el =================================================================== --- include-fixer/tool/clang-include-fixer.el +++ include-fixer/tool/clang-include-fixer.el @@ -213,14 +213,16 @@ (if (zerop chars) ;; Buffer contents are equal, nothing to do. t - (goto-char chars) + (goto-char (point-min)) + (forward-char chars) ;; We might have ended up in the middle of a line if the ;; current line partially matches. In this case we would ;; have to insert more than a line. Move to the beginning of ;; the line to avoid this situation. (beginning-of-line) (with-current-buffer from - (goto-char chars) + (goto-char (point-min)) + (forward-char chars) (beginning-of-line) (let ((from-begin (point)) (from-end (progn (forward-line) (point))) @@ -266,10 +268,9 @@ (clang-include-fixer--replace-buffer stdout) (let-alist context (let-alist (car .HeaderInfos) - (with-local-quit - (run-hook-with-args 'clang-include-fixer-add-include-hook - (substring .Header 1 -1) - (string= (substring .Header 0 1) "<"))))))) + (run-hook-with-args 'clang-include-fixer-add-include-hook + (substring .Header 1 -1) + (string= (substring .Header 0 1) "<")))))) (format "-insert-header=%s" (clang-include-fixer--encode-json context)))))))) nil) @@ -299,14 +300,12 @@ (let ((symbol (clang-include-fixer--symbol-name .QuerySymbolInfos)) ;; Add temporary highlighting so that the user knows which ;; symbols the current session is about. - (overlays (remove nil - (mapcar #'clang-include-fixer--highlight .QuerySymbolInfos)))) + (overlays (mapcar #'clang-include-fixer--highlight .QuerySymbolInfos))) (unwind-protect (save-excursion ;; While prompting, go to the closest overlay so that the user sees ;; some context. - (when overlays - (goto-char (clang-include-fixer--closest-overlay overlays))) + (goto-char (clang-include-fixer--closest-overlay overlays)) (cl-flet ((header (info) (let-alist info .Header))) ;; The header-infos is already sorted by include-fixer. (let* ((header (completing-read @@ -332,17 +331,16 @@ (car symbols))) (defun clang-include-fixer--highlight (symbol-info) - "Add an overlay to highlight SYMBOL-INFO, if it points to a non-empty range. -Return the overlay object, or nil." - (let-alist symbol-info - (unless (zerop .Range.Length) - (let ((overlay (make-overlay - (clang-include-fixer--filepos-to-bufferpos - .Range.Offset 'approximate) - (clang-include-fixer--filepos-to-bufferpos - (+ .Range.Offset .Range.Length) 'approximate)))) - (overlay-put overlay 'face 'clang-include-fixer-highlight) - overlay)))) + "Add an overlay to highlight SYMBOL-INFO. +Return the overlay object." + (let ((overlay (let-alist symbol-info + (make-overlay + (clang-include-fixer--filepos-to-bufferpos + .Range.Offset 'approximate) + (clang-include-fixer--filepos-to-bufferpos + (+ .Range.Offset .Range.Length) 'approximate))))) + (overlay-put overlay 'face 'clang-include-fixer-highlight) + overlay)) (defun clang-include-fixer--closest-overlay (overlays) "Return the start of the overlay in OVERLAYS that is closest to point." Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -31,6 +31,9 @@ endif() set(CLANG_TOOLS_TEST_DEPS + # Base line deps. + FileCheck count not + # clang-tidy tests require it. clang-headers @@ -55,13 +58,6 @@ ExtraToolsUnitTests ) -if(NOT LLVM_UTILS_PROVIDED) - list(APPEND CLANG_TOOLS_TEST_DEPS - # Base line deps. - FileCheck count not - ) -endif() - add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests" ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${CLANG_TOOLS_TEST_DEPS} Index: test/change-namespace/Inputs/fake-std.h =================================================================== --- test/change-namespace/Inputs/fake-std.h +++ /dev/null @@ -1,5 +0,0 @@ -namespace std { - class STD {}; -} - -using namespace std; Index: test/change-namespace/white-list.cpp =================================================================== --- test/change-namespace/white-list.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// RUN: echo "^std::.*$" > %T/white-list.txt -// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" --whitelist_file %T/white-list.txt %s -- | sed 's,// CHECK.*,,' | FileCheck %s - -#include "Inputs/fake-std.h" - -// CHECK: namespace x { -// CHECK-NEXT: namespace y { -namespace na { -namespace nb { -void f() { - std::STD x1; - STD x2; -// CHECK: {{^}} std::STD x1;{{$}} -// CHECK-NEXT: {{^}} STD x2;{{$}} -} -// CHECK: } // namespace y -// CHECK-NEXT: } // namespace x -} -} Index: test/clang-move/Inputs/var_test.h =================================================================== --- test/clang-move/Inputs/var_test.h +++ /dev/null @@ -1,11 +0,0 @@ -namespace a { -extern int kGlobalInt; -extern const char *const kGlobalStr; -} - -int kEvilInt = 2; - -inline void f1() { - int kGlobalInt = 3; - const char *const kGlobalStr = "Hello2"; -} Index: test/clang-move/Inputs/var_test.cpp =================================================================== --- test/clang-move/Inputs/var_test.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "var_test.h" - -namespace a{ -int kGlobalInt = 1; -const char *const kGlobalStr = "Hello"; -} Index: test/clang-move/move-var.cpp =================================================================== --- test/clang-move/move-var.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// RUN: mkdir -p %T/move-var -// RUN: cp %S/Inputs/var_test* %T/move-var -// RUN: cd %T/move-var -// RUN: clang-move -names="a::kGlobalInt" -new_header=%T/move-var/new_var_test.h -old_header=../move-var/var_test.h -old_cc=../move-var/var_test.cpp -new_cc=%T/move-var/new_var_test.cpp %T/move-var/var_test.cpp -- -// RUN: FileCheck -input-file=%T/move-var/var_test.h -check-prefix=CHECK-OLD-VAR-H-CASE1 %s -// RUN: FileCheck -input-file=%T/move-var/var_test.cpp -check-prefix=CHECK-OLD-VAR-CPP-CASE1 %s -// RUN: FileCheck -input-file=%T/move-var/new_var_test.h -check-prefix=CHECK-NEW-VAR-H-CASE1 %s -// RUN: FileCheck -input-file=%T/move-var/new_var_test.cpp -check-prefix=CHECK-NEW-VAR-CPP-CASE1 %s - -// CHECK-OLD-VAR-H-CASE1-NOT: extern int kGlobalInt; -// CHECK-OLD-VAR-H-CASE1: int kGlobalInt = 3; - -// CHECK-OLD-VAR-CPP-CASE1-NOT: int kGlobalInt = 1; - -// CHECK-NEW-VAR-H-CASE1: extern int kGlobalInt; -// CHECK-NEW-VAR-H-CASE1-NOT: int kGlobalInt = 3; - -// CHECK-NEW-VAR-CPP-CASE1: int kGlobalInt = 1; - - -// RUN: cp %S/Inputs/var_test* %T/move-var -// RUN: clang-move -names="a::kGlobalStr" -new_header=%T/move-var/new_var_test.h -old_header=../move-var/var_test.h -old_cc=../move-var/var_test.cpp -new_cc=%T/move-var/new_var_test.cpp %T/move-var/var_test.cpp -- -// RUN: FileCheck -input-file=%T/move-var/var_test.h -check-prefix=CHECK-OLD-VAR-H-CASE2 %s -// RUN: FileCheck -input-file=%T/move-var/var_test.cpp -check-prefix=CHECK-OLD-VAR-CPP-CASE2 %s -// RUN: FileCheck -input-file=%T/move-var/new_var_test.h -check-prefix=CHECK-NEW-VAR-H-CASE2 %s -// RUN: FileCheck -input-file=%T/move-var/new_var_test.cpp -check-prefix=CHECK-NEW-VAR-CPP-CASE2 %s - -// CHECK-OLD-VAR-H-CASE2-NOT: extern const char *const kGlobalStr; -// CHECK-OLD-VAR-H-CASE2: const char *const kGlobalStr = "Hello2"; - -// CHECK-OLD-VAR-CPP-CASE2-NOT: const char *const kGlobalStr = "Hello"; - -// CHECK-NEW-VAR-H-CASE2: extern const char *const kGlobalStr; -// CHECK-NEW-VAR-H-CASE2-NOT: const char *const kGlobalStr = "Hello2"; - -// CHECK-NEW-VAR-CPP-CASE2: const char *const kGlobalStr = "Hello"; - - -// RUN: cp %S/Inputs/var_test* %T/move-var -// RUN: clang-move -names="kEvilInt" -new_header=%T/move-var/new_var_test.h -old_header=../move-var/var_test.h -old_cc=../move-var/var_test.cpp -new_cc=%T/move-var/new_var_test.cpp %T/move-var/var_test.cpp -- -// RUN: FileCheck -input-file=%T/move-var/var_test.h -check-prefix=CHECK-OLD-VAR-H-CASE3 %s -// RUN: FileCheck -input-file=%T/move-var/new_var_test.h -check-prefix=CHECK-NEW-VAR-H-CASE3 %s - -// CHECK-OLD-VAR-H-CASE3-NOT: int kEvilInt = 2; - -// CHECK-NEW-VAR-H-CASE3: int kEvilInt = 2; Index: test/clang-tidy/check_clang_tidy.py =================================================================== --- test/clang-tidy/check_clang_tidy.py +++ test/clang-tidy/check_clang_tidy.py @@ -60,11 +60,6 @@ if len(clang_tidy_extra_args) == 0: clang_tidy_extra_args = ['--', '--std=c++11'] if extension == '.cpp' \ else ['--'] - - # Tests should not rely on STL being available, and instead provide mock - # implementations of relevant APIs. - clang_tidy_extra_args.append('-nostdinc++') - if resource_dir is not None: clang_tidy_extra_args.append('-resource-dir=%s' % resource_dir) Index: test/clang-tidy/clean-up-code.cpp =================================================================== --- test/clang-tidy/clean-up-code.cpp +++ test/clang-tidy/clean-up-code.cpp @@ -1,6 +1,4 @@ // RUN: %check_clang_tidy %s misc-unused-using-decls %t -// RUN: %check_clang_tidy %s misc-unused-using-decls %t -- -format-style=none -- -// RUN: %check_clang_tidy %s misc-unused-using-decls %t -- -format-style=llvm -- namespace a { class A {}; } namespace b { using a::A; Index: test/clang-tidy/cppcoreguidelines-no-malloc-custom.cpp =================================================================== --- test/clang-tidy/cppcoreguidelines-no-malloc-custom.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// RUN: %check_clang_tidy %s cppcoreguidelines-no-malloc %t \ -// RUN: -config='{CheckOptions: \ -// RUN: [{key: cppcoreguidelines-no-malloc.Allocations, value: "::malloc;::align_malloc;::calloc"},\ -// RUN: {key: cppcoreguidelines-no-malloc.Reallocations, value: "::realloc;::align_realloc"},\ -// RUN: {key: cppcoreguidelines-no-malloc.Deallocations, value: "::free;::align_free"}]}' \ -// RUN: -- - -using size_t = __SIZE_TYPE__; - -void *malloc(size_t size); -void *align_malloc(size_t size, unsigned short aligmnent); -void *calloc(size_t num, size_t size); -void *realloc(void *ptr, size_t size); -void *align_realloc(void *ptr, size_t size, unsigned short alignment); -void free(void *ptr); -void *align_free(void *ptr); - -void malloced_array() { - int *array0 = (int *)malloc(sizeof(int) * 20); - // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc] - - int *zeroed = (int *)calloc(20, sizeof(int)); - // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc] - - int *aligned = (int *)align_malloc(20 * sizeof(int), 16); - // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc] - - // reallocation memory, std::vector shall be used - char *realloced = (char *)realloc(array0, 50 * sizeof(int)); - // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: do not manage memory manually; consider std::vector or std::string [cppcoreguidelines-no-malloc] - - char *align_realloced = (char *)align_realloc(aligned, 50 * sizeof(int), 16); - // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: do not manage memory manually; consider std::vector or std::string [cppcoreguidelines-no-malloc] - - // freeing memory the bad way - free(realloced); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc] - - align_free(align_realloced); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc] - - // check if a call to malloc as function argument is found as well - free(malloc(20)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc] - // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc] -} - -/// newing an array is still not good, but not relevant to this checker -void newed_array() { - int *new_array = new int[10]; // OK(1) -} - -void arbitrary_call() { - // we dont want every function to raise the warning even if malloc is in the name - malloced_array(); // OK(2) - - // completly unrelated function call to malloc - newed_array(); // OK(3) -} Index: test/clang-tidy/cppcoreguidelines-no-malloc-no-functions.cpp =================================================================== --- test/clang-tidy/cppcoreguidelines-no-malloc-no-functions.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %check_clang_tidy %s cppcoreguidelines-no-malloc %t \ -// RUN: -config='{CheckOptions: \ -// RUN: [{key: cppcoreguidelines-no-malloc.Allocations, value: "::malloc"},\ -// RUN: {key: cppcoreguidelines-no-malloc.Reallocations, value: ""},\ -// RUN: {key: cppcoreguidelines-no-malloc.Deallocations, value: ""}]}' \ -// RUN: -- - -// Just ensure, the check will not crash, when no functions shall be checked. - -using size_t = __SIZE_TYPE__; - -void *malloc(size_t size); - -void malloced_array() { - int *array0 = (int *)malloc(sizeof(int) * 20); - // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc] -} Index: test/clang-tidy/cppcoreguidelines-special-member-functions-cxx-03.cpp =================================================================== --- test/clang-tidy/cppcoreguidelines-special-member-functions-cxx-03.cpp +++ test/clang-tidy/cppcoreguidelines-special-member-functions-cxx-03.cpp @@ -3,7 +3,7 @@ class DefinesDestructor { ~DefinesDestructor(); }; -// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a non-default destructor but does not define a copy constructor or a copy assignment operator [cppcoreguidelines-special-member-functions] +// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a destructor but does not define a copy constructor or a copy assignment operator [cppcoreguidelines-special-member-functions] class DefinesCopyConstructor { DefinesCopyConstructor(const DefinesCopyConstructor &); Index: test/clang-tidy/cppcoreguidelines-special-member-functions-relaxed.cpp =================================================================== --- test/clang-tidy/cppcoreguidelines-special-member-functions-relaxed.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// RUN: %check_clang_tidy %s cppcoreguidelines-special-member-functions %t -- -config="{CheckOptions: [{key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions, value: 1}, {key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor, value: 1}]}" -- - -class DefinesDestructor { - ~DefinesDestructor(); -}; -// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a non-default destructor but does not define a copy constructor or a copy assignment operator [cppcoreguidelines-special-member-functions] - -class DefinesDefaultedDestructor { - ~DefinesDefaultedDestructor() = default; -}; - -class DefinesCopyConstructor { - DefinesCopyConstructor(const DefinesCopyConstructor &); -}; -// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyConstructor' defines a copy constructor but does not define a destructor or a copy assignment operator [cppcoreguidelines-special-member-functions] - -class DefinesCopyAssignment { - DefinesCopyAssignment &operator=(const DefinesCopyAssignment &); -}; -// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyAssignment' defines a copy assignment operator but does not define a destructor or a copy constructor [cppcoreguidelines-special-member-functions] - -class DefinesMoveConstructor { - DefinesMoveConstructor(DefinesMoveConstructor &&); -}; -// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveConstructor' defines a move constructor but does not define a destructor, a copy constructor, a copy assignment operator or a move assignment operator [cppcoreguidelines-special-member-functions] - -class DefinesMoveAssignment { - DefinesMoveAssignment &operator=(DefinesMoveAssignment &&); -}; -// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveAssignment' defines a move assignment operator but does not define a destructor, a copy constructor, a copy assignment operator or a move constructor [cppcoreguidelines-special-member-functions] -class DefinesNothing { -}; - -class DefinesEverything { - DefinesEverything(const DefinesEverything &); - DefinesEverything &operator=(const DefinesEverything &); - DefinesEverything(DefinesEverything &&); - DefinesEverything &operator=(DefinesEverything &&); - ~DefinesEverything(); -}; - -class DeletesEverything { - DeletesEverything(const DeletesEverything &) = delete; - DeletesEverything &operator=(const DeletesEverything &) = delete; - DeletesEverything(DeletesEverything &&) = delete; - DeletesEverything &operator=(DeletesEverything &&) = delete; - ~DeletesEverything() = delete; -}; - -class DeletesCopyDefaultsMove { - DeletesCopyDefaultsMove(const DeletesCopyDefaultsMove &) = delete; - DeletesCopyDefaultsMove &operator=(const DeletesCopyDefaultsMove &) = delete; - DeletesCopyDefaultsMove(DeletesCopyDefaultsMove &&) = default; - DeletesCopyDefaultsMove &operator=(DeletesCopyDefaultsMove &&) = default; - ~DeletesCopyDefaultsMove() = default; -}; - -template -struct TemplateClass { - TemplateClass() = default; - TemplateClass(const TemplateClass &); - TemplateClass &operator=(const TemplateClass &); - TemplateClass(TemplateClass &&); - TemplateClass &operator=(TemplateClass &&); - ~TemplateClass(); -}; - -// Multiple instantiations of a class template will trigger multiple matches for defined special members. -// This should not cause problems. -TemplateClass InstantiationWithInt; -TemplateClass InstantiationWithDouble; Index: test/clang-tidy/cppcoreguidelines-special-member-functions.cpp =================================================================== --- test/clang-tidy/cppcoreguidelines-special-member-functions.cpp +++ test/clang-tidy/cppcoreguidelines-special-member-functions.cpp @@ -3,12 +3,7 @@ class DefinesDestructor { ~DefinesDestructor(); }; -// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions] - -class DefinesDefaultedDestructor { - ~DefinesDefaultedDestructor() = default; -}; -// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDefaultedDestructor' defines a default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions] +// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions] class DefinesCopyConstructor { DefinesCopyConstructor(const DefinesCopyConstructor &); Index: test/clang-tidy/google-explicit-constructor.cpp =================================================================== --- test/clang-tidy/google-explicit-constructor.cpp +++ test/clang-tidy/google-explicit-constructor.cpp @@ -40,8 +40,6 @@ explicit A(void *x, void *y) {} explicit operator bool() const { return true; } - operator double() const = delete; - explicit A(const A& a) {} // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: copy constructor should not be declared explicit [google-explicit-constructor] // CHECK-FIXES: {{^ }}A(const A& a) {} Index: test/clang-tidy/google-readability-casting.c =================================================================== --- test/clang-tidy/google-readability-casting.c +++ test/clang-tidy/google-readability-casting.c @@ -17,8 +17,6 @@ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant cast to the same type [google-readability-casting] // CHECK-FIXES: const char *cpc2 = cpc; char *pc = (char*)cpc; - typedef const char *Typedef1; - (Typedef1)cpc; } #endif Index: test/clang-tidy/google-readability-casting.cpp =================================================================== --- test/clang-tidy/google-readability-casting.cpp +++ test/clang-tidy/google-readability-casting.cpp @@ -15,14 +15,14 @@ typedef const char *Typedef2; Typedef1 t1; (Typedef2)t1; - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: C-style casts are discouraged; use static_cast (if needed, the cast may be redundant) [google-readability-casting] - // CHECK-FIXES: {{^}} static_cast(t1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: possibly redundant cast between typedefs of the same type [google-readability-casting] + // CHECK-FIXES: {{^}} (Typedef2)t1; (const char*)t1; - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed - // CHECK-FIXES: {{^}} static_cast(t1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: possibly redundant cast {{.*}} + // CHECK-FIXES: {{^}} (const char*)t1; (Typedef1)cpc; - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed - // CHECK-FIXES: {{^}} static_cast(cpc); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: possibly redundant cast {{.*}} + // CHECK-FIXES: {{^}} (Typedef1)cpc; (Typedef1)t1; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant cast to the same type // CHECK-FIXES: {{^}} t1; @@ -30,95 +30,64 @@ char *pc = (char*)cpc; // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use const_cast [google-readability-casting] // CHECK-FIXES: char *pc = const_cast(cpc); - typedef char Char; - Char *pChar = (Char*)pc; - // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}}; use static_cast (if needed - // CHECK-FIXES: {{^}} Char *pChar = static_cast(pc); - - (Char)*cpc; - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed - // CHECK-FIXES: {{^}} static_cast(*cpc); - - (char)*pChar; - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed - // CHECK-FIXES: {{^}} static_cast(*pChar); - - (const char*)cpv; - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast [ - // CHECK-FIXES: static_cast(cpv); char *pc2 = (char*)(cpc + 33); - // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast {{.*}} // CHECK-FIXES: char *pc2 = const_cast(cpc + 33); const char &crc = *cpc; char &rc = (char&)crc; - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}}; use const_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}}; use const_cast {{.*}} // CHECK-FIXES: char &rc = const_cast(crc); char &rc2 = (char&)*cpc; - // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast {{.*}} // CHECK-FIXES: char &rc2 = const_cast(*cpc); char ** const* const* ppcpcpc; char ****ppppc = (char****)ppcpcpc; - // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: {{.*}}; use const_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: {{.*}}; use const_cast {{.*}} // CHECK-FIXES: char ****ppppc = const_cast(ppcpcpc); char ***pppc = (char***)*(ppcpcpc); - // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: {{.*}}; use const_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: {{.*}}; use const_cast {{.*}} // CHECK-FIXES: char ***pppc = const_cast(*(ppcpcpc)); char ***pppc2 = (char***)(*ppcpcpc); - // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}}; use const_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}}; use const_cast {{.*}} // CHECK-FIXES: char ***pppc2 = const_cast(*ppcpcpc); char *pc5 = (char*)(const char*)(cpv); - // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast [ - // CHECK-MESSAGES: :[[@LINE-2]]:22: warning: {{.*}}; use static_cast [ - // CHECK-FIXES: char *pc5 = const_cast(static_cast(cpv)); + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast {{.*}} + // CHECK-MESSAGES: :[[@LINE-2]]:22: warning: {{.*}}; use reinterpret_cast {{.*}} + // CHECK-FIXES: char *pc5 = const_cast(reinterpret_cast(cpv)); int b1 = (int)b; - // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}}; use static_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}}; use static_cast {{.*}} // CHECK-FIXES: int b1 = static_cast(b); - b1 = (const int&)b; - // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [ - // CHECK-FIXES: b1 = (const int&)b; Y *pB = (Y*)pX; - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast {{.*}} Y &rB = (Y&)*pX; - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast {{.*}} const char *pc3 = (const char*)cpv; - // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}}; use static_cast [ - // CHECK-FIXES: const char *pc3 = static_cast(cpv); + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}}; use reinterpret_cast {{.*}} + // CHECK-FIXES: const char *pc3 = reinterpret_cast(cpv); char *pc4 = (char*)cpv; - // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast {{.*}} // CHECK-FIXES: char *pc4 = (char*)cpv; b1 = (int)Enum1; - // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast {{.*}} // CHECK-FIXES: b1 = static_cast(Enum1); Enum e = (Enum)b1; - // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}}; use static_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}}; use static_cast {{.*}} // CHECK-FIXES: Enum e = static_cast(b1); - e = (Enum)Enum1; - // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant cast to the same type - // CHECK-FIXES: {{^}} e = Enum1; - - e = (Enum)e; - // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant cast to the same type - // CHECK-FIXES: {{^}} e = e; - - static const int kZero = 0; - (int)kZero; - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant cast to the same type - // CHECK-FIXES: {{^}} kZero; - + // CHECK-MESSAGES-NOT: warning: int b2 = int(b); int b3 = static_cast(b); int b4 = b; @@ -130,7 +99,7 @@ template void template_function(T t, int n) { int i = (int)t; - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [ + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast {{.*}} // CHECK-FIXES: int i = (int)t; int j = (int)n; // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant cast to the same type @@ -274,15 +243,11 @@ auto s2a = (struct S)""; // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [ // CHECK-FIXES: auto s2a = static_cast(""); - auto s2b = (const S)""; - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [ - // FIXME: This should be constructor call syntax: S(""). - // CHECK-FIXES: auto s2b = static_cast(""); ConvertibleToS c; auto s3 = (const S&)c; // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use static_cast/const_cast/reinterpret_cast [ // CHECK-FIXES: auto s3 = (const S&)c; - // FIXME: This should be a static_cast. + // FIXME: This should be a static_cast // C HECK-FIXES: auto s3 = static_cast(c); auto s4 = (S)c; // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [ @@ -291,7 +256,7 @@ auto s5 = (const S&)cr; // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use static_cast/const_cast/reinterpret_cast [ // CHECK-FIXES: auto s5 = (const S&)cr; - // FIXME: This should be a static_cast. + // FIXME: This should be a static_cast // C HECK-FIXES: auto s5 = static_cast(cr); auto s6 = (S)cr; // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [ Index: test/clang-tidy/hicpp-no-assembler.cpp =================================================================== --- test/clang-tidy/hicpp-no-assembler.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %check_clang_tidy %s hicpp-no-assembler %t - -__asm__(".symver foo, bar@v"); -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: do not use inline assembler in safety-critical code [hicpp-no-assembler] - -static int s asm("spam"); -// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use inline assembler in safety-critical code [hicpp-no-assembler] - -void f() { - __asm("mov al, 2"); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use inline assembler in safety-critical code [hicpp-no-assembler] -} Index: test/clang-tidy/misc-forwarding-reference-overload.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-forwarding-reference-overload.cpp @@ -0,0 +1,139 @@ +// RUN: %check_clang_tidy %s misc-forwarding-reference-overload %t + +namespace std { +template +struct enable_if {}; + +template +struct enable_if { typedef T type; }; + +template +using enable_if_t = typename enable_if::type; + +template +struct enable_if_nice { typedef T type; }; +} + +namespace foo { +template +struct enable_if { typedef T type; }; +} + +template +constexpr bool just_true = true; + +class Test1 { +public: + template + Test1(T &&n); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and the move constructors [misc-forwarding-reference-overload] + + template + Test1(T &&n, int i = 5, ...); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and the move constructors [misc-forwarding-reference-overload] + + template ::type> + Test1(T &&n); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and the move constructors [misc-forwarding-reference-overload] + + template + Test1(T &&n, typename foo::enable_if::type i = 5, ...); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and the move constructors [misc-forwarding-reference-overload] + + Test1(const Test1 &other) {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here + + Test1(Test1 &other) {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here + + Test1(Test1 &&other) {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: note: move constructor declared here +}; + +template +class Test2 { +public: + // Two parameters without default value, can't act as copy / move constructor. + template + Test2(T &&n, V &&m, int i = 5, ...); + + // Guarded with enable_if. + template + Test2(T &&n, int i = 5, std::enable_if_t a = 5, ...); + + // Guarded with enable_if. + template ::type &> + Test2(T &&n); + + // Guarded with enable_if. + template + Test2(T &&n, typename std::enable_if>::type **a = nullptr); + + // Guarded with enable_if. + template > *&&> + Test2(T &&n, double d = 0.0); + + // Not a forwarding reference parameter. + template + Test2(const T &&n); + + // Not a forwarding reference parameter. + Test2(int &&x); + + // Two parameters without default value, can't act as copy / move constructor. + template + Test2(T &&n, int x); + + // Not a forwarding reference parameter. + template + Test2(U &&n); +}; + +// The copy and the move constructors are both disabled. +class Test3 { +public: + template + Test3(T &&n); + + template + Test3(T &&n, int I = 5, ...); + + Test3(const Test3 &rhs) = delete; + +private: + Test3(Test3 &&rhs); +}; + +// Both the copy and the (compiler generated) move constructors can be hidden. +class Test4 { +public: + template + Test4(T &&n); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and the move constructors [misc-forwarding-reference-overload] + + Test4(const Test4 &rhs); + // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here +}; + +// Only the (compiler generated) copy constructor can be hidden. +class Test5 { +public: + template + Test5(T &&n); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy constructor [misc-forwarding-reference-overload] + + Test5(Test5 &&rhs) = delete; +}; + +// Only the move constructor can be hidden. +class Test6 { +public: + template + Test6(T &&n); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the move constructor [misc-forwarding-reference-overload] + + Test6(Test6 &&rhs); + // CHECK-MESSAGES: :[[@LINE-1]]:3: note: move constructor declared here +private: + Test6(const Test6 &rhs); +}; \ No newline at end of file Index: test/clang-tidy/misc-noexcept-move-constructor.cpp =================================================================== --- test/clang-tidy/misc-noexcept-move-constructor.cpp +++ test/clang-tidy/misc-noexcept-move-constructor.cpp @@ -42,13 +42,3 @@ OK3(OK3 &&) noexcept(false) {} OK3 &operator=(OK3 &&) = delete; }; - -struct OK4 { - OK4(OK4 &&) noexcept = default; - OK4 &operator=(OK4 &&) noexcept = default; -}; - -struct OK5 { - OK5(OK5 &&) noexcept(true) = default; - OK5 &operator=(OK5 &&) noexcept(true) = default; -}; Index: test/clang-tidy/misc-use-after-move.cpp =================================================================== --- test/clang-tidy/misc-use-after-move.cpp +++ test/clang-tidy/misc-use-after-move.cpp @@ -282,7 +282,7 @@ S s{std::move(a)}; a.foo(); // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here + // CHECK-MESSAGES: [[@LINE-3]]:6: note: move occurred here } void lambdas() { @@ -397,21 +397,6 @@ } template void movedTypeIsDependentType(); -// We handle the case correctly where the move consists of an implicit call -// to a conversion operator. -void implicitConversionOperator() { - struct Convertible { - operator A() && { return A(); } - }; - void takeA(A a); - - Convertible convertible; - takeA(std::move(convertible)); - convertible; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'convertible' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:9: note: move occurred here -} - // Using decltype on an expression is not a use. void decltypeIsNotUse() { A a; Index: test/clang-tidy/modernize-use-nullptr.cpp =================================================================== --- test/clang-tidy/modernize-use-nullptr.cpp +++ test/clang-tidy/modernize-use-nullptr.cpp @@ -228,36 +228,3 @@ void test_default_argument() { D(nullptr); } - -// Test on two neighbour CXXDefaultArgExprs nodes. -typedef unsigned long long uint64; -struct ZZ { - explicit ZZ(uint64, const uint64* = NULL) {} -// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: use nullptr -// CHECK-FIXES: explicit ZZ(uint64, const uint64* = nullptr) {} - operator bool() { return true; } -}; - -uint64 Hash(uint64 seed = 0) { return 0; } - -void f() { - bool a; - a = ZZ(Hash()); -} - -// Test on ignoring substituted template types. -template -class TemplateClass { - public: - explicit TemplateClass(int a, T default_value = 0) {} - - void h(T *default_value = 0) {} - - void f(int* p = 0) {} -// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use nullptr -// CHECK-FIXES: void f(int* p = nullptr) {} -}; - -void IgnoreSubstTemplateType() { - TemplateClass a(1); -} Index: test/clang-tidy/modernize-use-override.cpp =================================================================== --- test/clang-tidy/modernize-use-override.cpp +++ test/clang-tidy/modernize-use-override.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s modernize-use-override %t -- -- -std=c++11 -fexceptions +// RUN: %check_clang_tidy %s modernize-use-override %t #define ABSTRACT = 0 @@ -288,17 +288,3 @@ }; template <> void MembersOfSpecializations<3>::a() {} void ff() { MembersOfSpecializations<3>().a(); }; - -// In case try statement is used as a method body, -// make sure that override fix is placed before try keyword. -struct TryStmtAsBody : public Base { - void a() try - // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: annotate this - // CHECK-FIXES: {{^}} void a() override try - { b(); } catch(...) { c(); } - - virtual void d() try - // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using - // CHECK-FIXES: {{^}} void d() override try - { e(); } catch(...) { f(); } -}; Index: test/clang-tidy/readability-braces-around-statements-format.cpp =================================================================== --- test/clang-tidy/readability-braces-around-statements-format.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// RUN: %check_clang_tidy %s readability-braces-around-statements %t -- -format-style="{IndentWidth: 3}" -- - -void do_something(const char *) {} - -bool cond(const char *) { - return false; -} - -void test() { - if (cond("if0") /*comment*/) do_something("same-line"); - // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: statement should be inside braces - // CHECK-FIXES: {{^}} if (cond("if0") /*comment*/) {{{$}} - // CHECK-FIXES-NEXT: {{^}} do_something("same-line");{{$}} - // CHECK-FIXES-NEXT: {{^}} }{{$}} - - if (1) while (2) if (3) for (;;) do ; while(false) /**/;/**/ - // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: statement should be inside braces - // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: statement should be inside braces - // CHECK-MESSAGES: :[[@LINE-3]]:26: warning: statement should be inside braces - // CHECK-MESSAGES: :[[@LINE-4]]:35: warning: statement should be inside braces - // CHECK-MESSAGES: :[[@LINE-5]]:38: warning: statement should be inside braces - // CHECK-FIXES: {{^}} if (1) {{{$}} - // CHECK-FIXES-NEXT: {{^}} while (2) { - // CHECK-FIXES-NEXT: {{^}} if (3) { - // CHECK-FIXES-NEXT: {{^}} for (;;) { - // CHECK-FIXES-NEXT: {{^}} do { - // CHECK-FIXES-NEXT: {{^}} ; - // CHECK-FIXES-NEXT: {{^}} } while (false) /**/; /**/ - // CHECK-FIXES-NEXT: {{^}} } - // CHECK-FIXES-NEXT: {{^}} } - // CHECK-FIXES-NEXT: {{^}} } - // CHECK-FIXES-NEXT: {{^}} } -} Index: test/clang-tidy/readability-container-size-empty.cpp =================================================================== --- test/clang-tidy/readability-container-size-empty.cpp +++ test/clang-tidy/readability-container-size-empty.cpp @@ -168,10 +168,6 @@ ; // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used // CHECK-FIXES: {{^ }}if (vect3->empty()){{$}} - if ((*vect3).size() == 0) - ; - // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used - // CHECK-FIXES: {{^ }}if ((*vect3).empty()){{$}} delete vect3; Index: test/clang-tidy/readability-function-size.cpp =================================================================== --- test/clang-tidy/readability-function-size.cpp +++ test/clang-tidy/readability-function-size.cpp @@ -1,6 +1,4 @@ -// RUN: %check_clang_tidy %s readability-function-size %t -- -config='{CheckOptions: [{key: readability-function-size.LineThreshold, value: 0}, {key: readability-function-size.StatementThreshold, value: 0}, {key: readability-function-size.BranchThreshold, value: 0}, {key: readability-function-size.ParameterThreshold, value: 5}]}' -- -std=c++11 - -// Bad formatting is intentional, don't run clang-format over the whole file! +// RUN: %check_clang_tidy %s readability-function-size %t -- -config='{CheckOptions: [{key: readability-function-size.LineThreshold, value: 0}, {key: readability-function-size.StatementThreshold, value: 0}, {key: readability-function-size.BranchThreshold, value: 0}]}' -- -std=c++11 void foo1() { } @@ -39,11 +37,6 @@ // CHECK-MESSAGES: :[[@LINE-4]]:25: note: 1 lines including whitespace and comments (threshold 0) // CHECK-MESSAGES: :[[@LINE-5]]:25: note: 1 statements (threshold 0) -void foo7(int p1, int p2, int p3, int p4, int p5, int p6) {;} -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo7' exceeds recommended size/complexity -// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 1 statements (threshold 0) -// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 6 parameters (threshold 5) - void bar1() { [](){;;;;;;;;;;;if(1){}}(); Index: test/clang-tidy/readability-misleading-indentation.cpp =================================================================== --- test/clang-tidy/readability-misleading-indentation.cpp +++ test/clang-tidy/readability-misleading-indentation.cpp @@ -76,36 +76,5 @@ { } - if(cond1) { - } - else if (cond2) { - } - else { - } - - if(cond1) { - } - else if (cond2) { - } - else { - } - // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: different indentation for 'if' and corresponding 'else' [readability-misleading-indentation] - - if (cond1) { - if (cond1) { - } - else if (cond2) { - } - else { - } - if (cond1) { - } else if (cond2) { - } else if (!cond2) { - } else { - } - } - else if (cond2) { - } - BLOCK } Index: test/clang-tidy/readability-redundant-declaration.cpp =================================================================== --- test/clang-tidy/readability-redundant-declaration.cpp +++ test/clang-tidy/readability-redundant-declaration.cpp @@ -1,9 +1,9 @@ // RUN: %check_clang_tidy %s readability-redundant-declaration %t extern int Xyz; -extern int Xyz; // Xyz +extern int Xyz; // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'Xyz' declaration [readability-redundant-declaration] -// CHECK-FIXES: {{^}}// Xyz{{$}} +// CHECK-FIXES: {{^}}{{$}} int Xyz = 123; extern int A; @@ -12,25 +12,19 @@ // CHECK-FIXES: {{^}}extern int A, B;{{$}} extern int Buf[10]; -extern int Buf[10]; // Buf[10] +extern int Buf[10]; // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'Buf' declaration -// CHECK-FIXES: {{^}}// Buf[10]{{$}} +// CHECK-FIXES: {{^}}{{$}} static int f(); -static int f(); // f +static int f(); // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'f' declaration -// CHECK-FIXES: {{^}}// f{{$}} +// CHECK-FIXES: {{^}}{{$}} static int f() {} // Original check crashed for the code below. namespace std { -typedef decltype(sizeof(0)) size_t; + typedef decltype(sizeof(0)) size_t; } -void *operator new(std::size_t) __attribute__((__externally_visible__)); -void *operator new[](std::size_t) __attribute__((__externally_visible__)); - -// Don't warn about static member definition. -struct C { - static int I; -}; -int C::I; +void* operator new(std::size_t) __attribute__((__externally_visible__)); +void* operator new[](std::size_t) __attribute__((__externally_visible__)); Index: test/clang-tidy/safety-no-assembler.cpp =================================================================== --- /dev/null +++ test/clang-tidy/safety-no-assembler.cpp @@ -0,0 +1,13 @@ +// RUN: %check_clang_tidy %s safety-no-assembler %t + +__asm__(".symver foo, bar@v"); +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: do not use inline assembler in safety-critical code [safety-no-assembler] + +static int s asm("spam"); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use inline assembler in safety-critical code [safety-no-assembler] + +void f() { + __asm("mov al, 2"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use inline assembler in safety-critical code [safety-no-assembler] +} + Index: test/clangd/fixits.test =================================================================== --- test/clangd/fixits.test +++ /dev/null @@ -1,22 +0,0 @@ -# RUN: clangd -run-synchronously < %s | FileCheck %s -# It is absolutely vital that this file has CRLF line endings. -# -Content-Length: 125 - -{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} -# -Content-Length: 180 - -{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}} -# -# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}} -# -Content-Length: 746 - - {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}}} -# -# CHECK: {"jsonrpc":"2.0","id":2, "result": [{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}] -# -Content-Length: 44 - -{"jsonrpc":"2.0","id":3,"method":"shutdown"} Index: test/clangd/formatting.test =================================================================== --- test/clangd/formatting.test +++ test/clangd/formatting.test @@ -4,13 +4,12 @@ Content-Length: 125 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} -# CHECK: Content-Length: 332 +# CHECK: Content-Length: 294 # CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{ # CHECK: "textDocumentSync": 1, # CHECK: "documentFormattingProvider": true, # CHECK: "documentRangeFormattingProvider": true, -# CHECK: "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]}, -# CHECK: "codeActionProvider": true +# CHECK: "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]} # CHECK: }}} # Content-Length: 193 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 @@ -6,20 +6,9 @@ - ContextType: Namespace ContextName: b FilePath: foo.h +LineNumber: 1 Type: Class -Seen: 1 -Used: 0 ---- -Name: foo_bar -Contexts: - - ContextType: Namespace - ContextName: a - - ContextType: Namespace - ContextName: b -FilePath: foobar.h -Type: Class -Seen: 0 -Used: 0 +NumOccurrences: 1 --- Name: bar Contexts: @@ -28,9 +17,9 @@ - ContextType: Namespace ContextName: b FilePath: ../include/bar.h +LineNumber: 1 Type: Class -Seen: 1 -Used: 0 +NumOccurrences: 1 --- Name: bar Contexts: @@ -39,9 +28,9 @@ - ContextType: Namespace ContextName: b FilePath: ../include/bar.h +LineNumber: 2 Type: Class -Seen: 3 -Used: 0 +NumOccurrences: 3 --- Name: bar Contexts: @@ -50,22 +39,22 @@ - ContextType: Namespace ContextName: b FilePath: ../include/zbar.h +LineNumber: 1 Type: Class -Seen: 3 -Used: 0 +NumOccurrences: 3 --- Name: b Contexts: FilePath: var.h +LineNumber: 1 Type: Variable -Seen: 1 -Used: 0 +NumOccurrences: 1 --- Name: bar Contexts: - ContextType: Namespace ContextName: c FilePath: test/include-fixer/baz.h +LineNumber: 1 Type: Class -Seen: 1 -Used: 0 +NumOccurrences: 1 Index: test/include-fixer/Inputs/merge/a.yaml =================================================================== --- test/include-fixer/Inputs/merge/a.yaml +++ test/include-fixer/Inputs/merge/a.yaml @@ -4,9 +4,9 @@ - ContextType: Namespace ContextName: a FilePath: foo.h +LineNumber: 1 Type: Class -Seen: 1 -Used: 1 +NumOccurrences: 1 ... --- Name: bar @@ -14,7 +14,7 @@ - ContextType: Namespace ContextName: a FilePath: ../include/bar.h +LineNumber: 1 Type: Class -Seen: 1 -Used: 2 +NumOccurrences: 1 ... Index: test/include-fixer/Inputs/merge/b.yaml =================================================================== --- test/include-fixer/Inputs/merge/b.yaml +++ test/include-fixer/Inputs/merge/b.yaml @@ -4,9 +4,9 @@ - ContextType: Namespace ContextName: a FilePath: foo.h +LineNumber: 1 Type: Class -Seen: 1 -Used: 2 +NumOccurrences: 1 ... --- Name: bar @@ -14,7 +14,7 @@ - ContextType: Namespace ContextName: a FilePath: ../include/barbar.h +LineNumber: 1 Type: Class -Seen: 1 -Used: 0 +NumOccurrences: 1 ... Index: test/include-fixer/merge.test =================================================================== --- test/include-fixer/merge.test +++ test/include-fixer/merge.test @@ -7,9 +7,9 @@ - ContextType: Namespace ContextName: a FilePath: ../include/bar.h +LineNumber: 1 Type: Class -Seen: 1 -Used: 1 +NumOccurrences: 1 ... --- Name: bar @@ -17,9 +17,9 @@ - ContextType: Namespace ContextName: a FilePath: ../include/barbar.h +LineNumber: 1 Type: Class -Seen: 1 -Used: 0 +NumOccurrences: 1 ... --- Name: foo @@ -27,7 +27,7 @@ - ContextType: Namespace ContextName: a FilePath: foo.h +LineNumber: 1 Type: Class -Seen: 2 -Used: 2 +NumOccurrences: 2 ... Index: test/include-fixer/yaml_fuzzy.cpp =================================================================== --- test/include-fixer/yaml_fuzzy.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: sed -e 's#//.*$##' %s > %t.cpp -// RUN: clang-include-fixer -db=fuzzyYaml -input=%p/Inputs/fake_yaml_db.yaml %t.cpp -- -// RUN: FileCheck %s -input-file=%t.cpp - -// include-fixer will add the include, but doesn't complete the symbol. -// CHECK: #include "foobar.h" -// CHECK: fba f; - -b::a::fba f; Index: unittests/CMakeLists.txt =================================================================== --- unittests/CMakeLists.txt +++ unittests/CMakeLists.txt @@ -10,5 +10,4 @@ add_subdirectory(clang-move) add_subdirectory(clang-query) add_subdirectory(clang-tidy) -add_subdirectory(clang-rename) add_subdirectory(include-fixer) Index: unittests/change-namespace/ChangeNamespaceTests.cpp =================================================================== --- unittests/change-namespace/ChangeNamespaceTests.cpp +++ unittests/change-namespace/ChangeNamespaceTests.cpp @@ -38,8 +38,7 @@ std::map FileToReplacements; change_namespace::ChangeNamespaceTool NamespaceTool( - OldNamespace, NewNamespace, FilePattern, - /*WhiteListedSymbolPatterns*/ {}, &FileToReplacements); + OldNamespace, NewNamespace, FilePattern, &FileToReplacements); ast_matchers::MatchFinder Finder; NamespaceTool.registerMatchers(&Finder); std::unique_ptr Factory = @@ -242,7 +241,7 @@ "} // namespace na\n" "namespace x {\n" "namespace y {\n" - "class X { na::A a; z::Z zz; T t; };\n" + "class X { ::na::A a; z::Z zz; T t; };\n" "} // namespace y\n" "} // namespace x\n"; EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); @@ -296,8 +295,8 @@ "namespace y {\n" "class C_X {\n" "public:\n" - " na::C_A a;\n" - " na::nc::C_C c;\n" + " ::na::C_A a;\n" + " ::na::nc::C_C c;\n" "};\n" "class C_Y {\n" " C_X x;\n" @@ -339,9 +338,9 @@ "namespace x {\n" "namespace y {\n" "void f() {\n" - " na::B b;\n" - " na::B b_c;\n" - " na::Two two;\n" + " ::na::B<::na::A> b;\n" + " ::na::B<::na::nc::C> b_c;\n" + " ::na::Two<::na::A, ::na::nc::C> two;\n" "}\n" "} // namespace y\n" "} // namespace x\n"; @@ -368,38 +367,8 @@ "namespace y {\n" "\n" "class A {\n" - " na::nb::FWD *fwd;\n" - "};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, InsertForwardDeclsProperly) { - std::string Code = "namespace na {\n" - "namespace nb {\n" - "\n" - "class FWD;\n" - "class FWD2;\n" - "class A {\n" - " FWD *fwd;\n" - "};\n" - "\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "namespace nb {\n" - "class FWD;\n" - "class FWD2;\n" - "} // namespace nb\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "\n" - "class A {\n" - " na::nb::FWD *fwd;\n" + " ::na::nb::FWD *fwd;\n" "};\n" - "\n" "} // namespace y\n" "} // namespace x\n"; EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); @@ -426,7 +395,7 @@ "namespace y {\n" "\n" "class A {\n" - " na::nb::FWD *fwd;\n" + " ::na::nb::FWD *fwd;\n" "};\n" "template class TEMP {};\n" "} // namespace y\n" @@ -481,8 +450,8 @@ "namespace x {\n" "namespace y {\n" "void fwd();\n" - "void f(na::C_A ca, na::nc::C_C cc) {\n" - " na::C_A ca_1 = ca;\n" + "void f(::na::C_A ca, ::na::nc::C_C cc) {\n" + " ::na::C_A ca_1 = ca;\n" "}\n" "} // namespace y\n" "} // namespace x\n"; @@ -521,9 +490,9 @@ "namespace x {\n" "namespace y {\n" "using ::na::nc::SAME;\n" - "using YO = na::nd::SAME;\n" - "typedef na::nd::SAME IDENTICAL;\n" - "void f(na::nd::SAME Same) {}\n" + "using YO = ::na::nd::SAME;\n" + "typedef ::na::nd::SAME IDENTICAL;\n" + "void f(::na::nd::SAME Same) {}\n" "} // namespace y\n" "} // namespace x\n"; EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); @@ -549,9 +518,9 @@ "} // namespace na\n" "namespace x {\n" "namespace y {\n" - "class D : public na::Base {\n" + "class D : public ::na::Base {\n" "public:\n" - " using AA = na::A; using B = na::Base;\n" + " using AA = ::na::A; using B = ::na::Base;\n" " using Base::m; using Base::Base;\n" "};" "} // namespace y\n" @@ -596,11 +565,11 @@ "namespace x {\n" "namespace y {\n" "class C_X {\n" - " na::C_A na;\n" - " na::C_A::Nested nested;\n" + " ::na::C_A na;\n" + " ::na::C_A::Nested nested;\n" " void f() {\n" - " na::C_A::Nested::nestedFunc();\n" - " int X = na::C_A::Nested::NestedX;\n" + " ::na::C_A::Nested::nestedFunc();\n" + " int X = ::na::C_A::Nested::NestedX;\n" " }\n" "};\n" "} // namespace y\n" @@ -638,8 +607,8 @@ "} // namespace na\n" "namespace x {\n" "namespace y {\n" - "void f() { na::a_f(); na::static_f(); na::A::f(); }\n" - "void g() { f(); na::A::g(); }\n" + "void f() { ::na::a_f(); ::na::static_f(); ::na::A::f(); }\n" + "void g() { f(); ::na::A::g(); }\n" "} // namespace y\n" "} // namespace x\n"; EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); @@ -675,8 +644,8 @@ "namespace x {\n" "namespace y {\n" "bool f() {\n" - " na::A x, y;\n" - " auto f = na::operator<;\n" + " ::na::A x, y;\n" + " auto f = ::na::operator<;\n" // FIXME: function calls to overloaded operators are not fixed now even if // they are referenced by qualified names. " return (x == y) && (x < y) && (operator<(x,y));\n" @@ -715,9 +684,9 @@ "namespace x {\n" "namespace y {\n" "void f() {\n" - " auto *ref1 = na::A::f;\n" - " auto *ref2 = na::a_f;\n" - " auto *ref3 = na::s_f;\n" + " auto *ref1 = ::na::A::f;\n" + " auto *ref2 = ::na::a_f;\n" + " auto *ref3 = ::na::s_f;\n" "}\n" "} // namespace y\n" "} // namespace x\n"; @@ -749,9 +718,9 @@ "namespace y {\n" "int GlobB;\n" "void f() {\n" - " int a = na::GlobA;\n" - " int b = na::GlobAStatic;\n" - " int c = na::nc::GlobC;\n" + " int a = ::na::GlobA;\n" + " int b = ::na::GlobAStatic;\n" + " int c = ::na::nc::GlobC;\n" "}\n" "} // namespace y\n" "} // namespace x\n"; @@ -786,7 +755,7 @@ "namespace x {\n" "namespace y {\n" "void f() {\n" - " int a = na::A::A1; int b = na::A::A2;\n" + " int a = ::na::A::A1; int b = ::na::A::A2;\n" "}\n" "} // namespace y\n" "} // namespace x\n"; @@ -901,7 +870,7 @@ "}\n" "namespace glob2 { class Glob2 {}; }\n" "namespace gl = glob;\n" - "namespace gl2 = glob2;\n" + "namespace gl2 = ::glob2;\n" "namespace na {\n" "namespace nb {\n" "void f() { gl::Glob g; gl2::Glob2 g2; }\n" @@ -914,7 +883,7 @@ "}\n" "namespace glob2 { class Glob2 {}; }\n" "namespace gl = glob;\n" - "namespace gl2 = glob2;\n" + "namespace gl2 = ::glob2;\n" "\n" "namespace x {\n" "namespace y {\n" @@ -1227,7 +1196,7 @@ "} // na\n" "namespace x {\n" "namespace y {\n" - "void d() { nx::f(); }\n" + "void d() { ::nx::f(); }\n" "} // namespace y\n" "} // namespace x\n"; EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); @@ -1278,7 +1247,7 @@ "} // c\n" "namespace x {\n" "namespace y {\n" - "void d() { f(); nx::g(); }\n" + "void d() { f(); ::nx::g(); }\n" "} // namespace y\n" "} // namespace x\n" "} // b\n" @@ -1521,7 +1490,7 @@ " A() : X(0) {}\n" " A(int i);\n" "};\n" - "A::A(int i) : X(i) { nx::ny::X x(1);}\n" + "A::A(int i) : X(i) { ::nx::ny::X x(1);}\n" "} // namespace y\n" "} // namespace x\n"; EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); @@ -1595,9 +1564,9 @@ "public:\n" " ::Glob glob_1;\n" " Glob glob_2;\n" - " na::C_A a_1;\n" + " ::na::C_A a_1;\n" " ::na::C_A a_2;\n" - " na::nc::C_C c;\n" + " ::na::nc::C_C c;\n" "};\n" "} // namespace y\n" "} // namespace x\n"; @@ -1731,47 +1700,6 @@ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); } -TEST_F(ChangeNamespaceTest, SymbolConflictWithNewNamespace) { - OldNamespace = "nx"; - NewNamespace = "ny::na::nc"; - std::string Code = "namespace na {\n" - "class A {};\n" - "namespace nb {\n" - "class B {};\n" - "} // namespace nb\n" - "} // namespace na\n" - "namespace ny {\n" - "class Y {};\n" - "}\n" - "namespace nx {\n" - "class X {\n" - " na::A a; na::nb::B b;\n" - " ny::Y y;" - "};\n" - "} // namespace nx\n"; - std::string Expected = "namespace na {\n" - "class A {};\n" - "namespace nb {\n" - "class B {};\n" - "} // namespace nb\n" - "} // namespace na\n" - "namespace ny {\n" - "class Y {};\n" - "}\n" - "\n" - "namespace ny {\n" - "namespace na {\n" - "namespace nc {\n" - "class X {\n" - " ::na::A a; ::na::nb::B b;\n" - " Y y;\n" - "};\n" - "} // namespace nc\n" - "} // namespace na\n" - "} // namespace ny\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - TEST_F(ChangeNamespaceTest, ShortenNamespaceSpecifier) { OldNamespace = "nx"; NewNamespace = "ny::na"; @@ -1889,9 +1817,9 @@ "void f() {\n" " Glob g1 = Glob::G1;\n" " Glob g2 = G2;\n" - " na::X x1 = na::X::X1;\n" - " na::Y y1 = na::Y::Y1;\n" - " na::Y y2 = na::Y::Y2;\n" + " ::na::X x1 = ::na::X::X1;\n" + " ::na::Y y1 = ::na::Y::Y1;\n" + " ::na::Y y2 = ::na::Y::Y2;\n" "}\n" "} // namespace y\n" "} // namespace x\n"; @@ -1924,7 +1852,7 @@ " ns::X x1 = ns::X::X1;\n" " ns::Y y1 = ns::Y::Y1;\n" // FIXME: this is redundant - " ns::Y y2 = ns::Y::Y2;\n" + " ns::Y y2 = ::ns::Y::Y2;\n" "}\n" "} // namespace y\n" "} // namespace x\n"; @@ -2038,54 +1966,8 @@ "namespace x {\n" "namespace y {\n" "void f() {\n" - " na::X::E e = na::X::E1;\n" - " na::X::E ee = na::X::E::E1;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, TypeAsTemplateParameter) { - std::string Code = "namespace na {\n" - "struct X {};\n" - "namespace nb {\n" - "template \n" - "void TempTemp(const TT& t) {\n" - " TT tmp;\n" - "}\n" - "template \n" - "void Temp(const T& t) {\n" - " T tmp = t;\n" - " TempTemp(tmp);\n" - " TempTemp(t);\n" - "}\n" - "void f() {\n" - " X x;\n" - " Temp(x);\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "struct X {};\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "template \n" - "void TempTemp(const TT& t) {\n" - " TT tmp;\n" - "}\n" - "template \n" - "void Temp(const T& t) {\n" - " T tmp = t;\n" - " TempTemp(tmp);\n" - " TempTemp(t);\n" - "}\n" - "void f() {\n" - " na::X x;\n" - " Temp(x);\n" + " ::na::X::E e = ::na::X::E1;\n" + " ::na::X::E ee = ::na::X::E::E1;\n" "}\n" "} // namespace y\n" "} // namespace x\n"; Index: unittests/clang-move/ClangMoveTests.cpp =================================================================== --- unittests/clang-move/ClangMoveTests.cpp +++ unittests/clang-move/ClangMoveTests.cpp @@ -539,8 +539,6 @@ "enum class E2 { Red };\n" "typedef int Int2;\n" "using Int = int;\n" - "extern int kGlobalInt;\n" - "extern const char* const kGlobalStr;\n" "} // namespace b\n" "} // namespace a\n"; const char TestCode[] = "#include \"foo.h\"\n"; @@ -555,8 +553,7 @@ {"A", "Class"}, {"B", "Class"}, {"a::Move1", "Class"}, {"a::f1", "Function"}, {"a::f2", "Function"}, {"a::b::Move1", "Class"}, {"a::b::f", "Function"}, {"a::b::E1", "Enum"}, {"a::b::E2", "Enum"}, - {"a::b::Int2", "TypeAlias"}, {"a::b::Int", "TypeAlias"}, - {"a::b::kGlobalInt", "Variable"}, {"a::b::kGlobalStr", "Variable"}}; + {"a::b::Int2", "TypeAlias"}, {"a::b::Int", "TypeAlias"} }; runClangMoveOnCode(Spec, TestHeader, TestCode, &Reporter); std::set Results; for (const auto& DelPair : Reporter.getDeclarationList()) Index: unittests/clang-rename/CMakeLists.txt =================================================================== --- unittests/clang-rename/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -set(LLVM_LINK_COMPONENTS - support - ) - -get_filename_component(CLANG_RENAME_SOURCE_DIR - ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-rename REALPATH) -include_directories( - ${CLANG_RENAME_SOURCE_DIR} - ) - -# We'd like clang/unittests/Tooling/RewriterTestContext.h in the test. -include_directories(${CLANG_SOURCE_DIR}) - -add_extra_unittest(ClangRenameTests - ClangRenameTests.cpp - ) - -target_link_libraries(ClangRenameTests - clangAST - clangASTMatchers - clangBasic - clangFormat - clangFrontend - clangRename - clangRewrite - clangTooling - clangToolingCore - ) Index: unittests/clang-rename/ClangRenameTests.cpp =================================================================== --- unittests/clang-rename/ClangRenameTests.cpp +++ /dev/null @@ -1,153 +0,0 @@ -//===-- ClangRenameTests.cpp - clang-rename unit tests --------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "RenamingAction.h" -#include "USRFindingAction.h" -#include "unittests/Tooling/RewriterTestContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/FileSystemOptions.h" -#include "clang/Basic/VirtualFileSystem.h" -#include "clang/Format/Format.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/PCHContainerOperations.h" -#include "clang/Tooling/Refactoring.h" -#include "clang/Tooling/Tooling.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/MemoryBuffer.h" -#include "gtest/gtest.h" -#include -#include -#include - -namespace clang { -namespace clang_rename { -namespace { - -struct Case { - std::string Before; - std::string After; -}; - -class ClangRenameTest : public testing::Test, - public testing::WithParamInterface { -protected: - void AppendToHeader(StringRef Code) { - HeaderContent += Code.str(); - } - - std::string runClangRenameOnCode(llvm::StringRef Code, - llvm::StringRef OldName, - llvm::StringRef NewName) { - std::string NewCode; - llvm::raw_string_ostream(NewCode) << llvm::format( - "#include \"%s\"\n%s", HeaderName.c_str(), Code.str().c_str()); - tooling::FileContentMappings FileContents = {{HeaderName, HeaderContent}, - {CCName, NewCode}}; - clang::RewriterTestContext Context; - Context.createInMemoryFile(HeaderName, HeaderContent); - clang::FileID InputFileID = Context.createInMemoryFile(CCName, NewCode); - - rename::USRFindingAction FindingAction({}, {OldName}); - std::unique_ptr USRFindingActionFactory = - tooling::newFrontendActionFactory(&FindingAction); - - if (!tooling::runToolOnCodeWithArgs( - USRFindingActionFactory->create(), NewCode, {"-std=c++11"}, CCName, - "clang-rename", std::make_shared(), - FileContents)) - return ""; - - const std::vector> &USRList = - FindingAction.getUSRList(); - const std::vector &PrevNames = FindingAction.getUSRSpellings(); - std::vector NewNames = {NewName}; - std::map FileToReplacements; - rename::RenamingAction RenameAction(NewNames, PrevNames, USRList, - FileToReplacements); - auto RenameActionFactory = tooling::newFrontendActionFactory(&RenameAction); - if (!tooling::runToolOnCodeWithArgs( - RenameActionFactory->create(), NewCode, {"-std=c++11"}, CCName, - "clang-rename", std::make_shared(), - FileContents)) - return ""; - - formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite, "llvm"); - return Context.getRewrittenText(InputFileID); - } - - void CompareSnippets(StringRef Expected, StringRef Actual) { - std::string ExpectedCode; - llvm::raw_string_ostream(ExpectedCode) << llvm::format( - "#include \"%s\"\n%s", HeaderName.c_str(), Expected.str().c_str()); - EXPECT_EQ(format(ExpectedCode), format(Actual)); - } - - std::string format(llvm::StringRef Code) { - tooling::Replacements Replaces = format::reformat( - format::getLLVMStyle(), Code, {tooling::Range(0, Code.size())}); - auto ChangedCode = tooling::applyAllReplacements(Code, Replaces); - EXPECT_TRUE(static_cast(ChangedCode)); - if (!ChangedCode) { - llvm::errs() << llvm::toString(ChangedCode.takeError()); - return ""; - } - return *ChangedCode; - } - - std::string HeaderContent; - std::string HeaderName = "header.h"; - std::string CCName = "input.cc"; -}; - -class RenameClassTest : public ClangRenameTest { - public: - RenameClassTest() { - AppendToHeader("\nclass Foo {};\n"); - } -}; - -INSTANTIATE_TEST_CASE_P( - RenameTests, RenameClassTest, - testing::ValuesIn(std::vector({ - {"Foo f;", "Bar f;"}, - {"void f(Foo f) {}", "void f(Bar f) {}"}, - {"void f(Foo *f) {}", "void f(Bar *f) {}"}, - {"Foo f() { return Foo(); }", "Bar f() { return Bar(); }"}, - }))); - -TEST_P(RenameClassTest, RenameClasses) { - auto Param = GetParam(); - std::string OldName = "Foo"; - std::string NewName = "Bar"; - std::string Actual = runClangRenameOnCode(Param.Before, OldName, NewName); - CompareSnippets(Param.After, Actual); -} - -class RenameFunctionTest : public ClangRenameTest {}; - -INSTANTIATE_TEST_CASE_P( - RenameTests, RenameFunctionTest, - testing::ValuesIn(std::vector({ - {"void func1() {}", "void func2() {}"}, - }))); - -TEST_P(RenameFunctionTest, RenameFunctions) { - auto Param = GetParam(); - std::string OldName = "func1"; - std::string NewName = "func2"; - std::string Actual = runClangRenameOnCode(Param.Before, OldName, NewName); - CompareSnippets(Param.After, Actual); -} - -} // anonymous namespace -} // namespace clang_rename -} // namesdpace clang Index: unittests/include-fixer/CMakeLists.txt =================================================================== --- unittests/include-fixer/CMakeLists.txt +++ unittests/include-fixer/CMakeLists.txt @@ -13,7 +13,6 @@ add_extra_unittest(IncludeFixerTests IncludeFixerTest.cpp - FuzzySymbolIndexTests.cpp ) target_link_libraries(IncludeFixerTests Index: unittests/include-fixer/FuzzySymbolIndexTests.cpp =================================================================== --- unittests/include-fixer/FuzzySymbolIndexTests.cpp +++ /dev/null @@ -1,61 +0,0 @@ -//===-- FuzzySymbolIndexTests.cpp - Fuzzy symbol index unit tests ---------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "FuzzySymbolIndex.h" -#include "gmock/gmock.h" -#include "llvm/Support/Regex.h" -#include "gtest/gtest.h" - -using testing::ElementsAre; -using testing::Not; - -namespace clang { -namespace include_fixer { -namespace { - -TEST(FuzzySymbolIndexTest, Tokenize) { - EXPECT_THAT(FuzzySymbolIndex::tokenize("URLHandlerCallback"), - ElementsAre("url", "handler", "callback")); - EXPECT_THAT(FuzzySymbolIndex::tokenize("snake_case11"), - ElementsAre("snake", "case", "11")); - EXPECT_THAT(FuzzySymbolIndex::tokenize("__$42!!BOB\nbob"), - ElementsAre("42", "bob", "bob")); -} - -MATCHER_P(MatchesSymbol, Identifier, "") { - llvm::Regex Pattern("^" + arg); - std::string err; - if (!Pattern.isValid(err)) { - *result_listener << "invalid regex: " << err; - return false; - } - auto Tokens = FuzzySymbolIndex::tokenize(Identifier); - std::string Target = llvm::join(Tokens.begin(), Tokens.end(), " "); - *result_listener << "matching against '" << Target << "'"; - return llvm::Regex("^" + arg).match(Target); -} - -TEST(FuzzySymbolIndexTest, QueryRegexp) { - auto QueryRegexp = [](const std::string &query) { - return FuzzySymbolIndex::queryRegexp(FuzzySymbolIndex::tokenize(query)); - }; - EXPECT_THAT(QueryRegexp("uhc"), MatchesSymbol("URLHandlerCallback")); - EXPECT_THAT(QueryRegexp("urhaca"), MatchesSymbol("URLHandlerCallback")); - EXPECT_THAT(QueryRegexp("uhcb"), Not(MatchesSymbol("URLHandlerCallback"))) - << "Non-prefix"; - EXPECT_THAT(QueryRegexp("uc"), Not(MatchesSymbol("URLHandlerCallback"))) - << "Skip token"; - - EXPECT_THAT(QueryRegexp("uptr"), MatchesSymbol("unique_ptr")); - EXPECT_THAT(QueryRegexp("UniP"), MatchesSymbol("unique_ptr")); -} - -} // namespace -} // namespace include_fixer -} // namespace clang Index: unittests/include-fixer/IncludeFixerTest.cpp =================================================================== --- unittests/include-fixer/IncludeFixerTest.cpp +++ unittests/include-fixer/IncludeFixerTest.cpp @@ -19,7 +19,6 @@ namespace { using find_all_symbols::SymbolInfo; -using find_all_symbols::SymbolAndSignals; static bool runOnCode(tooling::ToolAction *ToolAction, StringRef Code, StringRef FileName, @@ -53,49 +52,42 @@ static std::string runIncludeFixer( StringRef Code, const std::vector &ExtraArgs = std::vector()) { - std::vector Symbols = { - {SymbolInfo("string", SymbolInfo::SymbolKind::Class, "", - {{SymbolInfo::ContextType::Namespace, "std"}}), - SymbolInfo::Signals{}}, - {SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"", - {{SymbolInfo::ContextType::Namespace, "std"}}), - SymbolInfo::Signals{}}, - {SymbolInfo("foo", SymbolInfo::SymbolKind::Class, - "\"dir/otherdir/qux.h\"", - {{SymbolInfo::ContextType::Namespace, "b"}, - {SymbolInfo::ContextType::Namespace, "a"}}), - SymbolInfo::Signals{}}, - {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"", - {{SymbolInfo::ContextType::Namespace, "b"}, - {SymbolInfo::ContextType::Namespace, "a"}}), - SymbolInfo::Signals{}}, - {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar2.h\"", - {{SymbolInfo::ContextType::Namespace, "c"}, - {SymbolInfo::ContextType::Namespace, "a"}}), - SymbolInfo::Signals{}}, - {SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"", - {{SymbolInfo::ContextType::EnumDecl, "Color"}, - {SymbolInfo::ContextType::Namespace, "b"}, - {SymbolInfo::ContextType::Namespace, "a"}}), - SymbolInfo::Signals{}}, - {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", - {{SymbolInfo::ContextType::Namespace, "__a"}, - {SymbolInfo::ContextType::Namespace, "a"}}), - SymbolInfo::Signals{/*Seen=*/2, 0}}, - {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", - {{SymbolInfo::ContextType::Namespace, "a"}}), - SymbolInfo::Signals{/*Seen=*/2, 0}}, - {SymbolInfo("StrCat", SymbolInfo::SymbolKind::Class, "\"strcat.h\"", - {{SymbolInfo::ContextType::Namespace, "str"}}), - SymbolInfo::Signals{}}, - {SymbolInfo("str", SymbolInfo::SymbolKind::Class, "\"str.h\"", {}), - SymbolInfo::Signals{}}, - {SymbolInfo("foo2", SymbolInfo::SymbolKind::Class, "\"foo2.h\"", {}), - SymbolInfo::Signals{}}, + 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, {}), }; - 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"); @@ -104,14 +96,15 @@ assert(FixerContexts.size() == 1); if (FixerContexts.front().getHeaderInfos().empty()) return Code; - auto Replaces = createIncludeFixerReplacements(Code, FixerContexts.front()); + auto Replaces = clang::include_fixer::createIncludeFixerReplacements( + Code, FixerContexts.front()); EXPECT_TRUE(static_cast(Replaces)) << llvm::toString(Replaces.takeError()) << "\n"; if (!Replaces) return ""; - RewriterTestContext Context; - FileID ID = Context.createInMemoryFile(FakeFileName, Code); - tooling::applyAllReplacements(*Replaces, Context.Rewrite); + clang::RewriterTestContext Context; + clang::FileID ID = Context.createInMemoryFile(FakeFileName, Code); + clang::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,7 +19,6 @@ #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" @@ -32,37 +31,34 @@ static const char HeaderName[] = "symbols.h"; -class TestSymbolReporter : public SymbolReporter { +class TestSymbolReporter : public clang::find_all_symbols::SymbolReporter { public: ~TestSymbolReporter() override {} - void reportSymbols(llvm::StringRef FileName, - const SymbolInfo::SignalMap &NewSymbols) override { - for (const auto &Entry : NewSymbols) - Symbols[Entry.first] += Entry.second; + void reportSymbol(llvm::StringRef FileName, + const SymbolInfo &Symbol) override { + Symbols.push_back(Symbol); } - int seen(const SymbolInfo &Symbol) const { - auto it = Symbols.find(Symbol); - return it == Symbols.end() ? 0 : it->second.Seen; - } - - int used(const SymbolInfo &Symbol) const { - auto it = Symbols.find(Symbol); - return it == Symbols.end() ? 0 : it->second.Used; + bool hasSymbol(const SymbolInfo &Symbol) const { + for (const auto &S : Symbols) { + if (S == Symbol) + return true; + } + return false; } private: - SymbolInfo::SignalMap Symbols; + std::vector Symbols; }; class FindAllSymbolsTest : public ::testing::Test { public: - int seen(const SymbolInfo &Symbol) { return Reporter.seen(Symbol); } - - int used(const SymbolInfo &Symbol) { return Reporter.used(Symbol); } + bool hasSymbol(const SymbolInfo &Symbol) { + return Reporter.hasSymbol(Symbol); + } - bool runFindAllSymbols(StringRef HeaderCode, StringRef MainCode) { + bool runFindAllSymbols(StringRef Code) { llvm::IntrusiveRefCntPtr InMemoryFileSystem( new vfs::InMemoryFileSystem); llvm::IntrusiveRefCntPtr Files( @@ -84,15 +80,15 @@ std::string InternalCode = "#include \"private.inc\"\nclass Internal {};"; SymbolInfo InternalSymbol("Internal", SymbolInfo::SymbolKind::Class, - TopHeader, {}); + TopHeader, 2, {}); SymbolInfo IncSymbol("IncHeaderClass", SymbolInfo::SymbolKind::Class, - TopHeader, {}); + TopHeader, 1, {}); InMemoryFileSystem->addFile( IncHeader, 0, llvm::MemoryBuffer::getMemBuffer(IncHeaderCode)); InMemoryFileSystem->addFile(InternalHeader, 0, llvm::MemoryBuffer::getMemBuffer(InternalCode)); - std::unique_ptr Factory( + std::unique_ptr Factory( new FindAllSymbolsActionFactory(&Reporter, &RegexMap)); tooling::ToolInvocation Invocation( @@ -102,7 +98,7 @@ std::make_shared()); InMemoryFileSystem->addFile(HeaderName, 0, - llvm::MemoryBuffer::getMemBuffer(HeaderCode)); + llvm::MemoryBuffer::getMemBuffer(Code)); std::string Content = "#include\"" + std::string(HeaderName) + "\"\n" @@ -118,19 +114,18 @@ InMemoryFileSystem->addFile( DirtyHeader, 0, llvm::MemoryBuffer::getMemBuffer(DirtyHeaderContent)); SymbolInfo DirtyMacro("INTERNAL", SymbolInfo::SymbolKind::Macro, - CleanHeader, {}); + CleanHeader, 1, {}); SymbolInfo DirtySymbol("ExtraInternal", SymbolInfo::SymbolKind::Class, - CleanHeader, {}); + CleanHeader, 2, {}); #endif // _MSC_VER && __MINGW32__ - Content += "\n" + MainCode.str(); InMemoryFileSystem->addFile(FileName, 0, llvm::MemoryBuffer::getMemBuffer(Content)); Invocation.run(); - EXPECT_EQ(1, seen(InternalSymbol)); - EXPECT_EQ(1, seen(IncSymbol)); + EXPECT_TRUE(hasSymbol(InternalSymbol)); + EXPECT_TRUE(hasSymbol(IncSymbol)); #if !defined(_MSC_VER) && !defined(__MINGW32__) - EXPECT_EQ(1, seen(DirtySymbol)); - EXPECT_EQ(1, seen(DirtyMacro)); + EXPECT_TRUE(hasSymbol(DirtySymbol)); + EXPECT_TRUE(hasSymbol(DirtyMacro)); #endif // _MSC_VER && __MINGW32__ return true; } @@ -140,64 +135,49 @@ }; TEST_F(FindAllSymbolsTest, VariableSymbols) { - static const char Header[] = R"( + static const char Code[] = R"( extern int xargc; namespace na { static bool SSSS = false; namespace nb { const long long *XXXX; } })"; - static const char Main[] = R"( - auto y = &na::nb::XXXX; - int main() { if (na::SSSS) return xargc; } - )"; - runFindAllSymbols(Header, Main); + runFindAllSymbols(Code); SymbolInfo Symbol = - SymbolInfo("xargc", SymbolInfo::SymbolKind::Variable, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + SymbolInfo("xargc", SymbolInfo::SymbolKind::Variable, HeaderName, 2, {}); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("SSSS", SymbolInfo::SymbolKind::Variable, HeaderName, + Symbol = SymbolInfo("SSSS", SymbolInfo::SymbolKind::Variable, HeaderName, 4, {{SymbolInfo::ContextType::Namespace, "na"}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("XXXX", SymbolInfo::SymbolKind::Variable, HeaderName, + Symbol = SymbolInfo("XXXX", SymbolInfo::SymbolKind::Variable, HeaderName, 5, {{SymbolInfo::ContextType::Namespace, "nb"}, {SymbolInfo::ContextType::Namespace, "na"}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, ExternCSymbols) { - static const char Header[] = R"( + static const char Code[] = R"( extern "C" { int C_Func() { return 0; } struct C_struct { int Member; }; })"; - static const char Main[] = R"( - C_struct q() { - int(*ptr)() = C_Func; - return {0}; - } - )"; - runFindAllSymbols(Header, Main); + runFindAllSymbols(Code); SymbolInfo Symbol = - SymbolInfo("C_Func", SymbolInfo::SymbolKind::Function, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + SymbolInfo("C_Func", SymbolInfo::SymbolKind::Function, HeaderName, 3, {}); + EXPECT_TRUE(hasSymbol(Symbol)); Symbol = - SymbolInfo("C_struct", SymbolInfo::SymbolKind::Class, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + SymbolInfo("C_struct", SymbolInfo::SymbolKind::Class, HeaderName, 4, {}); + EXPECT_TRUE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, CXXRecordSymbols) { - static const char Header[] = R"( + static const char Code[] = R"( struct Glob {}; struct A; // Not a defintion, ignored. class NOP; // Not a defintion, ignored @@ -210,33 +190,26 @@ }; }; // )"; - static const char Main[] = R"( - static Glob glob; - static na::A::AAAA* a; - )"; - runFindAllSymbols(Header, Main); + runFindAllSymbols(Code); SymbolInfo Symbol = - SymbolInfo("Glob", SymbolInfo::SymbolKind::Class, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + SymbolInfo("Glob", SymbolInfo::SymbolKind::Class, HeaderName, 2, {}); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("A", SymbolInfo::SymbolKind::Class, HeaderName, + Symbol = SymbolInfo("A", SymbolInfo::SymbolKind::Class, HeaderName, 6, {{SymbolInfo::ContextType::Namespace, "na"}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("AAA", SymbolInfo::SymbolKind::Class, HeaderName, + Symbol = SymbolInfo("AAA", SymbolInfo::SymbolKind::Class, HeaderName, 7, {{SymbolInfo::ContextType::Record, "A"}, {SymbolInfo::ContextType::Namespace, "na"}}); - EXPECT_EQ(0, seen(Symbol)); - EXPECT_EQ(0, used(Symbol)); + EXPECT_FALSE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, CXXRecordSymbolsTemplate) { - static const char Header[] = R"( + static const char Code[] = R"( template - struct T_TEMP { + class T_TEMP { template struct rebind { typedef T_TEMP<_Tp1> other; }; }; @@ -249,15 +222,11 @@ // Ignore specialization. template <> class Observer {}; )"; - static const char Main[] = R"( - extern T_TEMP::rebind weirdo; - )"; - runFindAllSymbols(Header, Main); + runFindAllSymbols(Code); SymbolInfo Symbol = - SymbolInfo("T_TEMP", SymbolInfo::SymbolKind::Class, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + SymbolInfo("T_TEMP", SymbolInfo::SymbolKind::Class, HeaderName, 3, {}); + EXPECT_TRUE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, DontIgnoreTemplatePartialSpecialization) { @@ -270,16 +239,20 @@ template void f() {}; template<> void f() {}; )"; - runFindAllSymbols(Code, ""); + runFindAllSymbols(Code); SymbolInfo Symbol = - SymbolInfo("Class", SymbolInfo::SymbolKind::Class, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - Symbol = SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); + SymbolInfo("Class", SymbolInfo::SymbolKind::Class, HeaderName, 4, {}); + EXPECT_TRUE(hasSymbol(Symbol)); + Symbol = + SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName, 7, {}); + EXPECT_TRUE(hasSymbol(Symbol)); + Symbol = + SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName, 8, {}); + EXPECT_FALSE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, FunctionSymbols) { - static const char Header[] = R"( + static const char Code[] = R"( namespace na { int gg(int); int f(const int &a) { int Local; static int StaticLocal; return 0; } @@ -292,126 +265,91 @@ } // namespace nb } // namespace na"; )"; - 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); + runFindAllSymbols(Code); SymbolInfo Symbol = - SymbolInfo("gg", SymbolInfo::SymbolKind::Function, HeaderName, + SymbolInfo("gg", SymbolInfo::SymbolKind::Function, HeaderName, 3, {{SymbolInfo::ContextType::Namespace, "na"}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName, + Symbol = SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName, 4, {{SymbolInfo::ContextType::Namespace, "na"}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("SSSFFF", SymbolInfo::SymbolKind::Function, HeaderName, + Symbol = SymbolInfo("SSSFFF", SymbolInfo::SymbolKind::Function, HeaderName, 5, {{SymbolInfo::ContextType::Namespace, "na"}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("fun", SymbolInfo::SymbolKind::Function, HeaderName, + Symbol = SymbolInfo("fun", SymbolInfo::SymbolKind::Function, HeaderName, 10, {{SymbolInfo::ContextType::Namespace, "nb"}, {SymbolInfo::ContextType::Namespace, "na"}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, NamespaceTest) { - static const char Header[] = R"( + static const char Code[] = R"( int X1; namespace { int X2; } namespace { namespace { int X3; } } namespace { namespace nb { int X4; } } namespace na { inline namespace __1 { int X5; } } )"; - static const char Main[] = R"( - using namespace nb; - int main() { - X1 = X2; - X3 = X4; - (void)na::X5; - } - )"; - runFindAllSymbols(Header, Main); + runFindAllSymbols(Code); SymbolInfo Symbol = - SymbolInfo("X1", SymbolInfo::SymbolKind::Variable, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + SymbolInfo("X1", SymbolInfo::SymbolKind::Variable, HeaderName, 2, {}); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("X2", SymbolInfo::SymbolKind::Variable, HeaderName, + Symbol = SymbolInfo("X2", SymbolInfo::SymbolKind::Variable, HeaderName, 3, {{SymbolInfo::ContextType::Namespace, ""}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("X3", SymbolInfo::SymbolKind::Variable, HeaderName, + Symbol = SymbolInfo("X3", SymbolInfo::SymbolKind::Variable, HeaderName, 4, {{SymbolInfo::ContextType::Namespace, ""}, {SymbolInfo::ContextType::Namespace, ""}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("X4", SymbolInfo::SymbolKind::Variable, HeaderName, + Symbol = SymbolInfo("X4", SymbolInfo::SymbolKind::Variable, HeaderName, 5, {{SymbolInfo::ContextType::Namespace, "nb"}, {SymbolInfo::ContextType::Namespace, ""}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("X5", SymbolInfo::SymbolKind::Variable, HeaderName, + Symbol = SymbolInfo("X5", SymbolInfo::SymbolKind::Variable, HeaderName, 6, {{SymbolInfo::ContextType::Namespace, "na"}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, DecayedTypeTest) { - static const char Header[] = "void DecayedFunc(int x[], int y[10]) {}"; - static const char Main[] = R"(int main() { DecayedFunc(nullptr, nullptr); })"; - runFindAllSymbols(Header, Main); + static const char Code[] = "void DecayedFunc(int x[], int y[10]) {}"; + runFindAllSymbols(Code); SymbolInfo Symbol = SymbolInfo( - "DecayedFunc", SymbolInfo::SymbolKind::Function, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + "DecayedFunc", SymbolInfo::SymbolKind::Function, HeaderName, 1, {}); + EXPECT_TRUE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, CTypedefTest) { - static const char Header[] = R"( + static const char Code[] = R"( typedef unsigned size_t_; typedef struct { int x; } X; using XX = X; )"; - static const char Main[] = R"( - size_t_ f; - template struct vector{}; - vector list; - void foo(const XX&){} - )"; - runFindAllSymbols(Header, Main); + runFindAllSymbols(Code); SymbolInfo Symbol = SymbolInfo("size_t_", SymbolInfo::SymbolKind::TypedefName, - HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + HeaderName, 2, {}); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("X", SymbolInfo::SymbolKind::TypedefName, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + Symbol = + SymbolInfo("X", SymbolInfo::SymbolKind::TypedefName, HeaderName, 3, {}); + EXPECT_TRUE(hasSymbol(Symbol)); Symbol = - SymbolInfo("XX", SymbolInfo::SymbolKind::TypedefName, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + SymbolInfo("XX", SymbolInfo::SymbolKind::TypedefName, HeaderName, 4, {}); + EXPECT_TRUE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, EnumTest) { - static const char Header[] = R"( + static const char Code[] = R"( enum Glob_E { G1, G2 }; enum class Altitude { high='h', low='l'}; enum { A1, A2 }; @@ -421,157 +359,122 @@ }; enum DECL : int; )"; - 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); + runFindAllSymbols(Code); SymbolInfo Symbol = - SymbolInfo("Glob_E", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(0, used(Symbol)); + SymbolInfo("Glob_E", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 2, {}); + EXPECT_TRUE(hasSymbol(Symbol)); Symbol = - SymbolInfo("G1", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName, + SymbolInfo("G1", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName, 2, {{SymbolInfo::ContextType::EnumDecl, "Glob_E"}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); Symbol = - SymbolInfo("G2", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName, + SymbolInfo("G2", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName, 2, {{SymbolInfo::ContextType::EnumDecl, "Glob_E"}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = - SymbolInfo("Altitude", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + Symbol = SymbolInfo("Altitude", SymbolInfo::SymbolKind::EnumDecl, HeaderName, + 3, {}); + EXPECT_TRUE(hasSymbol(Symbol)); Symbol = SymbolInfo("high", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName, - {{SymbolInfo::ContextType::EnumDecl, "Altitude"}}); - EXPECT_EQ(0, seen(Symbol)); - EXPECT_EQ(0, used(Symbol)); + 3, {{SymbolInfo::ContextType::EnumDecl, "Altitude"}}); + EXPECT_FALSE(hasSymbol(Symbol)); Symbol = SymbolInfo("A1", SymbolInfo::SymbolKind::EnumConstantDecl, - HeaderName, {{SymbolInfo::ContextType::EnumDecl, ""}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + HeaderName, 4, {{SymbolInfo::ContextType::EnumDecl, ""}}); + EXPECT_TRUE(hasSymbol(Symbol)); Symbol = SymbolInfo("A2", SymbolInfo::SymbolKind::EnumConstantDecl, - HeaderName, {{SymbolInfo::ContextType::EnumDecl, ""}}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); - Symbol = SymbolInfo("", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {}); - EXPECT_EQ(0, seen(Symbol)); - EXPECT_EQ(0, used(Symbol)); - - Symbol = SymbolInfo("A_ENUM", SymbolInfo::SymbolKind::EnumDecl, HeaderName, + HeaderName, 4, {{SymbolInfo::ContextType::EnumDecl, ""}}); + EXPECT_TRUE(hasSymbol(Symbol)); + Symbol = SymbolInfo("", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 4, {}); + EXPECT_FALSE(hasSymbol(Symbol)); + + Symbol = SymbolInfo("A_ENUM", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 7, {{SymbolInfo::ContextType::Record, "A"}}); - EXPECT_EQ(0, seen(Symbol)); - EXPECT_EQ(0, used(Symbol)); + EXPECT_FALSE(hasSymbol(Symbol)); - Symbol = SymbolInfo("X1", SymbolInfo::SymbolKind::EnumDecl, HeaderName, + Symbol = SymbolInfo("X1", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 7, {{SymbolInfo::ContextType::EnumDecl, "A_ENUM"}, {SymbolInfo::ContextType::Record, "A"}}); - EXPECT_EQ(0, seen(Symbol)); + EXPECT_FALSE(hasSymbol(Symbol)); - Symbol = SymbolInfo("DECL", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {}); - EXPECT_EQ(0, seen(Symbol)); + Symbol = + SymbolInfo("DECL", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 9, {}); + EXPECT_FALSE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, IWYUPrivatePragmaTest) { - static const char Header[] = R"( + static const char Code[] = R"( // IWYU pragma: private, include "bar.h" struct Bar { }; )"; - static const char Main[] = R"( - Bar bar; - )"; - runFindAllSymbols(Header, Main); + runFindAllSymbols(Code); SymbolInfo Symbol = - SymbolInfo("Bar", SymbolInfo::SymbolKind::Class, "bar.h", {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + SymbolInfo("Bar", SymbolInfo::SymbolKind::Class, "bar.h", 3, {}); + EXPECT_TRUE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, MacroTest) { - static const char Header[] = R"( + static const char Code[] = R"( #define X #define Y 1 #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) )"; - static const char Main[] = R"( - #ifdef X - int main() { return MAX(0,Y); } - #endif - )"; - runFindAllSymbols(Header, Main); + runFindAllSymbols(Code); SymbolInfo Symbol = - SymbolInfo("X", SymbolInfo::SymbolKind::Macro, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + SymbolInfo("X", SymbolInfo::SymbolKind::Macro, HeaderName, 2, {}); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, HeaderName, 3, {}); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, HeaderName, 4, {}); + EXPECT_TRUE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, MacroTestWithIWYU) { - static const char Header[] = R"( + static const char Code[] = R"( // IWYU pragma: private, include "bar.h" - #define X 1 + #define X #define Y 1 #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) )"; - static const char Main[] = R"( - #ifdef X - int main() { return MAX(0,Y); } - #endif - )"; - runFindAllSymbols(Header, Main); + runFindAllSymbols(Code); SymbolInfo Symbol = - SymbolInfo("X", SymbolInfo::SymbolKind::Macro, "bar.h", {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + SymbolInfo("X", SymbolInfo::SymbolKind::Macro, "bar.h", 3, {}); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, "bar.h", {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, "bar.h", 4, {}); + EXPECT_TRUE(hasSymbol(Symbol)); - Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, "bar.h", {}); - EXPECT_EQ(1, seen(Symbol)); - EXPECT_EQ(1, used(Symbol)); + Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, "bar.h", 5, {}); + EXPECT_TRUE(hasSymbol(Symbol)); } TEST_F(FindAllSymbolsTest, NoFriendTest) { - static const char Header[] = R"( + static const char Code[] = R"( class WorstFriend { friend void Friend(); friend class BestFriend; }; )"; - runFindAllSymbols(Header, ""); - SymbolInfo Symbol = - SymbolInfo("WorstFriend", SymbolInfo::SymbolKind::Class, HeaderName, {}); - EXPECT_EQ(1, seen(Symbol)); - - Symbol = - SymbolInfo("Friend", SymbolInfo::SymbolKind::Function, HeaderName, {}); - EXPECT_EQ(0, seen(Symbol)); - - Symbol = - SymbolInfo("BestFriend", SymbolInfo::SymbolKind::Class, HeaderName, {}); - EXPECT_EQ(0, seen(Symbol)); + runFindAllSymbols(Code); + SymbolInfo Symbol = SymbolInfo("WorstFriend", SymbolInfo::SymbolKind::Class, + HeaderName, 2, {}); + EXPECT_TRUE(hasSymbol(Symbol)); + + Symbol = SymbolInfo("Friend", SymbolInfo::SymbolKind::Function, HeaderName, + 3, {}); + EXPECT_FALSE(hasSymbol(Symbol)); + + Symbol = SymbolInfo("BestFriend", SymbolInfo::SymbolKind::Class, HeaderName, + 4, {}); + EXPECT_FALSE(hasSymbol(Symbol)); } } // namespace find_all_symbols