Index: clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h =================================================================== --- clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h +++ clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h @@ -91,6 +91,11 @@ FileToReplacementsMap &GroupedReplacements, clang::SourceManager &SM); +// FIXME: Remove this function after changing clang-apply-replacements to use +// Replacements class. +bool applyAllReplacements(const std::vector &Replaces, + Rewriter &Rewrite); + /// \brief Apply all replacements in \c GroupedReplacements. /// /// \param[in] GroupedReplacements Deduplicated and conflict free Replacements Index: clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp =================================================================== --- clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp +++ clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp @@ -122,6 +122,81 @@ } } +// FIXME: Remove this function after changing clang-apply-replacements to use +// Replacements class. +bool applyAllReplacements(const std::vector &Replaces, + Rewriter &Rewrite) { + bool Result = true; + for (std::vector::const_iterator I = Replaces.begin(), + E = Replaces.end(); + I != E; ++I) { + if (I->isApplicable()) { + Result = I->apply(Rewrite) && Result; + } else { + Result = false; + } + } + return Result; +} + + +// FIXME: moved from libToolingCore. remove this when std::vector +// is replaced with tooling::Replacements class. +static void deduplicate(std::vector &Replaces, + std::vector &Conflicts) { + if (Replaces.empty()) + return; + + auto LessNoPath = [](const tooling::Replacement &LHS, + const tooling::Replacement &RHS) { + if (LHS.getOffset() != RHS.getOffset()) + return LHS.getOffset() < RHS.getOffset(); + if (LHS.getLength() != RHS.getLength()) + return LHS.getLength() < RHS.getLength(); + return LHS.getReplacementText() < RHS.getReplacementText(); + }; + + auto EqualNoPath = [](const tooling::Replacement &LHS, + const tooling::Replacement &RHS) { + return LHS.getOffset() == RHS.getOffset() && + LHS.getLength() == RHS.getLength() && + LHS.getReplacementText() == RHS.getReplacementText(); + }; + + // Deduplicate. We don't want to deduplicate based on the path as we assume + // that all replacements refer to the same file (or are symlinks). + std::sort(Replaces.begin(), Replaces.end(), LessNoPath); + Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath), + Replaces.end()); + + // Detect conflicts + tooling::Range ConflictRange(Replaces.front().getOffset(), + Replaces.front().getLength()); + unsigned ConflictStart = 0; + unsigned ConflictLength = 1; + for (unsigned i = 1; i < Replaces.size(); ++i) { + tooling::Range Current(Replaces[i].getOffset(), Replaces[i].getLength()); + if (ConflictRange.overlapsWith(Current)) { + // Extend conflicted range + ConflictRange = + tooling::Range(ConflictRange.getOffset(), + std::max(ConflictRange.getLength(), + Current.getOffset() + Current.getLength() - + ConflictRange.getOffset())); + ++ConflictLength; + } else { + if (ConflictLength > 1) + Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength)); + ConflictRange = Current; + ConflictStart = i; + ConflictLength = 1; + } + } + + if (ConflictLength > 1) + Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength)); +} + /// \brief Deduplicates and tests for conflicts among the replacements for each /// file in \c Replacements. Any conflicts found are reported. /// @@ -144,7 +219,7 @@ assert(Entry != nullptr && "No file entry!"); std::vector Conflicts; - tooling::deduplicate(FileAndReplacements.second, Conflicts); + deduplicate(FileAndReplacements.second, Conflicts); if (Conflicts.empty()) continue; @@ -197,7 +272,7 @@ // However, until we nail down the design of ReplacementGroups, might as well // leave this as is. for (const auto &FileAndReplacements : GroupedReplacements) { - if (!tooling::applyAllReplacements(FileAndReplacements.second, Rewrites)) + if (!applyAllReplacements(FileAndReplacements.second, Rewrites)) return false; } Index: clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp =================================================================== --- clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp +++ clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp @@ -108,7 +108,7 @@ Rewriter &Rewrites, std::string &Result) { if (Replacements.empty()) return true; - if (!tooling::applyAllReplacements(Replacements, Rewrites)) + if (!applyAllReplacements(Replacements, Rewrites)) return false; SourceManager &SM = Rewrites.getSourceMgr(); Index: clang-rename/RenamingAction.h =================================================================== --- clang-rename/RenamingAction.h +++ clang-rename/RenamingAction.h @@ -25,22 +25,22 @@ class RenamingAction { public: - RenamingAction(const std::string &NewName, const std::string &PrevName, - const std::vector &USRs, - tooling::Replacements &Replaces, bool PrintLocations = false) - : NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces), - PrintLocations(PrintLocations) { - } + RenamingAction(const std::vector &NewNames, + const std::vector &PrevNames, + const std::vector> &USRList, + std::map &FileToReplaces, + bool PrintLocations = false) + : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList), + FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {} std::unique_ptr newASTConsumer(); private: - const std::string &NewName, &PrevName; - const std::vector &USRs; - tooling::Replacements &Replaces; + const std::vector &NewNames, &PrevNames; + const std::vector> &USRList; + std::map &FileToReplaces; bool PrintLocations; }; - } } Index: clang-rename/RenamingAction.cpp =================================================================== --- clang-rename/RenamingAction.cpp +++ clang-rename/RenamingAction.cpp @@ -34,27 +34,32 @@ class RenamingASTConsumer : public ASTConsumer { public: - RenamingASTConsumer(const std::string &NewName, - const std::string &PrevName, - const std::vector &USRs, - tooling::Replacements &Replaces, - bool PrintLocations) - : NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces), - PrintLocations(PrintLocations) { - } + RenamingASTConsumer( + const std::vector &NewNames, + const std::vector &PrevNames, + const std::vector> &USRList, + std::map &FileToReplaces, + bool PrintLocations) + : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList), + FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {} void HandleTranslationUnit(ASTContext &Context) override { + for (unsigned I = 0; I < NewNames.size(); ++I) { + HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]); + } + } + + void HandleOneRename(ASTContext &Context, const std::string &NewName, + const std::string &PrevName, + const std::vector &USRs) { const auto &SourceMgr = Context.getSourceManager(); std::vector RenamingCandidates; std::vector NewCandidates; - for (const auto &USR : USRs) { - NewCandidates = getLocationsOfUSR(USR, PrevName, - Context.getTranslationUnitDecl()); - RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(), - NewCandidates.end()); - NewCandidates.clear(); - } + NewCandidates = + getLocationsOfUSRs(USRs, PrevName, Context.getTranslationUnitDecl()); + RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(), + NewCandidates.end()); auto PrevNameLen = PrevName.length(); for (const auto &Loc : RenamingCandidates) { @@ -64,21 +69,25 @@ << ":" << FullLoc.getSpellingLineNumber() << ":" << FullLoc.getSpellingColumnNumber() << "\n"; } - Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen, - NewName)); + // FIXME: better error handling. + auto Replace = tooling::Replacement(SourceMgr, Loc, PrevNameLen, NewName); + auto Err = FileToReplaces[Replace.getFilePath()].add(Replace); + if (Err) + llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! " + << llvm::toString(std::move(Err)) << "\n"; } } private: - const std::string &NewName, &PrevName; - const std::vector &USRs; - tooling::Replacements &Replaces; + const std::vector &NewNames, &PrevNames; + const std::vector> &USRList; + std::map &FileToReplaces; bool PrintLocations; }; std::unique_ptr RenamingAction::newASTConsumer() { - return llvm::make_unique(NewName, PrevName, USRs, - Replaces, PrintLocations); + return llvm::make_unique(NewNames, PrevNames, USRList, + FileToReplaces, PrintLocations); } } // namespace rename Index: clang-rename/USRFinder.h =================================================================== --- clang-rename/USRFinder.h +++ clang-rename/USRFinder.h @@ -49,7 +49,7 @@ class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback { public: explicit NestedNameSpecifierLocFinder(ASTContext &Context) - : Context(Context) {} + : Context(Context) {} std::vector getNestedNameSpecifierLocations() { addMatchers(); @@ -65,8 +65,8 @@ } virtual void run(const MatchFinder::MatchResult &Result) { - const auto *NNS = - Result.Nodes.getNodeAs("nestedNameSpecifierLoc"); + const auto *NNS = Result.Nodes.getNodeAs( + "nestedNameSpecifierLoc"); Locations.push_back(*NNS); } @@ -74,7 +74,6 @@ std::vector Locations; MatchFinder Finder; }; - } } Index: clang-rename/USRFinder.cpp =================================================================== --- clang-rename/USRFinder.cpp +++ clang-rename/USRFinder.cpp @@ -77,10 +77,38 @@ const auto TypeBeginLoc = Loc.getBeginLoc(); const auto TypeEndLoc = Lexer::getLocForEndOfToken( TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); + if (const auto *TemplateTypeParm = + dyn_cast(Loc.getType())) { + return setResult(TemplateTypeParm->getDecl(), TypeBeginLoc, TypeEndLoc); + } + if (const auto *TemplateSpecType = + dyn_cast(Loc.getType())) { + return setResult(TemplateSpecType->getTemplateName().getAsTemplateDecl(), + TypeBeginLoc, TypeEndLoc); + } return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc, TypeEndLoc); } + bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) { + for (const auto *Initializer : ConstructorDecl->inits()) { + if (!Initializer->isWritten()) { + // Ignore implicit initializers. + continue; + } + if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) { + const SourceLocation InitBeginLoc = Initializer->getSourceLocation(), + InitEndLoc = Lexer::getLocForEndOfToken( + InitBeginLoc, 0, Context.getSourceManager(), + Context.getLangOpts()); + if (!setResult(FieldDecl, InitBeginLoc, InitEndLoc)) { + return false; + } + } + } + return true; + } + // Other: const NamedDecl *getNamedDecl() { return Result; } Index: clang-rename/USRFindingAction.cpp =================================================================== --- clang-rename/USRFindingAction.cpp +++ clang-rename/USRFindingAction.cpp @@ -20,7 +20,6 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/RecursiveASTVisitor.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/FileManager.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" @@ -30,13 +29,11 @@ #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Tooling.h" #include -#include #include +#include #include - using namespace llvm; -using namespace clang::ast_matchers; namespace clang { namespace rename { @@ -46,65 +43,101 @@ // AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given // Decl refers to class and adds USRs of all overridden methods if Decl refers // to virtual method. -class AdditionalUSRFinder : public MatchFinder::MatchCallback { +class AdditionalUSRFinder : public RecursiveASTVisitor { public: explicit AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context, - std::vector *USRs) - : FoundDecl(FoundDecl), Context(Context), USRs(USRs), USRSet(), Finder() {} + std::vector *USRs) + : FoundDecl(FoundDecl), Context(Context), USRs(USRs) {} void Find() { - USRSet.insert(getUSRForDecl(FoundDecl)); - addUSRsFromOverrideSetsAndCtorDtors(); - addMatchers(); - Finder.matchAST(Context); + // Fill OverriddenMethods and PartialSpecs storages. + TraverseDecl(Context.getTranslationUnitDecl()); + if (const auto *MethodDecl = dyn_cast(FoundDecl)) { + addUSRsOfOverridenFunctions(MethodDecl); + for (const auto &OverriddenMethod : OverriddenMethods) { + if (checkIfOverriddenFunctionAscends(OverriddenMethod)) { + USRSet.insert(getUSRForDecl(OverriddenMethod)); + } + } + } else if (const auto *RecordDecl = dyn_cast(FoundDecl)) { + handleCXXRecordDecl(RecordDecl); + } else if (const auto *TemplateDecl = + dyn_cast(FoundDecl)) { + handleClassTemplateDecl(TemplateDecl); + } else { + USRSet.insert(getUSRForDecl(FoundDecl)); + } USRs->insert(USRs->end(), USRSet.begin(), USRSet.end()); } + bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) { + if (MethodDecl->isVirtual()) { + OverriddenMethods.push_back(MethodDecl); + } + return true; + } + + bool VisitClassTemplatePartialSpecializationDecl( + const ClassTemplatePartialSpecializationDecl *PartialSpec) { + PartialSpecs.push_back(PartialSpec); + return true; + } + private: - void addMatchers() { - const auto CXXMethodDeclMatcher = - cxxMethodDecl(isVirtual()).bind("cxxMethodDecl"); - Finder.addMatcher(CXXMethodDeclMatcher, this); + void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) { + RecordDecl = RecordDecl->getDefinition(); + if (const auto *ClassTemplateSpecDecl = + dyn_cast(RecordDecl)) { + handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate()); + } + addUSRsOfCtorDtors(RecordDecl); } - // FIXME: Implement hasOverriddenMethod and matchesUSR matchers to make - // lookups more efficient. - virtual void run(const MatchFinder::MatchResult &Result) { - const auto *VirtualMethod = - Result.Nodes.getNodeAs("cxxMethodDecl"); - bool Found = false; - for (const auto &OverriddenMethod : VirtualMethod->overridden_methods()) { - if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end()) { - Found = true; + void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) { + for (const auto *Specialization : TemplateDecl->specializations()) { + addUSRsOfCtorDtors(Specialization); + } + for (const auto *PartialSpec : PartialSpecs) { + if (PartialSpec->getSpecializedTemplate() == TemplateDecl) { + addUSRsOfCtorDtors(PartialSpec); } } - if (Found) { - USRSet.insert(getUSRForDecl(VirtualMethod)); + addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl()); + } + + void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) { + RecordDecl = RecordDecl->getDefinition(); + for (const auto *CtorDecl : RecordDecl->ctors()) { + USRSet.insert(getUSRForDecl(CtorDecl)); } + USRSet.insert(getUSRForDecl(RecordDecl->getDestructor())); + USRSet.insert(getUSRForDecl(RecordDecl)); } - void addUSRsFromOverrideSetsAndCtorDtors() { - // If D is CXXRecordDecl we should add all USRs of its constructors. - if (const auto *RecordDecl = dyn_cast(FoundDecl)) { - RecordDecl = RecordDecl->getDefinition(); - for (const auto *CtorDecl : RecordDecl->ctors()) { - USRSet.insert(getUSRForDecl(CtorDecl)); - } - USRSet.insert(getUSRForDecl(RecordDecl->getDestructor())); + void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) { + USRSet.insert(getUSRForDecl(MethodDecl)); + for (auto &OverriddenMethod : MethodDecl->overridden_methods()) { + // Recursively visit each OverridenMethod. + addUSRsOfOverridenFunctions(OverriddenMethod); } - // If D is CXXMethodDecl we should add all USRs of its overriden methods. - if (const auto *MethodDecl = dyn_cast(FoundDecl)) { - for (auto &OverriddenMethod : MethodDecl->overridden_methods()) { - USRSet.insert(getUSRForDecl(OverriddenMethod)); + } + + bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) { + for (auto &OverriddenMethod : MethodDecl->overridden_methods()) { + if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end()) { + return true; } + return checkIfOverriddenFunctionAscends(OverriddenMethod); } + return false; } const Decl *FoundDecl; ASTContext &Context; std::vector *USRs; std::set USRSet; - MatchFinder Finder; + std::vector OverriddenMethods; + std::vector PartialSpecs; }; } // namespace Index: clang-rename/USRLocFinder.h =================================================================== --- clang-rename/USRLocFinder.h +++ clang-rename/USRLocFinder.h @@ -26,7 +26,8 @@ // FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree! std::vector -getLocationsOfUSR(llvm::StringRef USR, llvm::StringRef PrevName, Decl *Decl); +getLocationsOfUSRs(const std::vector &USRs, + llvm::StringRef PrevName, Decl *Decl); } // namespace rename } // namespace clang Index: clang-rename/USRLocFinder.cpp =================================================================== --- clang-rename/USRLocFinder.cpp +++ clang-rename/USRLocFinder.cpp @@ -34,30 +34,23 @@ class USRLocFindingASTVisitor : public clang::RecursiveASTVisitor { public: - explicit USRLocFindingASTVisitor(StringRef USR, StringRef PrevName, + explicit USRLocFindingASTVisitor(const std::vector &USRs, + StringRef PrevName, const ASTContext &Context) - : USR(USR), PrevName(PrevName), Context(Context) {} + : USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) { + } // Declaration visitors: bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) { - for (auto &Initializer : ConstructorDecl->inits()) { - if (Initializer->getSourceOrder() == -1) { + for (const auto *Initializer : ConstructorDecl->inits()) { + if (!Initializer->isWritten()) { // Ignore implicit initializers. continue; } - if (const clang::FieldDecl *FieldDecl = Initializer->getAnyMember()) { - if (getUSRForDecl(FieldDecl) == USR) { - // The initializer refers to a field that is to be renamed. - SourceLocation Location = Initializer->getSourceLocation(); - StringRef TokenName = Lexer::getSourceText( - CharSourceRange::getTokenRange(Location), - Context.getSourceManager(), Context.getLangOpts()); - if (TokenName == PrevName) { - // The token of the source location we find actually has the old - // name. - LocationsFound.push_back(Initializer->getSourceLocation()); - } + if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) { + if (USRSet.find(getUSRForDecl(FieldDecl)) != USRSet.end()) { + LocationsFound.push_back(Initializer->getSourceLocation()); } } } @@ -65,7 +58,7 @@ } bool VisitNamedDecl(const NamedDecl *Decl) { - if (getUSRForDecl(Decl) == USR) { + if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) { checkAndAddLocation(Decl->getLocation()); } return true; @@ -76,7 +69,7 @@ bool VisitDeclRefExpr(const DeclRefExpr *Expr) { const auto *Decl = Expr->getFoundDecl(); - if (getUSRForDecl(Decl) == USR) { + if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) { const SourceManager &Manager = Decl->getASTContext().getSourceManager(); SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation()); checkAndAddLocation(Location); @@ -87,7 +80,7 @@ bool VisitMemberExpr(const MemberExpr *Expr) { const auto *Decl = Expr->getFoundDecl().getDecl(); - if (getUSRForDecl(Decl) == USR) { + if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) { const SourceManager &Manager = Decl->getASTContext().getSourceManager(); SourceLocation Location = Manager.getSpellingLoc(Expr->getMemberLoc()); checkAndAddLocation(Location); @@ -98,9 +91,17 @@ // Other visitors: bool VisitTypeLoc(const TypeLoc Loc) { - if (getUSRForDecl(Loc.getType()->getAsCXXRecordDecl()) == USR) { + if (USRSet.find(getUSRForDecl(Loc.getType()->getAsCXXRecordDecl())) != + USRSet.end()) { checkAndAddLocation(Loc.getBeginLoc()); } + if (const auto *TemplateTypeParm = + dyn_cast(Loc.getType())) { + if (USRSet.find(getUSRForDecl(TemplateTypeParm->getDecl())) != + USRSet.end()) { + checkAndAddLocation(Loc.getBeginLoc()); + } + } return true; } @@ -116,7 +117,7 @@ void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) { while (NameLoc) { const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace(); - if (Decl && getUSRForDecl(Decl) == USR) { + if (Decl && USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) { checkAndAddLocation(NameLoc.getLocalBeginLoc()); } NameLoc = NameLoc.getPrefix(); @@ -127,8 +128,7 @@ void checkAndAddLocation(SourceLocation Loc) { const auto BeginLoc = Loc; const auto EndLoc = Lexer::getLocForEndOfToken( - BeginLoc, 0, Context.getSourceManager(), - Context.getLangOpts()); + BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); StringRef TokenName = Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc), Context.getSourceManager(), Context.getLangOpts()); @@ -140,18 +140,17 @@ } } - // All the locations of the USR were found. - const std::string USR; - // Old name that is renamed. + const std::set USRSet; const std::string PrevName; std::vector LocationsFound; const ASTContext &Context; }; } // namespace -std::vector getLocationsOfUSR(StringRef USR, StringRef PrevName, - Decl *Decl) { - USRLocFindingASTVisitor Visitor(USR, PrevName, Decl->getASTContext()); +std::vector +getLocationsOfUSRs(const std::vector &USRs, StringRef PrevName, + Decl *Decl) { + USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext()); Visitor.TraverseDecl(Decl); NestedNameSpecifierLocFinder Finder(Decl->getASTContext()); for (const auto &Location : Finder.getNestedNameSpecifierLocations()) { Index: clang-rename/tool/CMakeLists.txt =================================================================== --- clang-rename/tool/CMakeLists.txt +++ clang-rename/tool/CMakeLists.txt @@ -10,3 +10,10 @@ ) install(TARGETS clang-rename RUNTIME DESTINATION bin) + +install(PROGRAMS clang-rename.py + DESTINATION share/clang + COMPONENT clang-rename) +install(PROGRAMS clang-rename.el + DESTINATION share/clang + COMPONENT clang-rename) Index: clang-rename/tool/ClangRename.cpp =================================================================== --- clang-rename/tool/ClangRename.cpp +++ clang-rename/tool/ClangRename.cpp @@ -13,8 +13,8 @@ /// //===----------------------------------------------------------------------===// -#include "../USRFindingAction.h" #include "../RenamingAction.h" +#include "../USRFindingAction.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" @@ -31,102 +31,210 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" #include #include #include using namespace llvm; -cl::OptionCategory ClangRenameCategory("Clang-rename options"); - -static cl::opt -NewName( - "new-name", - cl::desc("The new name to change the symbol to."), - cl::cat(ClangRenameCategory)); -static cl::opt -SymbolOffset( - "offset", - cl::desc("Locates the symbol by offset as opposed to :."), - cl::cat(ClangRenameCategory)); -static cl::opt -OldName( - "old-name", - cl::desc("The fully qualified name of the symbol, if -offset is not used."), - cl::cat(ClangRenameCategory)); -static cl::opt -Inplace( - "i", - cl::desc("Overwrite edited s."), - cl::cat(ClangRenameCategory)); -static cl::opt -PrintName( - "pn", - cl::desc("Print the found symbol's name prior to renaming to stderr."), - cl::cat(ClangRenameCategory)); -static cl::opt -PrintLocations( - "pl", - cl::desc("Print the locations affected by renaming to stderr."), - cl::cat(ClangRenameCategory)); -static cl::opt -ExportFixes( - "export-fixes", - cl::desc("YAML file to store suggested fixes in."), - cl::value_desc("filename"), - cl::cat(ClangRenameCategory)); - using namespace clang; -const char RenameUsage[] = "A tool to rename symbols in C/C++ code.\n\ +cl::OptionCategory ClangRenameAtCategory("clang-rename rename-at options"); +cl::OptionCategory ClangRenameAllCategory("clang-rename rename-all options"); + +const char RenameAtUsage[] = "A tool to rename symbols in C/C++ code.\n\ clang-rename renames every occurrence of a symbol found at in\n\ . If -i is specified, the edited files are overwritten to disk.\n\ Otherwise, the results are written to stdout.\n"; +const char RenameAllUsage[] = "A tool to rename symbols in C/C++ code.\n\ +clang-rename renames every occurrence of a symbol named .\n"; + +static int renameAtMain(int argc, const char *argv[]); +static int renameAllMain(int argc, const char *argv[]); +static int helpMain(int argc, const char *argv[]); + +/// \brief An oldname -> newname rename. +struct RenameAllInfo { + std::string OldName; + unsigned Offset; + std::string NewName; + + RenameAllInfo() : Offset(0) {} +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(RenameAllInfo) + +namespace llvm { +namespace yaml { + +/// \brief Specialized MappingTraits to describe how a RenameAllInfo is / +/// (de)serialized. +template <> struct MappingTraits { + static void mapping(IO &IO, RenameAllInfo &Info) { + IO.mapOptional("OldName", Info.OldName); + IO.mapOptional("Offset", Info.Offset); + IO.mapRequired("NewName", Info.NewName); + } +}; + +} // end namespace yaml +} // end namespace llvm + int main(int argc, const char **argv) { - tooling::CommonOptionsParser OP(argc, argv, ClangRenameCategory, RenameUsage); + if (argc > 1) { + using MainFunction = std::function; + MainFunction Func = StringSwitch(argv[1]) + .Case("rename-at", renameAtMain) + .Case("rename-all", renameAllMain) + .Cases("-help", "--help", helpMain) + .Default(nullptr); + + if (Func) { + std::string Invocation = std::string(argv[0]) + " " + argv[1]; + argv[1] = Invocation.c_str(); + return Func(argc - 1, argv + 1); + } else { + return renameAtMain(argc, argv); + } + } + + helpMain(argc, argv); + return 1; +} + +int subcommandMain(bool isRenameAll, int argc, const char **argv) { + cl::OptionCategory *Category = nullptr; + const char *Usage = nullptr; + if (isRenameAll) { + Category = &ClangRenameAllCategory; + Usage = RenameAllUsage; + } else { + Category = &ClangRenameAtCategory; + Usage = RenameAtUsage; + } + + cl::list NewNames( + "new-name", cl::desc("The new name to change the symbol to."), + (isRenameAll ? cl::ZeroOrMore : cl::Required), cl::cat(*Category)); + cl::list SymbolOffsets( + "offset", + cl::desc("Locates the symbol by offset as opposed to :."), + (isRenameAll ? cl::ZeroOrMore : cl::Required), cl::cat(*Category)); + cl::list OldNames( + "old-name", + cl::desc( + "The fully qualified name of the symbol, if -offset is not used."), + (isRenameAll ? cl::ZeroOrMore : cl::Optional), + cl::cat(ClangRenameAllCategory)); + cl::opt Inplace("i", cl::desc("Overwrite edited s."), + cl::cat(*Category)); + cl::opt PrintName( + "pn", + cl::desc("Print the found symbol's name prior to renaming to stderr."), + cl::cat(ClangRenameAtCategory)); + cl::opt PrintLocations( + "pl", cl::desc("Print the locations affected by renaming to stderr."), + cl::cat(ClangRenameAtCategory)); + cl::opt ExportFixes( + "export-fixes", cl::desc("YAML file to store suggested fixes in."), + cl::value_desc("filename"), cl::cat(*Category)); + cl::opt Input( + "input", cl::desc("YAML file to load oldname-newname pairs from."), + cl::Optional, cl::cat(ClangRenameAllCategory)); + + tooling::CommonOptionsParser OP(argc, argv, *Category, Usage); + + if (!Input.empty()) { + // Populate OldNames and NewNames from a YAML file. + auto Buffer = llvm::MemoryBuffer::getFile(Input); + if (!Buffer) { + errs() << "clang-rename: failed to read " << Input << ": " + << Buffer.getError().message() << "\n"; + exit(1); + } + + std::vector Infos; + llvm::yaml::Input YAML(Buffer.get()->getBuffer()); + YAML >> Infos; + for (const auto &Info : Infos) { + if (!Info.OldName.empty()) + OldNames.push_back(Info.OldName); + else + SymbolOffsets.push_back(Info.Offset); + NewNames.push_back(Info.NewName); + } + } // Check the arguments for correctness. - if (NewName.empty()) { - errs() << "ERROR: no new name provided.\n\n"; + if (NewNames.empty()) { + errs() << "clang-rename: either -new-name or -input is required.\n\n"; + exit(1); + } + + // Check if NewNames is a valid identifier in C++17. + for (const auto &NewName : NewNames) { + LangOptions Options; + Options.CPlusPlus = true; + Options.CPlusPlus1z = true; + IdentifierTable Table(Options); + auto NewNameTokKind = Table.get(NewName).getTokenID(); + if (!tok::isAnyIdentifier(NewNameTokKind)) { + errs() << "ERROR: new name is not a valid identifier in C++17.\n\n"; + exit(1); + } + } + + if (!OldNames.empty() && OldNames.size() != NewNames.size()) { + errs() << "clang-rename: number of old names (" << OldNames.size() + << ") do not equal to number of new names (" << NewNames.size() + << ").\n\n"; + cl::PrintHelpMessage(); exit(1); } - // Check if NewName is a valid identifier in C++17. - LangOptions Options; - Options.CPlusPlus = true; - Options.CPlusPlus1z = true; - IdentifierTable Table(Options); - auto NewNameTokKind = Table.get(NewName).getTokenID(); - if (!tok::isAnyIdentifier(NewNameTokKind)) { - errs() << "ERROR: new name is not a valid identifier in C++17.\n\n"; + if (!SymbolOffsets.empty() && SymbolOffsets.size() != NewNames.size()) { + errs() << "clang-rename: number of symbol offsets (" << SymbolOffsets.size() + << ") do not equal to number of new names (" << NewNames.size() + << ").\n\n"; + cl::PrintHelpMessage(); exit(1); } - // Get the USRs. + std::vector> USRList; + std::vector PrevNames; auto Files = OP.getSourcePathList(); tooling::RefactoringTool Tool(OP.getCompilations(), Files); - rename::USRFindingAction USRAction(SymbolOffset, OldName); + unsigned Count = OldNames.size() ? OldNames.size() : SymbolOffsets.size(); + for (unsigned I = 0; I < Count; ++I) { + unsigned SymbolOffset = SymbolOffsets.empty() ? 0 : SymbolOffsets[I]; + const std::string &OldName = OldNames.empty() ? std::string() : OldNames[I]; - // Find the USRs. - Tool.run(tooling::newFrontendActionFactory(&USRAction).get()); - const auto &USRs = USRAction.getUSRs(); - const auto &PrevName = USRAction.getUSRSpelling(); + // Get the USRs. + rename::USRFindingAction USRAction(SymbolOffset, OldName); - if (PrevName.empty()) { - // An error should have already been printed. - exit(1); - } + // Find the USRs. + Tool.run(tooling::newFrontendActionFactory(&USRAction).get()); + const auto &USRs = USRAction.getUSRs(); + USRList.push_back(USRs); + const auto &PrevName = USRAction.getUSRSpelling(); + PrevNames.push_back(PrevName); + + if (PrevName.empty()) { + // An error should have already been printed. + exit(1); + } - if (PrintName) { - errs() << "clang-rename: found name: " << PrevName << '\n'; + if (PrintName) { + errs() << "clang-rename: found name: " << PrevName << '\n'; + } } // Perform the renaming. - rename::RenamingAction RenameAction(NewName, PrevName, USRs, + rename::RenamingAction RenameAction(NewNames, PrevNames, USRList, Tool.getReplacements(), PrintLocations); auto Factory = tooling::newFrontendActionFactory(&RenameAction); int ExitCode; @@ -146,9 +254,10 @@ // Export replacements. tooling::TranslationUnitReplacements TUR; - const tooling::Replacements &Replacements = Tool.getReplacements(); - TUR.Replacements.insert(TUR.Replacements.end(), Replacements.begin(), - Replacements.end()); + const auto &FileToReplacements = Tool.getReplacements(); + for (const auto &Entry : FileToReplacements) + TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(), + Entry.second.end()); yaml::Output YAML(OS); YAML << TUR; @@ -160,12 +269,11 @@ // indication of which files start where, other than that we print the files // in the same order we see them. LangOptions DefaultLangOptions; - IntrusiveRefCntPtr DiagOpts = - new DiagnosticOptions(); + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts); DiagnosticsEngine Diagnostics( - IntrusiveRefCntPtr(new DiagnosticIDs()), - &*DiagOpts, &DiagnosticPrinter, false); + IntrusiveRefCntPtr(new DiagnosticIDs()), &*DiagOpts, + &DiagnosticPrinter, false); auto &FileMgr = Tool.getFiles(); SourceManager Sources(Diagnostics, FileMgr); Rewriter Rewrite(Sources, DefaultLangOptions); @@ -180,3 +288,24 @@ exit(ExitCode); } + +/// \brief Top level help. +/// FIXME It would be better if this could be auto-generated. +static int helpMain(int argc, const char *argv[]) { + errs() << "Usage: clang-rename {rename-at|rename-all} [OPTION]...\n\n" + "A tool to rename symbols in C/C++ code.\n\n" + "Subcommands:\n" + " rename-at: Perform rename off of a location in a file. (This " + "is the default.)\n" + " rename-all: Perform rename of all symbols matching one or more " + "fully qualified names.\n"; + return 0; +} + +static int renameAtMain(int argc, const char *argv[]) { + return subcommandMain(false, argc, argv); +} + +static int renameAllMain(int argc, const char *argv[]) { + return subcommandMain(true, argc, argv); +} Index: clang-rename/tool/clang-rename.el =================================================================== --- /dev/null +++ clang-rename/tool/clang-rename.el @@ -0,0 +1,44 @@ +;;; clang-rename.el --- Renames every occurrence of a symbol found at . + +;; Keywords: tools, c + +;;; Commentary: + +;; To install clang-rename.el make sure the directory of this file is in your +;; 'load-path' and add +;; +;; (require 'clang-rename) +;; +;; to your .emacs configuration. + +;;; Code: + +(defcustom clang-rename-binary "clang-rename" + "Path to clang-rename executable." + :type 'hook + :options '(turn-on-auto-fill flyspell-mode) + :group 'wp) + +(defun clang-rename (new-name) + "Rename all instances of the symbol at the point using clang-rename" + (interactive "sEnter a new name: ") + (let (;; Emacs offset is 1-based. + (offset (- (point) 1)) + (orig-buf (current-buffer)) + (file-name (buffer-file-name))) + + (let ((rename-command + (format "bash -f -c '%s -offset=%s -new-name=%s -i %s'" + clang-rename-binary offset new-name file-name))) + (message (format "Running clang-rename command %s" rename-command)) + ;; Run clang-rename via bash. + (shell-command rename-command) + ;; Reload buffer. + (revert-buffer t t) + ) + ) +) + +(provide 'clang-rename) + +;;; clang-rename.el ends here Index: clang-tidy/CMakeLists.txt =================================================================== --- clang-tidy/CMakeLists.txt +++ clang-tidy/CMakeLists.txt @@ -34,6 +34,7 @@ add_subdirectory(google) add_subdirectory(misc) add_subdirectory(modernize) +add_subdirectory(mpi) add_subdirectory(performance) add_subdirectory(readability) add_subdirectory(utils) Index: clang-tidy/ClangTidy.h =================================================================== --- clang-tidy/ClangTidy.h +++ clang-tidy/ClangTidy.h @@ -73,6 +73,23 @@ return Result; } + /// \brief Read a named option from the ``Context`` and parse it as an + /// integral type ``T``. + /// + /// Reads the option with the check-local name \p LocalName from local or + /// global ``CheckOptions``. Gets local option first. If local is not present, + /// falls back to get global option. If global option is not present either, + /// returns Default. + template + typename std::enable_if::value, T>::type + getLocalOrGlobal(StringRef LocalName, T Default) const { + std::string Value = getLocalOrGlobal(LocalName, ""); + T Result = Default; + if (!Value.empty()) + StringRef(Value).getAsInteger(10, Result); + return Result; + } + /// \brief Stores an option with the check-local name \p LocalName with string /// value \p Value to \p Options. void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, Index: clang-tidy/ClangTidy.cpp =================================================================== --- clang-tidy/ClangTidy.cpp +++ clang-tidy/ClangTidy.cpp @@ -47,7 +47,7 @@ using namespace clang::tooling; using namespace llvm; -template class llvm::Registry; +LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry) namespace clang { namespace tidy { @@ -126,27 +126,32 @@ } auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]")) << Message.Message << Name; - for (const tooling::Replacement &Fix : Error.Fix) { - // Retrieve the source range for applicable fixes. Macro definitions - // on the command line have locations in a virtual buffer and don't - // have valid file paths and are therefore not applicable. - SourceRange Range; - SourceLocation FixLoc; - if (Fix.isApplicable()) { - SmallString<128> FixAbsoluteFilePath = Fix.getFilePath(); - Files.makeAbsolutePath(FixAbsoluteFilePath); - FixLoc = getLocation(FixAbsoluteFilePath, Fix.getOffset()); - SourceLocation FixEndLoc = FixLoc.getLocWithOffset(Fix.getLength()); - Range = SourceRange(FixLoc, FixEndLoc); - Diag << FixItHint::CreateReplacement(Range, Fix.getReplacementText()); - } - - ++TotalFixes; - if (ApplyFixes) { - bool Success = Fix.isApplicable() && Fix.apply(Rewrite); - if (Success) - ++AppliedFixes; - FixLocations.push_back(std::make_pair(FixLoc, Success)); + for (const auto &FileAndReplacements : Error.Fix) { + for (const auto &Replacement : FileAndReplacements.second) { + // Retrieve the source range for applicable fixes. Macro definitions + // on the command line have locations in a virtual buffer and don't + // have valid file paths and are therefore not applicable. + SourceRange Range; + SourceLocation FixLoc; + if (Replacement.isApplicable()) { + SmallString<128> FixAbsoluteFilePath = Replacement.getFilePath(); + Files.makeAbsolutePath(FixAbsoluteFilePath); + FixLoc = getLocation(FixAbsoluteFilePath, Replacement.getOffset()); + SourceLocation FixEndLoc = + FixLoc.getLocWithOffset(Replacement.getLength()); + Range = SourceRange(FixLoc, FixEndLoc); + Diag << FixItHint::CreateReplacement( + Range, Replacement.getReplacementText()); + } + + ++TotalFixes; + if (ApplyFixes) { + bool Success = + Replacement.isApplicable() && Replacement.apply(Rewrite); + if (Success) + ++AppliedFixes; + FixLocations.push_back(std::make_pair(FixLoc, Success)); + } } } } @@ -511,9 +516,12 @@ void exportReplacements(const std::vector &Errors, raw_ostream &OS) { tooling::TranslationUnitReplacements TUR; - for (const ClangTidyError &Error : Errors) - TUR.Replacements.insert(TUR.Replacements.end(), Error.Fix.begin(), - Error.Fix.end()); + for (const ClangTidyError &Error : Errors) { + for (const auto &FileAndFixes : Error.Fix) + TUR.Replacements.insert(TUR.Replacements.end(), + FileAndFixes.second.begin(), + FileAndFixes.second.end()); + } yaml::Output YAML(OS); YAML << TUR; Index: clang-tidy/ClangTidyDiagnosticConsumer.h =================================================================== --- clang-tidy/ClangTidyDiagnosticConsumer.h +++ clang-tidy/ClangTidyDiagnosticConsumer.h @@ -62,7 +62,8 @@ std::string CheckName; ClangTidyMessage Message; - tooling::Replacements Fix; + // Fixes grouped by file path. + llvm::StringMap Fix; SmallVector Notes; // A build directory of the diagnostic source file. Index: clang-tidy/ClangTidyDiagnosticConsumer.cpp =================================================================== --- clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -77,7 +77,14 @@ assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() && "Only file locations supported in fix-it hints."); - Error.Fix.insert(tooling::Replacement(SM, Range, FixIt.CodeToInsert)); + tooling::Replacement Replacement(SM, Range, FixIt.CodeToInsert); + llvm::Error Err = Error.Fix[Replacement.getFilePath()].add(Replacement); + // FIXME: better error handling. + if (Err) { + llvm::errs() << "Fix conflicts with existing fix! " + << llvm::toString(std::move(Err)) << "\n"; + } + assert(!Err && "Fix conflicts with existing fix!"); } } @@ -426,7 +433,7 @@ // replacements. To detect overlapping replacements, we use a sweep line // algorithm over these sets of intervals. // An event here consists of the opening or closing of an interval. During the - // proccess, we maintain a counter with the amount of open intervals. If we + // process, we maintain a counter with the amount of open intervals. If we // find an endpoint of an interval and this counter is different from 0, it // means that this interval overlaps with another one, so we set it as // inapplicable. @@ -448,7 +455,7 @@ // priority than begin events. // // * If we have several begin points at the same position, we will mark as - // inapplicable the ones that we proccess later, so the first one has to + // inapplicable the ones that we process later, so the first one has to // be the one with the latest end point, because this one will contain // all the other intervals. For the same reason, if we have several end // points in the same position, the last one has to be the one with the @@ -456,14 +463,14 @@ // position of the complementary. // // * In case of two equal intervals, the one whose error is bigger can - // potentially contain the other one, so we want to proccess its begin + // potentially contain the other one, so we want to process its begin // points before and its end points later. // // * Finally, if we have two equal intervals whose errors have the same // size, none of them will be strictly contained inside the other. // Sorting by ErrorId will guarantee that the begin point of the first - // one will be proccessed before, disallowing the second one, and the - // end point of the first one will also be proccessed before, + // one will be processed before, disallowing the second one, and the + // end point of the first one will also be processed before, // disallowing the first one. if (Type == ET_Begin) Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId); @@ -488,25 +495,29 @@ std::vector Sizes; for (const auto &Error : Errors) { int Size = 0; - for (const auto &Replace : Error.Fix) - Size += Replace.getLength(); + for (const auto &FileAndReplaces : Error.Fix) { + for (const auto &Replace : FileAndReplaces.second) + Size += Replace.getLength(); + } Sizes.push_back(Size); } // Build events from error intervals. std::map> FileEvents; for (unsigned I = 0; I < Errors.size(); ++I) { - for (const auto &Replace : Errors[I].Fix) { - unsigned Begin = Replace.getOffset(); - unsigned End = Begin + Replace.getLength(); - const std::string &FilePath = Replace.getFilePath(); - // FIXME: Handle empty intervals, such as those from insertions. - if (Begin == End) - continue; - FileEvents[FilePath].push_back( - Event(Begin, End, Event::ET_Begin, I, Sizes[I])); - FileEvents[FilePath].push_back( - Event(Begin, End, Event::ET_End, I, Sizes[I])); + for (const auto &FileAndReplace : Errors[I].Fix) { + for (const auto &Replace : FileAndReplace.second) { + unsigned Begin = Replace.getOffset(); + unsigned End = Begin + Replace.getLength(); + const std::string &FilePath = Replace.getFilePath(); + // FIXME: Handle empty intervals, such as those from insertions. + if (Begin == End) + continue; + FileEvents[FilePath].push_back( + Event(Begin, End, Event::ET_Begin, I, Sizes[I])); + FileEvents[FilePath].push_back( + Event(Begin, End, Event::ET_End, I, Sizes[I])); + } } } Index: clang-tidy/ClangTidyModuleRegistry.h =================================================================== --- clang-tidy/ClangTidyModuleRegistry.h +++ clang-tidy/ClangTidyModuleRegistry.h @@ -13,8 +13,6 @@ #include "ClangTidyModule.h" #include "llvm/Support/Registry.h" -extern template class llvm::Registry; - namespace clang { namespace tidy { Index: clang-tidy/cert/LICENSE.TXT =================================================================== --- clang-tidy/cert/LICENSE.TXT +++ clang-tidy/cert/LICENSE.TXT @@ -7,7 +7,7 @@ Any file referencing a CERT Secure Coding guideline: Please allow this letter to serve as confirmation that open source projects on http://llvm.org are permitted to link via hypertext to the CERT(R) secure coding -guidelines available at https://www.securecoding.cert.org. +guidelines available at https://www.securecoding.cert.org. The foregoing is permitted by the Terms of Use as follows: "Linking to the Service Index: clang-tidy/cert/StrToNumCheck.cpp =================================================================== --- clang-tidy/cert/StrToNumCheck.cpp +++ clang-tidy/cert/StrToNumCheck.cpp @@ -127,7 +127,7 @@ Handler H; analyze_format_string::ParseScanfString(H, Fmt.begin(), Fmt.end(), LO, TI); - + return H.get(); } Index: clang-tidy/cppcoreguidelines/CMakeLists.txt =================================================================== --- clang-tidy/cppcoreguidelines/CMakeLists.txt +++ clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -13,6 +13,7 @@ ProTypeStaticCastDowncastCheck.cpp ProTypeUnionAccessCheck.cpp ProTypeVarargCheck.cpp + SpecialMemberFunctionsCheck.cpp SlicingCheck.cpp LINK_LIBS Index: clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp =================================================================== --- clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -22,6 +22,7 @@ #include "ProTypeStaticCastDowncastCheck.h" #include "ProTypeUnionAccessCheck.h" #include "ProTypeVarargCheck.h" +#include "SpecialMemberFunctionsCheck.h" #include "SlicingCheck.h" namespace clang { @@ -54,6 +55,8 @@ "cppcoreguidelines-pro-type-union-access"); CheckFactories.registerCheck( "cppcoreguidelines-pro-type-vararg"); + CheckFactories.registerCheck( + "cppcoreguidelines-special-member-functions"); CheckFactories.registerCheck( "cppcoreguidelines-slicing"); CheckFactories.registerCheck( Index: clang-tidy/cppcoreguidelines/ProBoundsArrayToPointerDecayCheck.h =================================================================== --- clang-tidy/cppcoreguidelines/ProBoundsArrayToPointerDecayCheck.h +++ clang-tidy/cppcoreguidelines/ProBoundsArrayToPointerDecayCheck.h @@ -15,7 +15,7 @@ namespace clang { namespace tidy { namespace cppcoreguidelines { - + /// This check flags all array to pointer decays /// /// For the user-facing documentation see: Index: clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.h =================================================================== --- clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.h +++ clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.h @@ -15,7 +15,7 @@ namespace clang { namespace tidy { namespace cppcoreguidelines { - + /// This check flags all instances of const_cast /// /// For the user-facing documentation see: Index: clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.cpp =================================================================== --- clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.cpp +++ clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.cpp @@ -16,7 +16,7 @@ namespace clang { namespace tidy { namespace cppcoreguidelines { - + void ProTypeConstCastCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Index: clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.h =================================================================== --- clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.h +++ clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.h @@ -15,7 +15,7 @@ namespace clang { namespace tidy { namespace cppcoreguidelines { - + /// Checks for usages of static_cast, where a base class is downcasted to a derived class. /// /// For the user-facing documentation see: Index: clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.cpp =================================================================== --- clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.cpp +++ clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.cpp @@ -16,7 +16,7 @@ namespace clang { namespace tidy { namespace cppcoreguidelines { - + void ProTypeStaticCastDowncastCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; Index: clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h =================================================================== --- /dev/null +++ clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h @@ -0,0 +1,97 @@ +//===--- SpecialMemberFunctionsCheck.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_CPPCOREGUIDELINES_SPECIAL_MEMBER_FUNCTIONS_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SPECIAL_MEMBER_FUNCTIONS_H + +#include "../ClangTidy.h" + +#include "llvm/ADT/DenseMapInfo.h" + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +/// Checks for classes where some, but not all, of the special member functions +/// are defined. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-special-member-functions.html +class SpecialMemberFunctionsCheck : public ClangTidyCheck { +public: + 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 { + Destructor, + CopyConstructor, + CopyAssignment, + MoveConstructor, + MoveAssignment + }; + + using ClassDefId = std::pair; + + using ClassDefiningSpecialMembersMap = + llvm::DenseMap>; + +private: + ClassDefiningSpecialMembersMap ClassWithSpecialMembers; +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +namespace llvm { +/// Specialisation of DenseMapInfo to allow ClassDefId objects in DenseMaps +/// FIXME: Move this to the corresponding cpp file as is done for +/// clang-tidy/readability/IdentifierNamingCheck.cpp. +template <> +struct DenseMapInfo< + clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::ClassDefId> { + using ClassDefId = + clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::ClassDefId; + + static inline ClassDefId getEmptyKey() { + return ClassDefId( + clang::SourceLocation::getFromRawEncoding(static_cast(-1)), + "EMPTY"); + } + + static inline ClassDefId getTombstoneKey() { + return ClassDefId( + clang::SourceLocation::getFromRawEncoding(static_cast(-2)), + "TOMBSTONE"); + } + + static unsigned getHashValue(ClassDefId Val) { + assert(Val != getEmptyKey() && "Cannot hash the empty key!"); + assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!"); + + std::hash SecondHash; + return Val.first.getRawEncoding() + SecondHash(Val.second); + } + + static bool isEqual(ClassDefId LHS, ClassDefId RHS) { + if (RHS == getEmptyKey()) + return LHS == getEmptyKey(); + if (RHS == getTombstoneKey()) + return LHS == getTombstoneKey(); + return LHS == RHS; + } +}; + +} // namespace llvm + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SPECIAL_MEMBER_FUNCTIONS_H Index: clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp @@ -0,0 +1,134 @@ +//===--- SpecialMemberFunctionsCheck.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 "SpecialMemberFunctionsCheck.h" + +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/StringExtras.h" + +#define DEBUG_TYPE "clang-tidy" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + Finder->addMatcher( + cxxRecordDecl( + eachOf( + has(cxxDestructorDecl(unless(isImplicit())).bind("dtor")), + has(cxxConstructorDecl(isCopyConstructor(), unless(isImplicit())) + .bind("copy-ctor")), + has(cxxMethodDecl(isCopyAssignmentOperator(), + unless(isImplicit())) + .bind("copy-assign")), + has(cxxConstructorDecl(isMoveConstructor(), unless(isImplicit())) + .bind("move-ctor")), + has(cxxMethodDecl(isMoveAssignmentOperator(), + unless(isImplicit())) + .bind("move-assign")))) + .bind("class-def"), + this); +} + +static llvm::StringRef +toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K) { + switch (K) { + case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::Destructor: + return "a destructor"; + case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor: + return "a copy constructor"; + case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment: + return "a copy assignment operator"; + case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveConstructor: + return "a move constructor"; + case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveAssignment: + return "a move assignment operator"; + } + llvm_unreachable("Unhandled SpecialMemberFunctionKind"); +} + +static std::string +join(ArrayRef SMFS, + llvm::StringRef AndOr) { + + assert(!SMFS.empty() && + "List of defined or undefined members should never be empty."); + std::string Buffer; + llvm::raw_string_ostream Stream(Buffer); + + Stream << toString(SMFS[0]); + size_t LastIndex = SMFS.size() - 1; + for (size_t i = 1; i < LastIndex; ++i) { + Stream << ", " << toString(SMFS[i]); + } + if (LastIndex != 0) { + Stream << AndOr << toString(SMFS[LastIndex]); + } + return Stream.str(); +} + +void SpecialMemberFunctionsCheck::check( + const MatchFinder::MatchResult &Result) { + const CXXRecordDecl *MatchedDecl = + Result.Nodes.getNodeAs("class-def"); + if (!MatchedDecl) + return; + + ClassDefId ID(MatchedDecl->getLocation(), MatchedDecl->getName()); + + std::initializer_list> + 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)) + ClassWithSpecialMembers[ID].insert(KV.second); +} + +void SpecialMemberFunctionsCheck::onEndOfTranslationUnit() { + llvm::SmallVector AllSpecialMembers = { + SpecialMemberFunctionKind::Destructor, + SpecialMemberFunctionKind::CopyConstructor, + SpecialMemberFunctionKind::CopyAssignment}; + + if (getLangOpts().CPlusPlus11) { + AllSpecialMembers.push_back(SpecialMemberFunctionKind::MoveConstructor); + AllSpecialMembers.push_back(SpecialMemberFunctionKind::MoveAssignment); + } + + for (const auto &C : ClassWithSpecialMembers) { + const auto &DefinedSpecialMembers = C.second; + + if (DefinedSpecialMembers.size() == AllSpecialMembers.size()) + continue; + + 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.getArrayRef(), " and ") + << join(UndefinedSpecialMembers, " or "); + } +} +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang Index: clang-tidy/misc/ArgumentCommentCheck.h =================================================================== --- clang-tidy/misc/ArgumentCommentCheck.h +++ clang-tidy/misc/ArgumentCommentCheck.h @@ -37,8 +37,10 @@ void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap& Opts) override; private: + const bool StrictMode; llvm::Regex IdentRE; bool isLikelyTypo(llvm::ArrayRef Params, StringRef ArgName, Index: clang-tidy/misc/ArgumentCommentCheck.cpp =================================================================== --- clang-tidy/misc/ArgumentCommentCheck.cpp +++ clang-tidy/misc/ArgumentCommentCheck.cpp @@ -22,16 +22,21 @@ ArgumentCommentCheck::ArgumentCommentCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), + StrictMode(Options.getLocalOrGlobal("StrictMode", 0) != 0), IdentRE("^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {} +void ArgumentCommentCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "StrictMode", StrictMode); +} + void ArgumentCommentCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( callExpr(unless(cxxOperatorCallExpr()), // NewCallback's arguments relate to the pointed function, don't // check them against NewCallback's parameter names. // FIXME: Make this configurable. - unless(hasDeclaration(functionDecl(anyOf( - hasName("NewCallback"), hasName("NewPermanentCallback")))))) + unless(hasDeclaration(functionDecl( + hasAnyName("NewCallback", "NewPermanentCallback"))))) .bind("expr"), this); Finder->addMatcher(cxxConstructExpr().bind("expr"), this); @@ -78,12 +83,13 @@ return Comments; } -bool -ArgumentCommentCheck::isLikelyTypo(llvm::ArrayRef Params, - StringRef ArgName, unsigned ArgIndex) { - std::string ArgNameLower = ArgName.lower(); +bool ArgumentCommentCheck::isLikelyTypo(llvm::ArrayRef Params, + StringRef ArgName, unsigned ArgIndex) { + std::string ArgNameLowerStr = ArgName.lower(); + StringRef ArgNameLower = ArgNameLowerStr; + // The threshold is arbitrary. unsigned UpperBound = (ArgName.size() + 2) / 3 + 1; - unsigned ThisED = StringRef(ArgNameLower).edit_distance( + unsigned ThisED = ArgNameLower.edit_distance( Params[ArgIndex]->getIdentifier()->getName().lower(), /*AllowReplacements=*/true, UpperBound); if (ThisED >= UpperBound) @@ -100,9 +106,9 @@ // Other parameters must be an edit distance at least Threshold more away // from this parameter. This gives us greater confidence that this is a typo // of this parameter and not one with a similar name. - unsigned OtherED = StringRef(ArgNameLower).edit_distance( - II->getName().lower(), - /*AllowReplacements=*/true, ThisED + Threshold); + unsigned OtherED = ArgNameLower.edit_distance(II->getName().lower(), + /*AllowReplacements=*/true, + ThisED + Threshold); if (OtherED < ThisED + Threshold) return false; } @@ -110,15 +116,24 @@ return true; } +static bool sameName(StringRef InComment, StringRef InDecl, bool StrictMode) { + if (StrictMode) + return InComment == InDecl; + InComment = InComment.trim('_'); + InDecl = InDecl.trim('_'); + // FIXME: compare_lower only works for ASCII. + return InComment.compare_lower(InDecl) == 0; +} + void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx, const FunctionDecl *Callee, SourceLocation ArgBeginLoc, llvm::ArrayRef Args) { Callee = Callee->getFirstDecl(); - for (unsigned i = 0, - e = std::min(Args.size(), Callee->getNumParams()); - i != e; ++i) { - const ParmVarDecl *PVD = Callee->getParamDecl(i); + for (unsigned I = 0, + E = std::min(Args.size(), Callee->getNumParams()); + I != E; ++I) { + const ParmVarDecl *PVD = Callee->getParamDecl(I); IdentifierInfo *II = PVD->getIdentifier(); if (!II) continue; @@ -126,28 +141,28 @@ // Don't warn on arguments for parameters instantiated from template // parameter packs. If we find more arguments than the template definition // has, it also means that they correspond to a parameter pack. - if (Template->getNumParams() <= i || - Template->getParamDecl(i)->isParameterPack()) { + if (Template->getNumParams() <= I || + Template->getParamDecl(I)->isParameterPack()) { continue; } } CharSourceRange BeforeArgument = CharSourceRange::getCharRange( - i == 0 ? ArgBeginLoc : Args[i - 1]->getLocEnd(), - Args[i]->getLocStart()); + I == 0 ? ArgBeginLoc : Args[I - 1]->getLocEnd(), + Args[I]->getLocStart()); BeforeArgument = Lexer::makeFileCharRange( BeforeArgument, Ctx->getSourceManager(), Ctx->getLangOpts()); for (auto Comment : getCommentsInRange(Ctx, BeforeArgument)) { llvm::SmallVector Matches; if (IdentRE.match(Comment.second, &Matches)) { - if (Matches[2] != II->getName()) { + if (!sameName(Matches[2], II->getName(), StrictMode)) { { DiagnosticBuilder Diag = diag(Comment.first, "argument name '%0' in comment does not " "match parameter name %1") << Matches[2] << II; - if (isLikelyTypo(Callee->parameters(), Matches[2], i)) { + if (isLikelyTypo(Callee->parameters(), Matches[2], I)) { Diag << FixItHint::CreateReplacement( Comment.first, (Matches[1] + II->getName() + Matches[3]).str()); @@ -163,7 +178,7 @@ void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) { const Expr *E = Result.Nodes.getNodeAs("expr"); - if (auto Call = dyn_cast(E)) { + if (const auto *Call = dyn_cast(E)) { const FunctionDecl *Callee = Call->getDirectCallee(); if (!Callee) return; @@ -171,7 +186,7 @@ checkCallArgs(Result.Context, Callee, Call->getCallee()->getLocEnd(), llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs())); } else { - auto Construct = cast(E); + const auto *Construct = cast(E); checkCallArgs( Result.Context, Construct->getConstructor(), Construct->getParenOrBraceRange().getBegin(), Index: clang-tidy/misc/MacroParenthesesCheck.cpp =================================================================== --- clang-tidy/misc/MacroParenthesesCheck.cpp +++ clang-tidy/misc/MacroParenthesesCheck.cpp @@ -154,7 +154,7 @@ void MacroParenthesesPPCallbacks::argument(const Token &MacroNameTok, const MacroInfo *MI) { - + // Skip variable declaration. bool VarDecl = possibleVarDecl(MI, MI->tokens_begin()); Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -120,7 +120,7 @@ CheckFactories.registerCheck( "misc-suspicious-semicolon"); CheckFactories.registerCheck( - "misc-suspicious-string-compare"); + "misc-suspicious-string-compare"); CheckFactories.registerCheck( "misc-swapped-arguments"); CheckFactories.registerCheck( Index: clang-tidy/misc/MoveConstructorInitCheck.cpp =================================================================== --- clang-tidy/misc/MoveConstructorInitCheck.cpp +++ clang-tidy/misc/MoveConstructorInitCheck.cpp @@ -133,7 +133,7 @@ QualType QT = Initializer->getInit()->getType(); if (QT.isTriviallyCopyableType(*Result.Context)) return; - + const auto *RD = QT->getAsCXXRecordDecl(); if (RD && RD->isTriviallyCopyable()) return; Index: clang-tidy/misc/SizeofContainerCheck.cpp =================================================================== --- clang-tidy/misc/SizeofContainerCheck.cpp +++ clang-tidy/misc/SizeofContainerCheck.cpp @@ -15,7 +15,7 @@ namespace clang { namespace tidy { -namespace misc { +namespace misc { void SizeofContainerCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( Index: clang-tidy/misc/StaticAssertCheck.h =================================================================== --- clang-tidy/misc/StaticAssertCheck.h +++ clang-tidy/misc/StaticAssertCheck.h @@ -16,7 +16,7 @@ namespace clang { namespace tidy { -namespace misc { +namespace misc { /// Replaces `assert()` with `static_assert()` if the condition is evaluatable /// at compile time. Index: clang-tidy/misc/StaticAssertCheck.cpp =================================================================== --- clang-tidy/misc/StaticAssertCheck.cpp +++ clang-tidy/misc/StaticAssertCheck.cpp @@ -22,7 +22,7 @@ namespace clang { namespace tidy { -namespace misc { +namespace misc { StaticAssertCheck::StaticAssertCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} Index: clang-tidy/misc/StringIntegerAssignmentCheck.h =================================================================== --- clang-tidy/misc/StringIntegerAssignmentCheck.h +++ clang-tidy/misc/StringIntegerAssignmentCheck.h @@ -14,7 +14,7 @@ namespace clang { namespace tidy { -namespace misc { +namespace misc { /// Finds instances where an integer is assigned to a string. /// Index: clang-tidy/misc/StringIntegerAssignmentCheck.cpp =================================================================== --- clang-tidy/misc/StringIntegerAssignmentCheck.cpp +++ clang-tidy/misc/StringIntegerAssignmentCheck.cpp @@ -16,7 +16,7 @@ namespace clang { namespace tidy { -namespace misc { +namespace misc { void StringIntegerAssignmentCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) Index: clang-tidy/misc/SuspiciousMissingCommaCheck.h =================================================================== --- clang-tidy/misc/SuspiciousMissingCommaCheck.h +++ clang-tidy/misc/SuspiciousMissingCommaCheck.h @@ -23,7 +23,7 @@ class SuspiciousMissingCommaCheck : public ClangTidyCheck { public: SuspiciousMissingCommaCheck(StringRef Name, ClangTidyContext *Context); - void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; Index: clang-tidy/misc/SuspiciousMissingCommaCheck.cpp =================================================================== --- clang-tidy/misc/SuspiciousMissingCommaCheck.cpp +++ clang-tidy/misc/SuspiciousMissingCommaCheck.cpp @@ -99,7 +99,7 @@ const auto *ConcatenatedLiteral = Result.Nodes.getNodeAs("str"); assert(InitializerList && ConcatenatedLiteral); - + // Skip small arrays as they often generate false-positive. unsigned int Size = InitializerList->getNumInits(); if (Size < SizeThreshold) return; Index: clang-tidy/misc/SwappedArgumentsCheck.cpp =================================================================== --- clang-tidy/misc/SwappedArgumentsCheck.cpp +++ clang-tidy/misc/SwappedArgumentsCheck.cpp @@ -91,7 +91,7 @@ << RHSFrom->getType() << tooling::fixit::createReplacement(*LHS, *RHS, Ctx) << tooling::fixit::createReplacement(*RHS, *LHS, Ctx); - + // Remember that we emitted a warning for this argument. UsedArgs.insert(RHSCast); } Index: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp =================================================================== --- clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp +++ clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp @@ -16,7 +16,7 @@ namespace clang { namespace tidy { -namespace misc { +namespace misc { ThrowByValueCatchByReferenceCheck::ThrowByValueCatchByReferenceCheck( StringRef Name, ClangTidyContext *Context) Index: clang-tidy/misc/UnusedAliasDeclsCheck.h =================================================================== --- clang-tidy/misc/UnusedAliasDeclsCheck.h +++ clang-tidy/misc/UnusedAliasDeclsCheck.h @@ -15,7 +15,7 @@ namespace clang { namespace tidy { -namespace misc { +namespace misc { /// Finds unused namespace alias declarations. class UnusedAliasDeclsCheck : public ClangTidyCheck { Index: clang-tidy/misc/UnusedParametersCheck.h =================================================================== --- clang-tidy/misc/UnusedParametersCheck.h +++ clang-tidy/misc/UnusedParametersCheck.h @@ -14,7 +14,7 @@ namespace clang { namespace tidy { -namespace misc { +namespace misc { /// Finds unused parameters and fixes them, so that `-Wunused-parameter` can be /// turned on. Index: clang-tidy/misc/UnusedUsingDeclsCheck.cpp =================================================================== --- clang-tidy/misc/UnusedUsingDeclsCheck.cpp +++ clang-tidy/misc/UnusedUsingDeclsCheck.cpp @@ -37,6 +37,11 @@ Finder->addMatcher(declRefExpr().bind("used"), this); Finder->addMatcher(callExpr(callee(unresolvedLookupExpr().bind("used"))), this); + Finder->addMatcher( + callExpr(hasDeclaration(functionDecl(hasAnyTemplateArgument( + anyOf(refersToTemplate(templateName().bind("used")), + refersToDeclaration(functionDecl().bind("used"))))))), + this); } void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) { @@ -71,20 +76,27 @@ Contexts.push_back(Context); return; } - // Mark using declarations as used by setting FoundDecls' value to zero. As // the AST is walked in order, usages are only marked after a the // corresponding using declaration has been found. // FIXME: This currently doesn't look at whether the type reference is // actually found with the help of the using declaration. if (const auto *Used = Result.Nodes.getNodeAs("used")) { - if (const auto *Specialization = - dyn_cast(Used)) + if (const auto *FD = dyn_cast(Used)) { + removeFromFoundDecls(FD->getPrimaryTemplate()); + } else if (const auto *Specialization = + dyn_cast(Used)) { Used = Specialization->getSpecializedTemplate(); + } removeFromFoundDecls(Used); return; } + if (const auto *Used = Result.Nodes.getNodeAs("used")) { + removeFromFoundDecls(Used->getAsTemplateDecl()); + return; + } + if (const auto *DRE = Result.Nodes.getNodeAs("used")) { if (const auto *FD = dyn_cast(DRE->getDecl())) { if (const auto *FDT = FD->getPrimaryTemplate()) @@ -109,6 +121,8 @@ } void UnusedUsingDeclsCheck::removeFromFoundDecls(const Decl *D) { + if (!D) + return; // FIXME: Currently, we don't handle the using-decls being used in different // scopes (such as different namespaces, different functions). Instead of // giving an incorrect message, we mark all of them as used. Index: clang-tidy/modernize/LoopConvertUtils.cpp =================================================================== --- clang-tidy/modernize/LoopConvertUtils.cpp +++ clang-tidy/modernize/LoopConvertUtils.cpp @@ -805,6 +805,18 @@ } bool ForLoopIndexUseVisitor::TraverseStmt(Stmt *S) { + // If this is an initialization expression for a lambda capture, prune the + // traversal so that we don't end up diagnosing the contained DeclRefExpr as + // inconsistent usage. No need to record the usage here -- this is done in + // TraverseLambdaCapture(). + if (const auto *LE = dyn_cast_or_null(NextStmtParent)) { + // Any child of a LambdaExpr that isn't the body is an initialization + // expression. + if (S != LE->getBody()) { + return true; + } + } + // All this pointer swapping is a mechanism for tracking immediate parentage // of Stmts. const Stmt *OldNextParent = NextStmtParent; Index: clang-tidy/modernize/MakeSmartPtrCheck.cpp =================================================================== --- clang-tidy/modernize/MakeSmartPtrCheck.cpp +++ clang-tidy/modernize/MakeSmartPtrCheck.cpp @@ -29,13 +29,19 @@ if (!getLangOpts().CPlusPlus11) return; + // It is possible to make smart ptr calling private ctor inside of a member + // function. Change to make_smart_ptr will be invalid. + auto CallsPrivateCtor = has( + ignoringImpCasts(cxxConstructExpr(hasDeclaration(decl(isPrivate()))))); + Finder->addMatcher( cxxBindTemporaryExpr(has(ignoringParenImpCasts( cxxConstructExpr( hasType(getSmartPointerTypeMatcher()), argumentCountIs(1), hasArgument(0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType( - equalsBoundNode(PointerType)))))) + equalsBoundNode(PointerType))))), + unless(CallsPrivateCtor)) .bind(NewExpression))) .bind(ConstructorCall)))), this); Index: clang-tidy/modernize/PassByValueCheck.cpp =================================================================== --- clang-tidy/modernize/PassByValueCheck.cpp +++ clang-tidy/modernize/PassByValueCheck.cpp @@ -183,7 +183,7 @@ // If the parameter is trivial to copy, don't move it. Moving a trivivally // copyable type will cause a problem with misc-move-const-arg - if (ParamDecl->getType().isTriviallyCopyableType(*Result.Context)) + if (ParamDecl->getType().isTriviallyCopyableType(*Result.Context)) return; auto Diag = diag(ParamDecl->getLocStart(), "pass by value and use std::move"); Index: clang-tidy/modernize/UseBoolLiteralsCheck.cpp =================================================================== --- clang-tidy/modernize/UseBoolLiteralsCheck.cpp +++ clang-tidy/modernize/UseBoolLiteralsCheck.cpp @@ -29,6 +29,17 @@ unless(isInTemplateInstantiation()), anyOf(hasParent(explicitCastExpr().bind("cast")), anything())), this); + + Finder->addMatcher( + conditionalOperator( + hasParent(implicitCastExpr( + hasImplicitDestinationType(qualType(booleanType())), + unless(isInTemplateInstantiation()))), + eachOf(hasTrueExpression( + ignoringParenImpCasts(integerLiteral().bind("literal"))), + hasFalseExpression( + ignoringParenImpCasts(integerLiteral().bind("literal"))))), + this); } void UseBoolLiteralsCheck::check(const MatchFinder::MatchResult &Result) { Index: clang-tidy/mpi/CMakeLists.txt =================================================================== --- /dev/null +++ clang-tidy/mpi/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangTidyMPIModule + MPITidyModule.cpp + TypeMismatchCheck.cpp + + LINK_LIBS + clangAST + clangASTMatchers + clangBasic + clangLex + clangTidy + clangTidyUtils + clangTooling + clangStaticAnalyzerCheckers + ) Index: clang-tidy/mpi/MPITidyModule.cpp =================================================================== --- /dev/null +++ clang-tidy/mpi/MPITidyModule.cpp @@ -0,0 +1,37 @@ +//===--- MPITidyModule.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 "TypeMismatchCheck.h" + +namespace clang { +namespace tidy { +namespace mpi { + +class MPIModule : public ClangTidyModule { +public: + void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck("mpi-type-mismatch"); + } +}; + +} // namespace mpi + +// Register the MPITidyModule using this statically initialized variable. +static ClangTidyModuleRegistry::Add + X("mpi-module", "Adds MPI clang-tidy checks."); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the MPIModule. +volatile int MPIModuleAnchorSource = 0; + +} // namespace tidy +} // namespace clang Index: clang-tidy/mpi/TypeMismatchCheck.h =================================================================== --- /dev/null +++ clang-tidy/mpi/TypeMismatchCheck.h @@ -0,0 +1,52 @@ +//===--- TypeMismatchCheck.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_MPI_TYPE_MISMATCH_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_TYPE_MISMATCH_H + +#include "../ClangTidy.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +namespace clang { +namespace tidy { +namespace mpi { + +/// This check verifies if buffer type and MPI (Message Passing Interface) +/// datatype pairs match. All MPI datatypes defined by the MPI standard (3.1) +/// are verified by this check. User defined typedefs, custom MPI datatypes and +/// null pointer constants are skipped, in the course of verification. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/mpi-type-mismatch.html +class TypeMismatchCheck : public ClangTidyCheck { +public: + TypeMismatchCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + /// Check if the buffer type MPI datatype pairs match. + /// + /// \param BufferTypes buffer types + /// \param BufferExprs buffer arguments as expressions + /// \param MPIDatatypes MPI datatype + /// \param LO language options + void checkArguments(ArrayRef BufferTypes, + ArrayRef BufferExprs, + ArrayRef MPIDatatypes, + const LangOptions &LO); +}; + +} // namespace mpi +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_TYPE_MISMATCH_H Index: clang-tidy/mpi/TypeMismatchCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/mpi/TypeMismatchCheck.cpp @@ -0,0 +1,336 @@ +//===--- TypeMismatchCheck.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 "TypeMismatchCheck.h" +#include "clang/Lex/Lexer.h" +#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h" +#include "clang/Tooling/FixIt.h" +#include +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace mpi { + +/// Check if a BuiltinType::Kind matches the MPI datatype. +/// +/// \param MultiMap datatype group +/// \param Kind buffer type kind +/// \param MPIDatatype name of the MPI datatype +/// +/// \returns true if the pair matches +static bool +isMPITypeMatching(const std::multimap &MultiMap, + const BuiltinType::Kind Kind, + const std::string &MPIDatatype) { + auto ItPair = MultiMap.equal_range(Kind); + while (ItPair.first != ItPair.second) { + if (ItPair.first->second == MPIDatatype) + return true; + ++ItPair.first; + } + return false; +} + +/// Check if the MPI datatype is a standard type. +/// +/// \param MPIDatatype name of the MPI datatype +/// +/// \returns true if the type is a standard type +static bool isStandardMPIDatatype(const std::string &MPIDatatype) { + static std::unordered_set AllTypes = { + "MPI_C_BOOL", + "MPI_CHAR", + "MPI_SIGNED_CHAR", + "MPI_UNSIGNED_CHAR", + "MPI_WCHAR", + "MPI_INT", + "MPI_LONG", + "MPI_SHORT", + "MPI_LONG_LONG", + "MPI_LONG_LONG_INT", + "MPI_UNSIGNED", + "MPI_UNSIGNED_SHORT", + "MPI_UNSIGNED_LONG", + "MPI_UNSIGNED_LONG_LONG", + "MPI_FLOAT", + "MPI_DOUBLE", + "MPI_LONG_DOUBLE", + "MPI_C_COMPLEX", + "MPI_C_FLOAT_COMPLEX", + "MPI_C_DOUBLE_COMPLEX", + "MPI_C_LONG_DOUBLE_COMPLEX", + "MPI_INT8_T", + "MPI_INT16_T", + "MPI_INT32_T", + "MPI_INT64_T", + "MPI_UINT8_T", + "MPI_UINT16_T", + "MPI_UINT32_T", + "MPI_UINT64_T", + "MPI_CXX_BOOL", + "MPI_CXX_FLOAT_COMPLEX", + "MPI_CXX_DOUBLE_COMPLEX", + "MPI_CXX_LONG_DOUBLE_COMPLEX"}; + + return AllTypes.find(MPIDatatype) != AllTypes.end(); +} + +/// Check if a BuiltinType matches the MPI datatype. +/// +/// \param Builtin the builtin type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// \param LO language options +/// +/// \returns true if the type matches +static bool isBuiltinTypeMatching(const BuiltinType *Builtin, + std::string &BufferTypeName, + const std::string &MPIDatatype, + const LangOptions &LO) { + static std::multimap BuiltinMatches = { + // On some systems like PPC or ARM, 'char' is unsigned by default which is + // why distinct signedness for the buffer and MPI type is tolerated. + {BuiltinType::SChar, "MPI_CHAR"}, + {BuiltinType::SChar, "MPI_SIGNED_CHAR"}, + {BuiltinType::SChar, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::Char_S, "MPI_CHAR"}, + {BuiltinType::Char_S, "MPI_SIGNED_CHAR"}, + {BuiltinType::Char_S, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::UChar, "MPI_CHAR"}, + {BuiltinType::UChar, "MPI_SIGNED_CHAR"}, + {BuiltinType::UChar, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::Char_U, "MPI_CHAR"}, + {BuiltinType::Char_U, "MPI_SIGNED_CHAR"}, + {BuiltinType::Char_U, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::WChar_S, "MPI_WCHAR"}, + {BuiltinType::WChar_U, "MPI_WCHAR"}, + {BuiltinType::Bool, "MPI_C_BOOL"}, + {BuiltinType::Bool, "MPI_CXX_BOOL"}, + {BuiltinType::Short, "MPI_SHORT"}, + {BuiltinType::Int, "MPI_INT"}, + {BuiltinType::Long, "MPI_LONG"}, + {BuiltinType::LongLong, "MPI_LONG_LONG"}, + {BuiltinType::LongLong, "MPI_LONG_LONG_INT"}, + {BuiltinType::UShort, "MPI_UNSIGNED_SHORT"}, + {BuiltinType::UInt, "MPI_UNSIGNED"}, + {BuiltinType::ULong, "MPI_UNSIGNED_LONG"}, + {BuiltinType::ULongLong, "MPI_UNSIGNED_LONG_LONG"}, + {BuiltinType::Float, "MPI_FLOAT"}, + {BuiltinType::Double, "MPI_DOUBLE"}, + {BuiltinType::LongDouble, "MPI_LONG_DOUBLE"}}; + + if (!isMPITypeMatching(BuiltinMatches, Builtin->getKind(), MPIDatatype)) { + BufferTypeName = Builtin->getName(LO); + return false; + } + + return true; +} + +/// Check if a complex float/double/long double buffer type matches +/// the MPI datatype. +/// +/// \param Complex buffer type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// \param LO language options +/// +/// \returns true if the type matches or the buffer type is unknown +static bool isCComplexTypeMatching(const ComplexType *const Complex, + std::string &BufferTypeName, + const std::string &MPIDatatype, + const LangOptions &LO) { + static std::multimap ComplexCMatches = { + {BuiltinType::Float, "MPI_C_COMPLEX"}, + {BuiltinType::Float, "MPI_C_FLOAT_COMPLEX"}, + {BuiltinType::Double, "MPI_C_DOUBLE_COMPLEX"}, + {BuiltinType::LongDouble, "MPI_C_LONG_DOUBLE_COMPLEX"}}; + + const auto *Builtin = + Complex->getElementType().getTypePtr()->getAs(); + + if (Builtin && + !isMPITypeMatching(ComplexCMatches, Builtin->getKind(), MPIDatatype)) { + BufferTypeName = (llvm::Twine(Builtin->getName(LO)) + " _Complex").str(); + return false; + } + return true; +} + +/// Check if a complex templated buffer type matches +/// the MPI datatype. +/// +/// \param Template buffer type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// \param LO language options +/// +/// \returns true if the type matches or the buffer type is unknown +static bool +isCXXComplexTypeMatching(const TemplateSpecializationType *const Template, + std::string &BufferTypeName, + const std::string &MPIDatatype, + const LangOptions &LO) { + static std::multimap ComplexCXXMatches = { + {BuiltinType::Float, "MPI_CXX_FLOAT_COMPLEX"}, + {BuiltinType::Double, "MPI_CXX_DOUBLE_COMPLEX"}, + {BuiltinType::LongDouble, "MPI_CXX_LONG_DOUBLE_COMPLEX"}}; + + if (Template->getAsCXXRecordDecl()->getName() != "complex") + return true; + + const auto *Builtin = + Template->getArg(0).getAsType().getTypePtr()->getAs(); + + if (Builtin && + !isMPITypeMatching(ComplexCXXMatches, Builtin->getKind(), MPIDatatype)) { + BufferTypeName = + (llvm::Twine("complex<") + Builtin->getName(LO) + ">").str(); + return false; + } + + return true; +} + +/// Check if a fixed size width buffer type matches the MPI datatype. +/// +/// \param Typedef buffer type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// +/// \returns true if the type matches or the buffer type is unknown +static bool isTypedefTypeMatching(const TypedefType *const Typedef, + std::string &BufferTypeName, + const std::string &MPIDatatype) { + static llvm::StringMap FixedWidthMatches = { + {"int8_t", "MPI_INT8_T"}, {"int16_t", "MPI_INT16_T"}, + {"int32_t", "MPI_INT32_T"}, {"int64_t", "MPI_INT64_T"}, + {"uint8_t", "MPI_UINT8_T"}, {"uint16_t", "MPI_UINT16_T"}, + {"uint32_t", "MPI_UINT32_T"}, {"uint64_t", "MPI_UINT64_T"}}; + + const auto it = FixedWidthMatches.find(Typedef->getDecl()->getName()); + // Check if the typedef is known and not matching the MPI datatype. + if (it != FixedWidthMatches.end() && it->getValue() != MPIDatatype) { + BufferTypeName = Typedef->getDecl()->getName(); + return false; + } + return true; +} + +/// Get the unqualified, dereferenced type of an argument. +/// +/// \param CE call expression +/// \param idx argument index +/// +/// \returns type of the argument +static const Type *argumentType(const CallExpr *const CE, const size_t idx) { + const QualType QT = CE->getArg(idx)->IgnoreImpCasts()->getType(); + return QT.getTypePtr()->getPointeeOrArrayElementType(); +} + +void TypeMismatchCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(callExpr().bind("CE"), this); +} + +void TypeMismatchCheck::check(const MatchFinder::MatchResult &Result) { + static ento::mpi::MPIFunctionClassifier FuncClassifier(*Result.Context); + const CallExpr *const CE = Result.Nodes.getNodeAs("CE"); + if (!CE->getDirectCallee()) + return; + + const IdentifierInfo *Identifier = CE->getDirectCallee()->getIdentifier(); + if (!Identifier || !FuncClassifier.isMPIType(Identifier)) + return; + + // These containers are used, to capture buffer, MPI datatype pairs. + SmallVector BufferTypes; + SmallVector BufferExprs; + SmallVector MPIDatatypes; + + // Adds a buffer, MPI datatype pair of an MPI call expression to the + // containers. For buffers, the type and expression is captured. + auto addPair = [&CE, &Result, &BufferTypes, &BufferExprs, &MPIDatatypes]( + const size_t BufferIdx, const size_t DatatypeIdx) { + // Skip null pointer constants and in place 'operators'. + if (CE->getArg(BufferIdx)->isNullPointerConstant( + *Result.Context, Expr::NPC_ValueDependentIsNull) || + tooling::fixit::getText(*CE->getArg(BufferIdx), *Result.Context) == + "MPI_IN_PLACE") + return; + + StringRef MPIDatatype = + tooling::fixit::getText(*CE->getArg(DatatypeIdx), *Result.Context); + + const Type *ArgType = argumentType(CE, BufferIdx); + // Skip unknown MPI datatypes and void pointers. + if (!isStandardMPIDatatype(MPIDatatype) || ArgType->isVoidType()) + return; + + BufferTypes.push_back(ArgType); + BufferExprs.push_back(CE->getArg(BufferIdx)); + MPIDatatypes.push_back(MPIDatatype); + }; + + // Collect all buffer, MPI datatype pairs for the inspected call expression. + if (FuncClassifier.isPointToPointType(Identifier)) { + addPair(0, 2); + } else if (FuncClassifier.isCollectiveType(Identifier)) { + if (FuncClassifier.isReduceType(Identifier)) { + addPair(0, 3); + addPair(1, 3); + } else if (FuncClassifier.isScatterType(Identifier) || + FuncClassifier.isGatherType(Identifier) || + FuncClassifier.isAlltoallType(Identifier)) { + addPair(0, 2); + addPair(3, 5); + } else if (FuncClassifier.isBcastType(Identifier)) { + addPair(0, 2); + } + } + checkArguments(BufferTypes, BufferExprs, MPIDatatypes, + Result.Context->getLangOpts()); +} + +void TypeMismatchCheck::checkArguments(ArrayRef BufferTypes, + ArrayRef BufferExprs, + ArrayRef MPIDatatypes, + const LangOptions &LO) { + std::string BufferTypeName; + + for (size_t i = 0; i < MPIDatatypes.size(); ++i) { + const Type *const BT = BufferTypes[i]; + bool Error = false; + + if (const auto *Typedef = BT->getAs()) { + Error = !isTypedefTypeMatching(Typedef, BufferTypeName, MPIDatatypes[i]); + } else if (const auto *Complex = BT->getAs()) { + Error = + !isCComplexTypeMatching(Complex, BufferTypeName, MPIDatatypes[i], LO); + } else if (const auto *Template = BT->getAs()) { + Error = !isCXXComplexTypeMatching(Template, BufferTypeName, + MPIDatatypes[i], LO); + } else if (const auto *Builtin = BT->getAs()) { + Error = + !isBuiltinTypeMatching(Builtin, BufferTypeName, MPIDatatypes[i], LO); + } + + if (Error) { + const auto Loc = BufferExprs[i]->getSourceRange().getBegin(); + diag(Loc, "buffer type '%0' does not match the MPI datatype '%1'") + << BufferTypeName << MPIDatatypes[i]; + } + } +} + +} // namespace mpi +} // namespace tidy +} // namespace clang Index: clang-tidy/performance/CMakeLists.txt =================================================================== --- clang-tidy/performance/CMakeLists.txt +++ clang-tidy/performance/CMakeLists.txt @@ -4,6 +4,7 @@ FasterStringFindCheck.cpp ForRangeCopyCheck.cpp ImplicitCastInLoopCheck.cpp + InefficientStringConcatenationCheck.cpp PerformanceTidyModule.cpp UnnecessaryCopyInitialization.cpp UnnecessaryValueParamCheck.cpp Index: clang-tidy/performance/InefficientStringConcatenationCheck.h =================================================================== --- /dev/null +++ clang-tidy/performance/InefficientStringConcatenationCheck.h @@ -0,0 +1,41 @@ +//===--- InefficientStringConcatenationCheck.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_PERFORMANCE_INEFFICIENTSTRINGCONCATENATION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTSTRINGCONCATENATION_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace performance { + +/// This check is to warn about the performance overhead arising from +/// concatenating strings, using the operator+, instead of operator+=. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-string-concatenation.html +class InefficientStringConcatenationCheck : public ClangTidyCheck { +public: + InefficientStringConcatenationCheck(StringRef Name, + ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + +private: + const bool StrictMode; +}; + +} // namespace performance +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTSTRINGCONCATENATION_H Index: clang-tidy/performance/InefficientStringConcatenationCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/performance/InefficientStringConcatenationCheck.cpp @@ -0,0 +1,86 @@ +//===--- InefficientStringConcatenationCheck.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 "InefficientStringConcatenationCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace performance { + +void InefficientStringConcatenationCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "StrictMode", StrictMode); +} + +InefficientStringConcatenationCheck::InefficientStringConcatenationCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), StrictMode(Options.get("StrictMode", 0)) {} + +void InefficientStringConcatenationCheck::registerMatchers( + MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + const auto BasicStringType = + hasType(cxxRecordDecl(hasName("::std::basic_string"))); + + const auto BasicStringPlusOperator = cxxOperatorCallExpr( + hasOverloadedOperatorName("+"), + hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType)))); + + const auto PlusOperator = + cxxOperatorCallExpr( + hasOverloadedOperatorName("+"), + hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))), + hasDescendant(BasicStringPlusOperator)) + .bind("plusOperator"); + + const auto AssignOperator = cxxOperatorCallExpr( + hasOverloadedOperatorName("="), + hasArgument(0, declRefExpr(BasicStringType, + hasDeclaration(decl().bind("lhsStrT"))) + .bind("lhsStr")), + hasArgument(1, stmt(hasDescendant(declRefExpr( + hasDeclaration(decl(equalsBoundNode("lhsStrT"))))))), + hasDescendant(BasicStringPlusOperator)); + + if (StrictMode) { + Finder->addMatcher(cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator)), + this); + } else { + Finder->addMatcher( + cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator), + hasAncestor(stmt(anyOf(cxxForRangeStmt(), + whileStmt(), forStmt())))), + this); + } +} + +void InefficientStringConcatenationCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *LhsStr = Result.Nodes.getNodeAs("lhsStr"); + const auto *PlusOperator = + Result.Nodes.getNodeAs("plusOperator"); + const auto DiagMsg = + "string concatenation results in allocation of unnecessary temporary " + "strings; consider using 'operator+=' or 'string::append()' instead"; + + if (LhsStr) + diag(LhsStr->getExprLoc(), DiagMsg); + else if (PlusOperator) + diag(PlusOperator->getExprLoc(), DiagMsg); +} + +} // namespace performance +} // namespace tidy +} // namespace clang Index: clang-tidy/performance/PerformanceTidyModule.cpp =================================================================== --- clang-tidy/performance/PerformanceTidyModule.cpp +++ clang-tidy/performance/PerformanceTidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "InefficientStringConcatenationCheck.h" #include "FasterStringFindCheck.h" #include "ForRangeCopyCheck.h" @@ -30,6 +31,8 @@ "performance-for-range-copy"); CheckFactories.registerCheck( "performance-implicit-cast-in-loop"); + CheckFactories.registerCheck( + "performance-inefficient-string-concatenation"); CheckFactories.registerCheck( "performance-unnecessary-copy-initialization"); CheckFactories.registerCheck( Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp =================================================================== --- clang-tidy/performance/UnnecessaryValueParamCheck.cpp +++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp @@ -157,8 +157,8 @@ if (CopyArgument.getLocStart().isMacroID()) return; const auto &SM = Context.getSourceManager(); - auto EndLoc = Lexer::getLocForEndOfToken(CopyArgument.getLocation(), 0, SM, - Context.getLangOpts()); + auto EndLoc = Lexer::getLocForEndOfToken(CopyArgument.getLocation(), 0, SM, + Context.getLangOpts()); Diag << FixItHint::CreateInsertion(CopyArgument.getLocStart(), "std::move(") << FixItHint::CreateInsertion(EndLoc, ")"); if (auto IncludeFixit = Inserter->CreateIncludeInsertion( Index: clang-tidy/plugin/CMakeLists.txt =================================================================== --- clang-tidy/plugin/CMakeLists.txt +++ clang-tidy/plugin/CMakeLists.txt @@ -15,6 +15,7 @@ clangTidyLLVMModule clangTidyMiscModule clangTidyModernizeModule + clangTidyMPIModule clangTidyPerformanceModule clangTidyReadabilityModule clangTooling Index: clang-tidy/readability/IdentifierNamingCheck.cpp =================================================================== --- clang-tidy/readability/IdentifierNamingCheck.cpp +++ clang-tidy/readability/IdentifierNamingCheck.cpp @@ -9,13 +9,13 @@ #include "IdentifierNamingCheck.h" +#include "llvm/ADT/DenseMapInfo.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" -#include "llvm/ADT/DenseMapInfo.h" #define DEBUG_TYPE "clang-tidy" Index: clang-tidy/tool/CMakeLists.txt =================================================================== --- clang-tidy/tool/CMakeLists.txt +++ clang-tidy/tool/CMakeLists.txt @@ -20,6 +20,7 @@ clangTidyLLVMModule clangTidyMiscModule clangTidyModernizeModule + clangTidyMPIModule clangTidyPerformanceModule clangTidyReadabilityModule clangTooling Index: clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tidy/tool/ClangTidyMain.cpp +++ clang-tidy/tool/ClangTidyMain.cpp @@ -454,6 +454,11 @@ static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination = ModernizeModuleAnchorSource; +// This anchor is used to force the linker to link the MPIModule. +extern volatile int MPIModuleAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED MPIModuleAnchorDestination = + MPIModuleAnchorSource; + // This anchor is used to force the linker to link the PerformanceModule. extern volatile int PerformanceModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED PerformanceModuleAnchorDestination = Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -64,6 +64,25 @@ Flags slicing of member variables or vtable. +- New `cppcoreguidelines-special-member-functions + `_ check + + Flags classes where some, but not all, special member functions are user-defined. + +- New `performance-inefficient-string-concatenation + `_ check + + This check warns about the performance overhead arising from concatenating + strings using the ``operator+``, instead of ``operator+=``. + +- Bugfix for `modernize-make-unique + `_ + and `modernize-make-shared + `_ + checks + It is invalid to use ``make_{smart_ptr}`` when the called constructor is + private. + Improvements to include-fixer ----------------------------- Index: docs/clang-rename.rst =================================================================== --- docs/clang-rename.rst +++ docs/clang-rename.rst @@ -10,9 +10,9 @@ :maxdepth: 1 -:program:`clang-rename` is a clang-based C++ "linter" tool. Its purpose is to -perform efficient renaming actions in large-scale projects such as renaming -classes, functions, variables, arguments, namespaces etc. +:program:`clang-rename` is a C++ refactoring tool. Its purpose is to perform +efficient renaming actions in large-scale projects such as renaming classes, +functions, variables, arguments, namespaces etc. The tool is in a very early development stage, so you might encounter bugs and crashes. Submitting reports with information about how to reproduce the issue @@ -20,7 +20,7 @@ project. If you have any ideas or suggestions, you might want to put a feature request there. -Using clang-rename +Using Clang-Rename ================== :program:`clang-rename` is a `LibTooling @@ -28,7 +28,7 @@ work with if you set up a compile command database for your project (for an example of how to do this see `How To Setup Tooling For LLVM `_). You can also -specify compilation options on the command line after ``--``: +specify compilation options on the command line after `--`: .. code-block:: console @@ -47,24 +47,79 @@ renaming actions in the future. :program:`clang-rename` also aims to be easily integrated into popular text -editors, such as Vim, and improve the workflow of users. +editors, such as Vim and Emacs, and improve the workflow of users. Although a command line interface exists, it is highly recommended to use the text editor interface instead for better experience. +You can also identify one or more symbols to be renamed by giving the fully qualified +name: + +.. code-block:: console + + $ clang-rename rename-all -old-name=foo -new-name=bar test.cpp + + +Alternatively, old name / new name pairs can be put into a YAML file: + +.. code-block:: yaml + + --- + - OldName: foo + NewName: bar + ... + + +That way you can avoid spelling out all the names as commandline arguments: + +.. code-block:: console + + $ clang-rename rename-all -input=test.yaml test.cpp + + +The YAML file also supports offsets: + +.. code-block:: yaml + + --- + - Offset: 42 + NewName: foo + ... + + +:program:`clang-rename` offers the following options: + .. code-block:: console $ clang-rename -help + Usage: clang-rename {rename-at|rename-all} [OPTION]... + + A tool to rename symbols in C/C++ code. + + Subcommands: + rename-at: Perform rename off of a location in a file. (This is the default.) + rename-all: Perform rename of all symbols matching one or more fully qualified names. + + +.. code-block:: console + + $ clang-rename rename-at -help OVERVIEW: A tool to rename symbols in C/C++ code. clang-rename renames every occurrence of a symbol found at in . If -i is specified, the edited files are overwritten to disk. Otherwise, the results are written to stdout. - - USAGE: clang-rename [subcommand] [options] [... ] - + + USAGE: clang-rename rename-at [subcommand] [options] [... ] + OPTIONS: + + Generic Options: + + -help - Display available options (-help-hidden for more) + -help-list - Display list of available options (-help-list-hidden for more) + -version - Display the version of this program - Clang-rename options: + clang-rename rename-at options: -export-fixes= - YAML file to store suggested fixes in. -extra-arg= - Additional argument to append to the compiler command line @@ -72,20 +127,42 @@ -i - Overwrite edited s. -new-name= - The new name to change the symbol to. -offset= - Locates the symbol by offset as opposed to :. - -old-name= - The fully qualified name of the symbol, if -offset is not used. -p= - Build path -pl - Print the locations affected by renaming to stderr. -pn - Print the found symbol's name prior to renaming to stderr. + +.. code-block:: console + + $ clang-rename rename-all -help + OVERVIEW: A tool to rename symbols in C/C++ code. + clang-rename renames every occurrence of a symbol named . + + USAGE: clang-rename rename-all [subcommand] [options] [... ] + + OPTIONS: + Generic Options: -help - Display available options (-help-hidden for more) -help-list - Display list of available options (-help-list-hidden for more) -version - Display the version of this program + clang-rename rename-all options: + + -export-fixes= - YAML file to store suggested fixes in. + -extra-arg= - Additional argument to append to the compiler command line + -extra-arg-before= - Additional argument to prepend to the compiler command line + -i - Overwrite edited s. + -input= - YAML file to load oldname-newname pairs from. + -new-name= - The new name to change the symbol to. + -offset= - Locates the symbol by offset as opposed to :. + -old-name= - The fully qualified name of the symbol, if -offset is not used. + -p= - Build path + -clang-rename Vim integration -============================ +Vim Integration +=============== You can call :program:`clang-rename` directly from Vim! To set up :program:`clang-rename` integration for Vim see @@ -96,7 +173,21 @@ happen before running the tool**. Once installed, you can point your cursor to symbols you want to rename, press -``cr`` and print new desired name. The -[`` key``](http://vim.wikia.com/wiki/Mapping_keys_in_Vim_-_Tutorial_(Part_3)#Map_leader) +`cr` and type new desired name. The ` key +`_ is a reference to a specific key defined by the mapleader variable and is bound to backslash by default. + +Emacs Integration +================= + +You can also use :program:`clang-rename` while using Emacs! To set up +:program:`clang-rename` integration for Emacs see +`clang-rename/tool/clang-rename.el +`_. + +Once installed, you can point your cursor to symbols you want to rename, press +`M-X`, type `clang-rename` and new desired name. + +Please note that **you have to save all buffers, in which the replacement will +happen before running the tool**. Index: docs/clang-tidy/checks/cppcoreguidelines-slicing.rst =================================================================== --- docs/clang-tidy/checks/cppcoreguidelines-slicing.rst +++ docs/clang-tidy/checks/cppcoreguidelines-slicing.rst @@ -10,13 +10,13 @@ .. code:: c++ - struct B { int a; virtual int f(); }; - struct D : B { int b; int f() override; }; - void use(B b) { // Missing reference, intended ? - b.f(); // Calls B::f. - } - D d; - use(d); // Slice. + struct B { int a; virtual int f(); }; + struct D : B { int b; int f() override; }; + void use(B b) { // Missing reference, intended ? + b.f(); // Calls B::f. + } + D d; + use(d); // Slice. See the relevant CppCoreGuidelines sections for details: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es63-dont-slice Index: docs/clang-tidy/checks/cppcoreguidelines-special-member-functions.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/cppcoreguidelines-special-member-functions.rst @@ -0,0 +1,21 @@ +.. title:: clang-tidy - cppcoreguidelines-special-member-functions + +cppcoreguidelines-special-member-functions +========================================== + +The check finds classes where some but not all of the special member functions +are defined. + +By default the compiler defines a copy constructor, copy assignment operator, +move constructor, move assignment operator and destructor. The default can be +supressed by explicit user-definitions. The relationship between which +functions will be supressed by definitions of other functions is complicated +and it is advised that all five are defaulted or explicitly defined. + +Note that defining a function with ``= delete`` is considered to be a +definition. + +This rule is part of the "Constructors, assignments, and destructors" profile of the C++ Core +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. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -30,6 +30,7 @@ cppcoreguidelines-pro-type-union-access cppcoreguidelines-pro-type-vararg cppcoreguidelines-slicing + cppcoreguidelines-special-member-functions google-build-explicit-make-pair google-build-namespaces google-build-using-namespace @@ -109,9 +110,11 @@ modernize-use-nullptr modernize-use-override modernize-use-using + mpi-type-mismatch performance-faster-string-find performance-for-range-copy performance-implicit-cast-in-loop + performance-inefficient-string-concatenation performance-unnecessary-copy-initialization performance-unnecessary-value-param readability-avoid-const-params-in-decls 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 @@ -18,3 +18,7 @@ // warning: argument name 'bar' in comment does not match parameter name 'foo' The check tries to detect typos and suggest automated fixes for them. + +Supported options: + - `StrictMode` (local or global): when non-zero, the check will ignore leading + and trailing underscores and case when comparing parameter names. Index: docs/clang-tidy/checks/modernize-use-bool-literals.rst =================================================================== --- docs/clang-tidy/checks/modernize-use-bool-literals.rst +++ docs/clang-tidy/checks/modernize-use-bool-literals.rst @@ -10,9 +10,11 @@ bool p = 1; bool f = static_cast(1); std::ios_base::sync_with_stdio(0); + bool x = p ? 1 : 0; // transforms to bool p = true; bool f = true; std::ios_base::sync_with_stdio(false); + bool x = p ? true : false; 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 @@ -20,25 +20,25 @@ .. code:: c++ - std::vector v; - v.push_back(MyClass(21, 37)); + std::vector v; + v.push_back(MyClass(21, 37)); - std::vector> w; + std::vector> w; - w.push_back(std::pair(21, 37)); - w.push_back(std::make_pair(21L, 37L)); + w.push_back(std::pair(21, 37)); + w.push_back(std::make_pair(21L, 37L)); After: .. code:: c++ - std::vector v; - v.emplace_back(21, 37); + std::vector v; + v.emplace_back(21, 37); - std::vector> w; - w.emplace_back(21, 37); - // This will be fixed to w.push_back(21, 37); in next version - w.emplace_back(std::make_pair(21L, 37L); + std::vector> w; + w.emplace_back(21, 37); + // 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 inside a container. @@ -47,15 +47,15 @@ .. code:: c++ - std::vector > v; - v.push_back("abc"); + std::vector > v; + v.push_back("abc"); After: .. code:: c++ - std::vector > v; - v.emplace_back("abc"); + std::vector > v; + v.emplace_back("abc"); In some cases the transformation would be valid, but the code @@ -63,11 +63,11 @@ In this case the calls of ``push_back`` won't be replaced. .. code:: c++ - + std::vector> v; - v.push_back(std::unique_ptr(new int(0))); - auto *ptr = new int(1); - v.push_back(std::unique_ptr(ptr)); + v.push_back(std::unique_ptr(new int(0))); + auto *ptr = new int(1); + v.push_back(std::unique_ptr(ptr)); This is because replacing it with ``emplace_back`` could cause a leak of this pointer if ``emplace_back`` would throw exception before emplacement Index: docs/clang-tidy/checks/mpi-type-mismatch.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/mpi-type-mismatch.rst @@ -0,0 +1,20 @@ +.. title:: clang-tidy - mpi-type-mismatch + +mpi-type-mismatch +================= + +This check verifies if buffer type and MPI (Message Passing Interface) datatype +pairs match for used MPI functions. All MPI datatypes defined by the MPI +standard (3.1) are verified by this check. User defined typedefs, custom MPI +datatypes and null pointer constants are skipped, in the course of verification. + +Example: +.. code:: c++ + + // In this case, the buffer type matches MPI datatype. + char buf; + MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + + // In the following case, the buffer type does not match MPI datatype. + int buf; + MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); Index: docs/clang-tidy/checks/performance-inefficient-string-concatenation.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/performance-inefficient-string-concatenation.rst @@ -0,0 +1,49 @@ +.. title:: clang-tidy - performance-inefficient-string-concatenation + +performance-inefficient-string-concatenation +============================================ + +This check warns about the performance overhead arising from concatenating strings using the ``operator+``, for instance: + +.. code:: c++ + + std::string a("Foo"), b("Bar"); + a = a + b; + +Instead of this structure you should use ``operator+=`` or ``std::string``'s (``std::basic_string``) class member function ``append()``. For instance: + +.. code:: c++ + + std::string a("Foo"), b("Baz"); + for (int i = 0; i < 20000; ++i) { + a = a + "Bar" + b; + } + +Could be rewritten in a greatly more efficient way like: + +.. code:: c++ + + std::string a("Foo"), b("Baz"); + for (int i = 0; i < 20000; ++i) { + a.append("Bar").append(b); + } + +And this can be rewritten too: + +.. code:: c++ + + void f(const std::string&) {} + std::string a("Foo"), b("Baz"); + void g() { + f(a + "Bar" + b); + } + +In a slightly more efficient way like: + +.. code:: c++ + + void f(const std::string&) {} + std::string a("Foo"), b("Baz"); + void g() { + f(std::string(a).append("Bar").append(b)); + } Index: docs/clang-tidy/index.rst =================================================================== --- docs/clang-tidy/index.rst +++ docs/clang-tidy/index.rst @@ -79,7 +79,7 @@ Clang diagnostics have check names starting with ``clang-diagnostic-``. Diagnostics which have a corresponding warning option, are named -``clang-diagostic-``, e.g. Clang warning controlled by +``clang-diagnostic-``, e.g. Clang warning controlled by ``-Wliteral-conversion`` will be reported with check name ``clang-diagnostic-literal-conversion``. @@ -140,7 +140,7 @@ report to stderr. -export-fixes= - YAML file to store suggested fixes in. The - stored fixes can be applied to the input sorce + stored fixes can be applied to the input source code with clang-apply-replacements. -extra-arg= - Additional argument to append to the compiler command line -extra-arg-before= - Additional argument to prepend to the compiler command line @@ -246,7 +246,7 @@ There are a few tools particularly useful when developing clang-tidy checks: * ``add_new_check.py`` is a script to automate the process of adding a new check, it will create the check, update the CMake file and create a test; - * ``rename_check.py`` does what the script name suggest, renames an existsing + * ``rename_check.py`` does what the script name suggest, renames an existing check; * :program:`clang-query` is invaluable for interactive prototyping of AST matchers and exploration of the Clang AST; @@ -562,7 +562,7 @@ } There are many dark corners in the C++ language, and it may be difficult to make -your check work perfectly in all cases, especially if it issues fixit hints. The +your check work perfectly in all cases, especially if it issues fix-it hints. The most frequent pitfalls are macros and templates: 1. code written in a macro body/template definition may have a different meaning @@ -570,7 +570,7 @@ 2. multiple macro expansions/template instantiations may result in the same code being inspected by the check multiple times (possibly, with different meanings, see 1), and the same warning (or a slightly different one) may be - issued by the check multipe times; clang-tidy will deduplicate _identical_ + issued by the check multiple times; clang-tidy will deduplicate _identical_ warnings, but if the warnings are slightly different, all of them will be shown to the user (and used for applying fixes, if any); 3. making replacements to a macro body/template definition may be fine for some Index: docs/include-fixer.rst =================================================================== --- docs/include-fixer.rst +++ docs/include-fixer.rst @@ -67,9 +67,9 @@ noremap cf :pyf path/to/llvm/source/tools/clang/tools/extra/include-fixer/tool/clang-include-fixer.py This enables `clang-include-fixer` for NORMAL and VISUAL mode. Change -``cf`` to another binding if you need clang-include-fixer on a different -key. The -[`` key``](http://vim.wikia.com/wiki/Mapping_keys_in_Vim_-_Tutorial_(Part_3)#Map_leader) +`cf` to another binding if you need clang-include-fixer on a different +key. The ` key +`_ is a reference to a specific key defined by the mapleader variable and is bound to backslash by default. Index: include-fixer/IncludeFixer.h =================================================================== --- include-fixer/IncludeFixer.h +++ include-fixer/IncludeFixer.h @@ -30,11 +30,12 @@ class IncludeFixerActionFactory : public clang::tooling::ToolAction { public: /// \param SymbolIndexMgr A source for matching symbols to header files. - /// \param Context A context for the symbol being queried. + /// \param Contexts The contexts for the symbols being queried. /// \param StyleName Fallback style for reformatting. /// \param MinimizeIncludePaths whether inserted include paths are optimized. IncludeFixerActionFactory(SymbolIndexManager &SymbolIndexMgr, - IncludeFixerContext &Context, StringRef StyleName, + std::vector &Contexts, + StringRef StyleName, bool MinimizeIncludePaths = true); ~IncludeFixerActionFactory() override; @@ -49,8 +50,8 @@ /// The client to use to find cross-references. SymbolIndexManager &SymbolIndexMgr; - /// The context that contains all information about the symbol being queried. - IncludeFixerContext &Context; + /// Multiple contexts for files being processed. + std::vector &Contexts; /// Whether inserted include paths should be optimized. bool MinimizeIncludePaths; @@ -65,7 +66,6 @@ /// first header for insertion. /// /// \param Code The source code. -/// \param FilePath The source file path. /// \param Context The context which contains all information for creating /// include-fixer replacements. /// \param Style clang-format style being used. @@ -76,7 +76,7 @@ /// qualifiers on success; otherwise, an llvm::Error carrying llvm::StringError /// is returned. llvm::Expected createIncludeFixerReplacements( - StringRef Code, StringRef FilePath, const IncludeFixerContext &Context, + StringRef Code, const IncludeFixerContext &Context, const format::FormatStyle &Style = format::getLLVMStyle(), bool AddQualifiers = true); Index: include-fixer/IncludeFixer.cpp =================================================================== --- include-fixer/IncludeFixer.cpp +++ include-fixer/IncludeFixer.cpp @@ -37,6 +37,7 @@ std::unique_ptr CreateASTConsumer(clang::CompilerInstance &Compiler, StringRef InFile) override { + FilePath = InFile; return llvm::make_unique(); } @@ -228,7 +229,7 @@ Symbol.getContexts(), Symbol.getNumOccurrences()); } - return IncludeFixerContext(QuerySymbolInfos, SymbolCandidates); + return IncludeFixerContext(FilePath, QuerySymbolInfos, SymbolCandidates); } private: @@ -275,8 +276,11 @@ // 1. lookup a::b::foo. // 2. lookup b::foo. std::string QueryString = ScopedQualifiers.str() + Query.str(); - MatchedSymbols = SymbolIndexMgr.search(QueryString); - if (MatchedSymbols.empty() && !ScopedQualifiers.empty()) + // It's unsafe to do nested search for the identifier with scoped namespace + // context, it might treat the identifier as a nested class of the scoped + // namespace. + MatchedSymbols = SymbolIndexMgr.search(QueryString, /*IsNestedSearch=*/false); + if (MatchedSymbols.empty()) MatchedSymbols = SymbolIndexMgr.search(Query); DEBUG(llvm::dbgs() << "Having found " << MatchedSymbols.size() << " symbols\n"); @@ -294,6 +298,9 @@ /// recovery. std::vector MatchedSymbols; + /// The file path to the file being processed. + std::string FilePath; + /// Whether we should use the smallest possible include path. bool MinimizeIncludePaths = true; }; @@ -301,9 +308,10 @@ } // namespace IncludeFixerActionFactory::IncludeFixerActionFactory( - SymbolIndexManager &SymbolIndexMgr, IncludeFixerContext &Context, - StringRef StyleName, bool MinimizeIncludePaths) - : SymbolIndexMgr(SymbolIndexMgr), Context(Context), + SymbolIndexManager &SymbolIndexMgr, + std::vector &Contexts, StringRef StyleName, + bool MinimizeIncludePaths) + : SymbolIndexMgr(SymbolIndexMgr), Contexts(Contexts), MinimizeIncludePaths(MinimizeIncludePaths) {} IncludeFixerActionFactory::~IncludeFixerActionFactory() = default; @@ -334,9 +342,9 @@ llvm::make_unique(SymbolIndexMgr, MinimizeIncludePaths); Compiler.ExecuteAction(*ScopedToolAction); - Context = ScopedToolAction->getIncludeFixerContext( + Contexts.push_back(ScopedToolAction->getIncludeFixerContext( Compiler.getSourceManager(), - Compiler.getPreprocessor().getHeaderSearchInfo()); + Compiler.getPreprocessor().getHeaderSearchInfo())); // Technically this should only return true if we're sure that we have a // parseable file. We don't know that though. Only inform users of fatal @@ -345,30 +353,44 @@ } llvm::Expected createIncludeFixerReplacements( - StringRef Code, StringRef FilePath, const IncludeFixerContext &Context, + StringRef Code, const IncludeFixerContext &Context, const clang::format::FormatStyle &Style, bool AddQualifiers) { if (Context.getHeaderInfos().empty()) return tooling::Replacements(); + StringRef FilePath = Context.getFilePath(); std::string IncludeName = "#include " + Context.getHeaderInfos().front().Header + "\n"; // Create replacements for the new header. - clang::tooling::Replacements Insertions = { - tooling::Replacement(FilePath, UINT_MAX, 0, IncludeName)}; + clang::tooling::Replacements Insertions; + auto Err = + Insertions.add(tooling::Replacement(FilePath, UINT_MAX, 0, IncludeName)); + if (Err) + return std::move(Err); auto CleanReplaces = cleanupAroundReplacements(Code, Insertions, Style); if (!CleanReplaces) return CleanReplaces; + auto Replaces = std::move(*CleanReplaces); if (AddQualifiers) { for (const auto &Info : Context.getQuerySymbolInfos()) { // Ignore the empty range. - if (Info.Range.getLength() > 0) - CleanReplaces->insert({FilePath, Info.Range.getOffset(), - Info.Range.getLength(), - Context.getHeaderInfos().front().QualifiedName}); + if (Info.Range.getLength() > 0) { + auto R = tooling::Replacement( + {FilePath, Info.Range.getOffset(), Info.Range.getLength(), + Context.getHeaderInfos().front().QualifiedName}); + auto Err = Replaces.add(R); + if (Err) { + llvm::consumeError(std::move(Err)); + R = tooling::Replacement( + R.getFilePath(), Replaces.getShiftedCodePosition(R.getOffset()), + R.getLength(), R.getReplacementText()); + Replaces = Replaces.merge(tooling::Replacements(R)); + } + } } } - return formatReplacements(Code, *CleanReplaces, Style); + return formatReplacements(Code, Replaces, Style); } } // namespace include_fixer Index: include-fixer/IncludeFixerContext.h =================================================================== --- include-fixer/IncludeFixerContext.h +++ include-fixer/IncludeFixerContext.h @@ -18,7 +18,8 @@ namespace clang { namespace include_fixer { -/// \brief A context for the symbol being queried. +/// \brief A context for a file being processed. It includes all query +/// information, e.g. symbols being queried in database, all header candidates. class IncludeFixerContext { public: struct HeaderInfo { @@ -46,7 +47,8 @@ }; IncludeFixerContext() = default; - IncludeFixerContext(std::vector QuerySymbols, + IncludeFixerContext(StringRef FilePath, + std::vector QuerySymbols, std::vector Symbols); /// \brief Get symbol name. @@ -59,6 +61,9 @@ return QuerySymbolInfos.front().Range; } + /// \brief Get the file path to the file being processed. + StringRef getFilePath() const { return FilePath; } + /// \brief Get header information. const std::vector &getHeaderInfos() const { return HeaderInfos; } @@ -70,6 +75,9 @@ private: friend struct llvm::yaml::MappingTraits; + /// \brief The file path to the file being processed. + std::string FilePath; + /// \brief All instances of an unidentified symbol being queried. std::vector QuerySymbolInfos; Index: include-fixer/IncludeFixerContext.cpp =================================================================== --- include-fixer/IncludeFixerContext.cpp +++ include-fixer/IncludeFixerContext.cpp @@ -44,7 +44,8 @@ std::string StrippedQualifiers; while (!SymbolQualifiers.empty() && !llvm::StringRef(QualifiedName).endswith(SymbolQualifiers.back())) { - StrippedQualifiers = "::" + SymbolQualifiers.back().str(); + StrippedQualifiers = + "::" + SymbolQualifiers.back().str() + StrippedQualifiers; SymbolQualifiers.pop_back(); } // Append the missing stripped qualifiers. @@ -75,9 +76,9 @@ } // anonymous namespace IncludeFixerContext::IncludeFixerContext( - std::vector QuerySymbols, + StringRef FilePath, std::vector QuerySymbols, std::vector Symbols) - : QuerySymbolInfos(std::move(QuerySymbols)), + : FilePath(FilePath), QuerySymbolInfos(std::move(QuerySymbols)), MatchedSymbols(std::move(Symbols)) { // Remove replicated QuerySymbolInfos with the same range. // Index: include-fixer/SymbolIndexManager.h =================================================================== --- include-fixer/SymbolIndexManager.h +++ include-fixer/SymbolIndexManager.h @@ -28,12 +28,15 @@ /// Search for header files to be included for an identifier. /// \param Identifier The identifier being searched for. May or may not be /// fully qualified. - /// \returns A list of inclusion candidates, in a format ready for being - /// pasted after an #include token. - // FIXME: Move mapping from SymbolInfo to headers out of - // SymbolIndexManager::search and return SymbolInfos instead of header paths. + /// \param IsNestedSearch Whether searching nested classes. If true, the + /// method tries to strip identifier name parts from the end until it + /// finds the corresponding candidates in database (e.g for identifier + /// "b::foo", the method will try to find "b" if it fails to find + /// "b::foo"). + /// + /// \returns A list of symbol candidates. std::vector - search(llvm::StringRef Identifier) const; + search(llvm::StringRef Identifier, bool IsNestedSearch = true) const; private: std::vector> SymbolIndices; Index: include-fixer/SymbolIndexManager.cpp =================================================================== --- include-fixer/SymbolIndexManager.cpp +++ include-fixer/SymbolIndexManager.cpp @@ -42,7 +42,8 @@ } std::vector -SymbolIndexManager::search(llvm::StringRef Identifier) const { +SymbolIndexManager::search(llvm::StringRef Identifier, + bool IsNestedSearch) const { // The identifier may be fully qualified, so split it and get all the context // names. llvm::SmallVector Names; @@ -60,7 +61,7 @@ // either) and can report that result. bool TookPrefix = false; std::vector MatchedSymbols; - while (MatchedSymbols.empty() && !Names.empty()) { + do { std::vector Symbols; for (const auto &DB : SymbolIndices) { auto Res = DB->search(Names.back().str()); @@ -116,7 +117,7 @@ } Names.pop_back(); TookPrefix = true; - } + } while (MatchedSymbols.empty() && !Names.empty() && IsNestedSearch); rankByPopularity(MatchedSymbols); return MatchedSymbols; Index: include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp =================================================================== --- include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp +++ include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp @@ -93,7 +93,7 @@ {"bits/basic_string.h$", ""}, {"bits/basic_string.tcc$", ""}, {"bits/char_traits.h$", ""}, - {"bits/codecvt.h$", ""}, + {"bits/codecvt.h$", ""}, {"bits/concept_check.h$", ""}, {"bits/cpp_type_traits.h$", ""}, {"bits/cxxabi_forced.h$", ""}, @@ -120,18 +120,18 @@ {"bits/locale_facets.tcc$", ""}, {"bits/locale_facets_nonio.h$", ""}, {"bits/locale_facets_nonio.tcc$", ""}, - {"bits/localefwd.h$", ""}, + {"bits/localefwd.h$", ""}, {"bits/mask_array.h$", ""}, - {"bits/memoryfwd.h$", ""}, + {"bits/memoryfwd.h$", ""}, {"bits/move.h$", ""}, {"bits/nested_exception.h$", ""}, {"bits/ostream.tcc$", ""}, - {"bits/ostream_insert.h$", ""}, + {"bits/ostream_insert.h$", ""}, {"bits/postypes.h$", ""}, - {"bits/ptr_traits.h$", ""}, + {"bits/ptr_traits.h$", ""}, {"bits/random.h$", ""}, {"bits/random.tcc$", ""}, - {"bits/range_access.h$", ""}, + {"bits/range_access.h$", ""}, {"bits/regex.h$", ""}, {"bits/regex_compiler.h$", ""}, {"bits/regex_constants.h$", ""}, @@ -316,8 +316,8 @@ {"bits/basic_file.h$", ""}, {"bits/c++allocator.h$", ""}, {"bits/c++config.h$", ""}, - {"bits/c++io.h$", ""}, - {"bits/c++locale.h$", ""}, + {"bits/c++io.h$", ""}, + {"bits/c++locale.h$", ""}, {"bits/cpu_defines.h$", ""}, {"bits/ctype_base.h$", ""}, {"bits/cxxabi_tweaks.h$", ""}, Index: include-fixer/tool/ClangIncludeFixer.cpp =================================================================== --- include-fixer/tool/ClangIncludeFixer.cpp +++ include-fixer/tool/ClangIncludeFixer.cpp @@ -73,6 +73,7 @@ static void mapping(IO &IO, IncludeFixerContext &Context) { IO.mapRequired("QuerySymbolInfos", Context.QuerySymbolInfos); IO.mapRequired("HeaderInfos", Context.HeaderInfos); + IO.mapRequired("FilePath", Context.FilePath); } }; } // namespace yaml @@ -203,7 +204,9 @@ void writeToJson(llvm::raw_ostream &OS, const IncludeFixerContext& Context) { OS << "{\n" - " \"QuerySymbolInfos\": [\n"; + << " \"FilePath\": \"" + << llvm::yaml::escape(Context.getFilePath()) << "\",\n" + << " \"QuerySymbolInfos\": [\n"; for (const auto &Info : Context.getQuerySymbolInfos()) { OS << " {\"RawIdentifier\": \"" << Info.RawIdentifier << "\",\n"; OS << " \"Range\":{"; @@ -251,9 +254,6 @@ tool.mapVirtualFile(options.getSourcePathList().front(), Code->getBuffer()); } - StringRef FilePath = options.getSourcePathList().front(); - format::FormatStyle InsertStyle = format::getStyle("file", FilePath, Style); - if (!InsertHeader.empty()) { if (!STDINMode) { errs() << "Should be running in STDIN mode\n"; @@ -289,9 +289,10 @@ const IncludeFixerContext::HeaderInfo &RHS) { return LHS.QualifiedName == RHS.QualifiedName; }); - + format::FormatStyle InsertStyle = + format::getStyle("file", Context.getFilePath(), Style); auto Replacements = clang::include_fixer::createIncludeFixerReplacements( - Code->getBuffer(), FilePath, Context, InsertStyle, + Code->getBuffer(), Context, InsertStyle, /*AddQualifiers=*/IsUniqueQualifiedName); if (!Replacements) { errs() << "Failed to create replacements: " @@ -316,8 +317,8 @@ return 1; // Now run our tool. - include_fixer::IncludeFixerContext Context; - include_fixer::IncludeFixerActionFactory Factory(*SymbolIndexMgr, Context, + std::vector Contexts; + include_fixer::IncludeFixerActionFactory Factory(*SymbolIndexMgr, Contexts, Style, MinimizeIncludePaths); if (tool.run(&Factory) != 0) { @@ -326,43 +327,49 @@ return 1; } + assert(!Contexts.empty()); + if (OutputHeaders) { - writeToJson(llvm::outs(), Context); + // FIXME: Print contexts of all processing files instead of the first one. + writeToJson(llvm::outs(), Contexts.front()); return 0; } - if (Context.getHeaderInfos().empty()) - return 0; + std::vector FixerReplacements; + for (const auto &Context : Contexts) { + StringRef FilePath = Context.getFilePath(); + format::FormatStyle InsertStyle = format::getStyle("file", FilePath, Style); + auto Buffer = llvm::MemoryBuffer::getFile(FilePath); + if (!Buffer) { + errs() << "Couldn't open file: " + FilePath.str() + ": " + << Buffer.getError().message() + "\n"; + return 1; + } - auto Buffer = llvm::MemoryBuffer::getFile(FilePath); - if (!Buffer) { - errs() << "Couldn't open file: " << FilePath << ": " - << Buffer.getError().message() << '\n'; - return 1; + auto Replacements = clang::include_fixer::createIncludeFixerReplacements( + Buffer.get()->getBuffer(), Context, InsertStyle); + if (!Replacements) { + errs() << "Failed to create replacement: " + << llvm::toString(Replacements.takeError()) << "\n"; + return 1; + } + FixerReplacements.push_back(*Replacements); } - auto Replacements = clang::include_fixer::createIncludeFixerReplacements( - /*Code=*/Buffer.get()->getBuffer(), FilePath, Context, InsertStyle); - if (!Replacements) { - errs() << "Failed to create header insertion replacement: " - << llvm::toString(Replacements.takeError()) << "\n"; - return 1; + if (!Quiet) { + for (const auto &Context : Contexts) { + if (!Context.getHeaderInfos().empty()) { + llvm::errs() << "Added #include " + << Context.getHeaderInfos().front().Header << " for " + << Context.getFilePath() << "\n"; + } + } } - if (!Quiet) - errs() << "Added #include " << Context.getHeaderInfos().front().Header - << '\n'; - - // Set up a new source manager for applying the resulting replacements. - IntrusiveRefCntPtr DiagOpts(new DiagnosticOptions); - DiagnosticsEngine Diagnostics(new DiagnosticIDs, &*DiagOpts); - TextDiagnosticPrinter DiagnosticPrinter(outs(), &*DiagOpts); - SourceManager SM(Diagnostics, tool.getFiles()); - Diagnostics.setClient(&DiagnosticPrinter, false); - if (STDINMode) { - auto ChangedCode = - tooling::applyAllReplacements(Code->getBuffer(), *Replacements); + assert(FixerReplacements.size() == 1); + auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), + FixerReplacements.front()); if (!ChangedCode) { llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; return 1; @@ -371,9 +378,21 @@ return 0; } + // Set up a new source manager for applying the resulting replacements. + IntrusiveRefCntPtr DiagOpts(new DiagnosticOptions); + DiagnosticsEngine Diagnostics(new DiagnosticIDs, &*DiagOpts); + TextDiagnosticPrinter DiagnosticPrinter(outs(), &*DiagOpts); + SourceManager SM(Diagnostics, tool.getFiles()); + Diagnostics.setClient(&DiagnosticPrinter, false); + // Write replacements to disk. Rewriter Rewrites(SM, LangOptions()); - tooling::applyAllReplacements(*Replacements, Rewrites); + for (const auto Replacement : FixerReplacements) { + if (!tooling::applyAllReplacements(Replacement, Rewrites)) { + llvm::errs() << "Failed to apply replacements.\n"; + return 1; + } + } return Rewrites.overwriteChangedFiles(); } Index: include-fixer/tool/clang-include-fixer.py =================================================================== --- include-fixer/tool/clang-include-fixer.py +++ include-fixer/tool/clang-include-fixer.py @@ -149,21 +149,16 @@ return try: - # If there is only one suggested header, insert it directly. - if len(unique_headers) == 1 or maximum_suggested_headers == 1: - InsertHeaderToVimBuffer({"QuerySymbolInfos": query_symbol_infos, - "HeaderInfos": header_infos}, text) - print "Added #include {0} for {1}.".format(unique_headers[0], symbol) - return - - selected = GetUserSelection("choose a header file for {0}.".format(symbol), - unique_headers, maximum_suggested_headers) - selected_header_infos = [ - header for header in header_infos if header["Header"] == selected] - - # Insert a selected header. - InsertHeaderToVimBuffer({"QuerySymbolInfos": query_symbol_infos, - "HeaderInfos": selected_header_infos}, text) + inserted_header_infos = header_infos + if len(unique_headers) > 1: + selected = GetUserSelection( + "choose a header file for {0}.".format(symbol), + unique_headers, maximum_suggested_headers) + inserted_header_infos = [ + header for header in header_infos if header["Header"] == selected] + include_fixer_context["HeaderInfos"] = inserted_header_infos + + InsertHeaderToVimBuffer(include_fixer_context, text) print "Added #include {0} for {1}.".format(selected, symbol) except Exception as error: print >> sys.stderr, error.message Index: test/clang-rename/ClassAsTemplateArgumentFindByClass.cpp =================================================================== --- test/clang-rename/ClassAsTemplateArgumentFindByClass.cpp +++ test/clang-rename/ClassAsTemplateArgumentFindByClass.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=74 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo {}; // CHECK: class Bar {}; Index: test/clang-rename/ClassAsTemplateArgumentFindByTemplateArgument.cpp =================================================================== --- test/clang-rename/ClassAsTemplateArgumentFindByTemplateArgument.cpp +++ test/clang-rename/ClassAsTemplateArgumentFindByTemplateArgument.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=243 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=304 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo {}; // CHECK: class Bar {}; Index: test/clang-rename/ClassFindByName.cpp =================================================================== --- test/clang-rename/ClassFindByName.cpp +++ test/clang-rename/ClassFindByName.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -old-name=Foo -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename rename-all -old-name=Foo -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo { // CHECK: class Bar }; Index: test/clang-rename/ClassNameInFunctionDefinition.cpp =================================================================== --- test/clang-rename/ClassNameInFunctionDefinition.cpp +++ test/clang-rename/ClassNameInFunctionDefinition.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=74 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo { // CHECK: class Bar { public: Index: test/clang-rename/ClassSimpleRenaming.cpp =================================================================== --- test/clang-rename/ClassSimpleRenaming.cpp +++ test/clang-rename/ClassSimpleRenaming.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=74 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo {}; // CHECK: class Bar @@ -7,5 +9,5 @@ return 0; } -// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// Use grep -FUbo 'Foo' to get the correct offset of Cla when changing // this file. Index: test/clang-rename/ClassTestMulti.cpp =================================================================== --- /dev/null +++ test/clang-rename/ClassTestMulti.cpp @@ -0,0 +1,8 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename rename-all -offset=174 -new-name=Bar1 -offset=212 -new-name=Bar2 %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s +class Foo1 { // CHECK: class Bar1 +}; + +class Foo2 { // CHECK: class Bar2 +}; Index: test/clang-rename/ClassTestMultiByName.cpp =================================================================== --- /dev/null +++ test/clang-rename/ClassTestMultiByName.cpp @@ -0,0 +1,8 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename rename-all -old-name=Foo1 -new-name=Bar1 -old-name=Foo2 -new-name=Bar2 %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s +class Foo1 { // CHECK: class Bar1 +}; + +class Foo2 { // CHECK: class Bar2 +}; Index: test/clang-rename/ClassTestMultiByNameYAML.cpp =================================================================== --- /dev/null +++ test/clang-rename/ClassTestMultiByNameYAML.cpp @@ -0,0 +1,7 @@ +class Foo1 { // CHECK: class Bar1 +}; + +class Foo2 { // CHECK: class Bar2 +}; +// RUN: clang-rename rename-all -input %S/Inputs/ClassTestMultiByNameYAMLRenameAll.yaml %s -- | sed 's,//.*,,' | FileCheck %s +// RUN: clang-rename rename-all -input %S/Inputs/ClassTestMultiByNameYAMLRenameAt.yaml %s -- | sed 's,//.*,,' | FileCheck %s Index: test/clang-rename/ComplexFunctionOverride.cpp =================================================================== --- /dev/null +++ test/clang-rename/ComplexFunctionOverride.cpp @@ -0,0 +1,23 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=307 -new-name=bar %t.cpp -i -- -std=c++11 +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s + +struct A { + virtual void foo(); // CHECK: virtual void bar(); +}; + +struct B : A { + void foo() override; // CHECK: void bar() override; +}; + +struct C : B { + void foo() override; // CHECK: void bar() override; +}; + +struct D : B { + void foo() override; // CHECK: void bar() override; +}; + +struct E : D { + void foo() override; // CHECK: void bar() override; +}; Index: test/clang-rename/ComplicatedClassType.cpp =================================================================== --- test/clang-rename/ComplicatedClassType.cpp +++ test/clang-rename/ComplicatedClassType.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=159 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=220 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s // Forward declaration. class Foo; // CHECK: class Bar; Index: test/clang-rename/ConstCastExpr.cpp =================================================================== --- test/clang-rename/ConstCastExpr.cpp +++ test/clang-rename/ConstCastExpr.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=74 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo { // CHECK: class Bar { public: @@ -12,5 +14,5 @@ const_cast(C)->getValue(); // CHECK: const_cast(C)->getValue(); } -// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// Use grep -FUbo 'Cla' to get the correct offset of foo when changing // this file. Index: test/clang-rename/ConstructExpr.cpp =================================================================== --- test/clang-rename/ConstructExpr.cpp +++ test/clang-rename/ConstructExpr.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=74 -new-name=Boo %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=136 -new-name=Boo %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo {}; // CHECK: class Boo {}; @@ -6,5 +8,5 @@ Foo *C = new Foo(); // CHECK: Boo *C = new Boo(); } -// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// Use grep -FUbo 'Boo' to get the correct offset of foo when changing // this file. Index: test/clang-rename/CtorFindByDeclaration.cpp =================================================================== --- test/clang-rename/CtorFindByDeclaration.cpp +++ test/clang-rename/CtorFindByDeclaration.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=113 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=174 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo { // CHECK: class Bar public: @@ -7,5 +9,5 @@ Foo::Foo() {} // CHECK: Bar::Bar() -// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// Use grep -FUbo 'C' to get the correct offset of foo when changing // this file. Index: test/clang-rename/CtorFindByDefinition.cpp =================================================================== --- test/clang-rename/CtorFindByDefinition.cpp +++ test/clang-rename/CtorFindByDefinition.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=151 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=212 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo { // CHECK: class Bar public: @@ -7,5 +9,5 @@ Foo::Foo() {} // CHECK: Bar::Bar() -// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// Use grep -FUbo 'C' to get the correct offset of foo when changing // this file. Index: test/clang-rename/CtorInitializer.cpp =================================================================== --- test/clang-rename/CtorInitializer.cpp +++ test/clang-rename/CtorInitializer.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=102 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=163 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Baz {}; Index: test/clang-rename/DeclRefExpr.cpp =================================================================== --- test/clang-rename/DeclRefExpr.cpp +++ test/clang-rename/DeclRefExpr.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=100 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=161 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class C { public: @@ -14,5 +16,5 @@ int y = C::Foo; // CHECK: C::Bar } -// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// Use grep -FUbo 'X' to get the correct offset of foo when changing // this file. Index: test/clang-rename/DtorDeclaration.cpp =================================================================== --- test/clang-rename/DtorDeclaration.cpp +++ test/clang-rename/DtorDeclaration.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=114 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=175 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo { // CHECK: class Bar { public: @@ -8,5 +10,5 @@ Foo::~Foo() { // CHECK: Bar::~Bar() } -// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// Use grep -FUbo 'Bar' to get the correct offset of foo when changing // this file. Index: test/clang-rename/DtorDefinition.cpp =================================================================== --- test/clang-rename/DtorDefinition.cpp +++ test/clang-rename/DtorDefinition.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=158 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=219 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo { // CHECK: class Bar { public: @@ -8,5 +10,5 @@ Foo::~Foo() {} // CHECK: Bar::~Bar() -// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// Use grep -FUbo 'Foo' to get the correct offset of foo when changing // this file. Index: test/clang-rename/DynamicCastExpr.cpp =================================================================== --- test/clang-rename/DynamicCastExpr.cpp +++ test/clang-rename/DynamicCastExpr.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=134 -new-name=Bar %s -- -frtti | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=195 -new-name=Bar %t.cpp -i -- -frtti +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Baz { virtual int getValue() const = 0; @@ -20,5 +22,5 @@ dynamic_cast(Pointer)->getValue(); // CHECK: dynamic_cast(Pointer)->getValue(); } -// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// Use grep -FUbo 'Foo' to get the correct offset of foo when changing // this file. Index: test/clang-rename/Field.cpp =================================================================== --- test/clang-rename/Field.cpp +++ test/clang-rename/Field.cpp @@ -1,12 +1,15 @@ -// RUN: clang-rename -offset=87 -new-name=Bar %s -- | FileCheck %s - class Baz { - int Foo; // CHECK: Bar; + int Foo; /* Test 1 */ // CHECK: int Bar; public: Baz(); }; -Baz::Baz() : Foo(0) {} // CHECK: Baz::Baz() : Bar(0) {} +Baz::Baz() : Foo(0) /* Test 2 */ {} // CHECK: Baz::Baz() : Bar(0) + +// Test 1. +// RUN: clang-rename -offset=18 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s +// Test 2. +// RUN: clang-rename -offset=89 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s -// Use grep -FUbo 'Foo' to get the correct offset of foo when changing -// this file. +// To find offsets after modifying the file, use: +// grep -Ubo 'Foo.*' Index: test/clang-rename/FunctionMacro.cpp =================================================================== --- test/clang-rename/FunctionMacro.cpp +++ test/clang-rename/FunctionMacro.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=138 -new-name=macro_function %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=199 -new-name=macro_function %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s #define moo foo // CHECK: #define moo macro_function @@ -15,5 +17,5 @@ boo(moo()); } -// Use grep -FUbo 'foo' to get the correct offset of foo when changing +// Use grep -FUbo 'foo;' to get the correct offset of foo when changing // this file. Index: test/clang-rename/FunctionOverride.cpp =================================================================== --- /dev/null +++ test/clang-rename/FunctionOverride.cpp @@ -0,0 +1,10 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=318 -new-name=bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s + +class A { virtual void foo(); }; // CHECK: class A { virtual void bar(); }; +class B : public A { void foo(); }; // CHECK: class B : public A { void bar(); }; +class C : public B { void foo(); }; // CHECK: class C : public B { void bar(); }; + +// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// this file. Index: test/clang-rename/FunctionWithClassFindByName.cpp =================================================================== --- test/clang-rename/FunctionWithClassFindByName.cpp +++ test/clang-rename/FunctionWithClassFindByName.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -old-name=Foo -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename rename-all -old-name=Foo -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s void foo() { } Index: test/clang-rename/Inputs/ClassTestMultiByNameYAMLRenameAll.yaml =================================================================== --- /dev/null +++ test/clang-rename/Inputs/ClassTestMultiByNameYAMLRenameAll.yaml @@ -0,0 +1,6 @@ +--- +- OldName: Foo1 + NewName: Bar1 +- OldName: Foo2 + NewName: Bar2 +... Index: test/clang-rename/Inputs/ClassTestMultiByNameYAMLRenameAt.yaml =================================================================== --- /dev/null +++ test/clang-rename/Inputs/ClassTestMultiByNameYAMLRenameAt.yaml @@ -0,0 +1,6 @@ +--- +- Offset: 6 + NewName: Bar1 +- Offset: 44 + NewName: Bar2 +... Index: test/clang-rename/MemberExprMacro.cpp =================================================================== --- test/clang-rename/MemberExprMacro.cpp +++ test/clang-rename/MemberExprMacro.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=95 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=156 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Baz { public: Index: test/clang-rename/Namespace.cpp =================================================================== --- test/clang-rename/Namespace.cpp +++ test/clang-rename/Namespace.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=79 -new-name=llvm %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=143 -new-name=llvm %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s namespace foo { // CHECK: namespace llvm { int x; @@ -8,5 +10,5 @@ foo::x = 42; // CHECK: llvm::x = 42; } -// Use grep -FUbo 'foo' to get the correct offset of foo when changing +// Use grep -FUbo 'foo;' to get the correct offset of foo when changing // this file. Index: test/clang-rename/NoNewName.cpp =================================================================== --- test/clang-rename/NoNewName.cpp +++ test/clang-rename/NoNewName.cpp @@ -1,4 +1,4 @@ // Check for an error while -new-name argument has not been passed to // clang-rename. // RUN: not clang-rename -offset=133 %s 2>&1 | FileCheck %s -// CHECK: ERROR: no new name provided. +// CHECK: clang-rename: for the -new-name option: must be specified Index: test/clang-rename/ReinterpretCastExpr.cpp =================================================================== --- test/clang-rename/ReinterpretCastExpr.cpp +++ test/clang-rename/ReinterpretCastExpr.cpp @@ -1,5 +1,6 @@ -// RUN: clang-rename -offset=73 -new-name=X %s -- | FileCheck %s - +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=133 -new-name=X %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Cla { public: int getValue() const { @@ -12,5 +13,5 @@ reinterpret_cast(C)->getValue(); // CHECK: reinterpret_cast } -// Use grep -FUbo 'Cla' to get the correct offset of Cla when changing +// Use grep -FUbo 'Cla' to get the correct offset of foo when changing // this file. Index: test/clang-rename/StaticCastExpr.cpp =================================================================== --- test/clang-rename/StaticCastExpr.cpp +++ test/clang-rename/StaticCastExpr.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=91 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=152 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Baz { }; Index: test/clang-rename/TemplateClassInstantiation.cpp =================================================================== --- test/clang-rename/TemplateClassInstantiation.cpp +++ test/clang-rename/TemplateClassInstantiation.cpp @@ -1,14 +1,5 @@ -// RUN: cat %s > %t.cpp -// RUN: clang-rename -offset=703 -new-name=Bar %t.cpp -i -- -// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s - -// Currently unsupported test. -// FIXME: clang-rename should be able to rename classes with templates -// correctly. -// XFAIL: * - template -class Foo { // CHECK: class Bar; +class Foo { // CHECK: class Bar { public: T foo(T arg, T& ref, T* ptr) { T value; @@ -40,5 +31,14 @@ return 0; } -// Use grep -FUbo 'Foo' to get the correct offset of foo when changing -// this file. +// RUN: cat %s > %t-0.cpp +// RUN: clang-rename -offset=29 -new-name=Bar %t-0.cpp -i -- -fno-delayed-template-parsing +// RUN: sed 's,//.*,,' %t-0.cpp | FileCheck %s + +// RUN: cat %s > %t-1.cpp +// RUN: clang-rename -offset=311 -new-name=Bar %t-1.cpp -i -- -fno-delayed-template-parsing +// RUN: sed 's,//.*,,' %t-1.cpp | FileCheck %s + +// RUN: cat %s > %t-2.cpp +// RUN: clang-rename -offset=445 -new-name=Bar %t-2.cpp -i -- -fno-delayed-template-parsing +// RUN: sed 's,//.*,,' %t-2.cpp | FileCheck %s Index: test/clang-rename/TemplateClassInstantiationFindByDeclaration.cpp =================================================================== --- test/clang-rename/TemplateClassInstantiationFindByDeclaration.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// RUN: cat %s > %t.cpp -// RUN: clang-rename -offset=287 -new-name=Bar %t.cpp -i -- -// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s - -// Currently unsupported test. -// FIXME: clang-rename should be able to rename classes with templates -// correctly. -// XFAIL: * - -template -class Foo { // CHECK: class Bar; -public: - T foo(T arg, T& ref, T* ptr) { - T value; - int number = 42; - value = (T)number; - value = static_cast(number); - return value; - } - static void foo(T value) {} - T member; -}; - -template -void func() { - Foo obj; // CHECK: Bar obj; - obj.member = T(); - Foo::foo(); // CHECK: Bar::foo(); -} - -int main() { - Foo i; // CHECK: Bar i; - i.member = 0; - Foo::foo(0); // CHECK: Bar::foo(0); - - Foo b; // CHECK: Bar b; - b.member = false; - Foo::foo(false); // CHECK: Bar::foo(false); - - return 0; -} - -// Use grep -FUbo 'C' to get the correct offset of foo when changing -// this file. Index: test/clang-rename/TemplateFunctionFindByDeclaration.cpp =================================================================== --- test/clang-rename/TemplateFunctionFindByDeclaration.cpp +++ test/clang-rename/TemplateFunctionFindByDeclaration.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=93 -new-name=bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=154 -new-name=bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s template T foo(T value) { // CHECK: T bar(T value) { Index: test/clang-rename/TemplateFunctionFindByUse.cpp =================================================================== --- test/clang-rename/TemplateFunctionFindByUse.cpp +++ test/clang-rename/TemplateFunctionFindByUse.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=172 -new-name=bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=233 -new-name=bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s template T foo(T value) { // CHECK: T bar(T value) { Index: test/clang-rename/TemplateTypenameFindByTemplateParam.cpp =================================================================== --- test/clang-rename/TemplateTypenameFindByTemplateParam.cpp +++ test/clang-rename/TemplateTypenameFindByTemplateParam.cpp @@ -1,11 +1,3 @@ -// RUN: cat %s > %t.cpp -// RUN: clang-rename -offset=270 -new-name=U %t.cpp -i -- -// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s - -// Currently unsupported test. -// FIXME: clang-rename should be able to rename template parameters correctly. -// XFAIL: * - template // CHECK: template class Foo { T foo(T arg, T& ref, T* ptr) { // CHECK: U foo(U arg, U& ref, U* ptr) { @@ -20,3 +12,7 @@ T member; // CHECK: U member; }; + +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=19 -new-name=U %t.cpp -i -- -fno-delayed-template-parsing +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s Index: test/clang-rename/TemplateTypenameFindByTypeInside.cpp =================================================================== --- test/clang-rename/TemplateTypenameFindByTypeInside.cpp +++ test/clang-rename/TemplateTypenameFindByTypeInside.cpp @@ -1,9 +1,3 @@ -// RUN: clang-rename -offset=289 -new-name=U %s -- | FileCheck %s - -// Currently unsupported test. -// FIXME: clang-rename should be able to rename template parameters correctly. -// XFAIL: * - template // CHECK: template class Foo { T foo(T arg, T& ref, T* ptr) { // CHECK: U foo(U arg, U& ref, U* ptr) { @@ -18,3 +12,7 @@ T member; // CHECK: U member; }; + +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=99 -new-name=U %t.cpp -i -- -fno-delayed-template-parsing +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s Index: test/clang-rename/UserDefinedConversion.cpp =================================================================== --- test/clang-rename/UserDefinedConversion.cpp +++ test/clang-rename/UserDefinedConversion.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=143 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=205 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo {}; // CHECK: class Bar {}; Index: test/clang-rename/UserDefinedConversionFindByTypeDeclaration.cpp =================================================================== --- test/clang-rename/UserDefinedConversionFindByTypeDeclaration.cpp +++ test/clang-rename/UserDefinedConversionFindByTypeDeclaration.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=75 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo { // CHECK: class Bar { // ^ offset must be here @@ -20,5 +22,5 @@ return 0; } -// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// Use grep -FUbo 'Foo' to get the correct offset of Cla when changing // this file. Index: test/clang-rename/Variable.cpp =================================================================== --- test/clang-rename/Variable.cpp +++ test/clang-rename/Variable.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=87 -new-name=Bar %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=148 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s namespace A { int Foo; // CHECK: int Bar; @@ -21,5 +23,5 @@ Foo = b.Foo; // Foo = b.Foo; } -// Use grep -FUbo 'Foo' to get the correct offset of Foo when changing +// Use grep -FUbo 'Foo' to get the correct offset of foo when changing // this file. Index: test/clang-rename/VariableMacro.cpp =================================================================== --- test/clang-rename/VariableMacro.cpp +++ test/clang-rename/VariableMacro.cpp @@ -1,4 +1,6 @@ -// RUN: clang-rename -offset=147 -new-name=Z %s -- | FileCheck %s +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=208 -new-name=Z %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s #define Y X // CHECK: #define Y Z @@ -12,5 +14,5 @@ foo(Y); } -// Use grep -FUbo 'X' to get the correct offset of X when changing +// Use grep -FUbo 'foo;' to get the correct offset of foo when changing // this file. Index: test/clang-tidy/Inputs/modernize-pass-by-value/header-with-fix.h =================================================================== --- /dev/null +++ test/clang-tidy/Inputs/modernize-pass-by-value/header-with-fix.h @@ -0,0 +1,8 @@ +struct S { + S(S&&); + S(const S&); +}; +struct Foo { + Foo(const S &s); + S s; +}; Index: test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h =================================================================== --- /dev/null +++ test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h @@ -0,0 +1,62 @@ +// This Message Passing Interface mock header is used, to mock typedefs, +// constants and functions, required for integration tests being part of +// clang-tidy MPI checks. + +#ifndef MPIMOCK_H +#define MPIMOCK_H + +#define NULL 0 + +// These typedefs are used to mock MPI types, fixed width integer types and the +// templated C++ complex number type. +typedef int MPI_Datatype; +typedef int MPI_Comm; +typedef int MPI_Request; +typedef int MPI_Status; +typedef int MPI_Op; +typedef int int8_t; +typedef int uint8_t; +typedef int uint16_t; +typedef int int64_t; +namespace std { template struct complex { T real; T imag; }; } + +// These defines are used to mock MPI constants. +#define MPI_DATATYPE_NULL 0 +#define MPI_CHAR 0 +#define MPI_BYTE 0 +#define MPI_INT 0 +#define MPI_LONG 0 +#define MPI_LONG_DOUBLE 0 +#define MPI_UNSIGNED 0 +#define MPI_INT8_T 0 +#define MPI_UINT8_T 0 +#define MPI_UINT16_T 0 +#define MPI_C_LONG_DOUBLE_COMPLEX 0 +#define MPI_FLOAT 0 +#define MPI_DOUBLE 0 +#define MPI_CXX_BOOL 0 +#define MPI_CXX_FLOAT_COMPLEX 0 +#define MPI_CXX_DOUBLE_COMPLEX 0 +#define MPI_CXX_LONG_DOUBLE_COMPLEX 0 +#define MPI_IN_PLACE 0 +#define MPI_COMM_WORLD 0 +#define MPI_STATUS_IGNORE 0 +#define MPI_STATUSES_IGNORE 0 +#define MPI_SUM 0 + +// These declarations are used to mock MPI functions. +int MPI_Comm_size(MPI_Comm, int *); +int MPI_Comm_rank(MPI_Comm, int *); +int MPI_Send(const void *, int, MPI_Datatype, int, int, MPI_Comm); +int MPI_Recv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Status *); +int MPI_Isend(const void *, int, MPI_Datatype, int, int, MPI_Comm, + MPI_Request *); +int MPI_Irecv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *); +int MPI_Wait(MPI_Request *, MPI_Status *); +int MPI_Waitall(int, MPI_Request[], MPI_Status[]); +int MPI_Reduce(const void *, void *, int, MPI_Datatype, MPI_Op, int, MPI_Comm); +int MPI_Ireduce(const void *, void *, int, MPI_Datatype, MPI_Op, int, MPI_Comm, + MPI_Request *); +int MPI_Bcast(void *, int count, MPI_Datatype, int, MPI_Comm); + +#endif // end of include guard: MPIMOCK_H Index: test/clang-tidy/Inputs/unused-using-decls.h =================================================================== --- /dev/null +++ test/clang-tidy/Inputs/unused-using-decls.h @@ -0,0 +1,11 @@ +class MyClass { +public: + template